summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorJoost Mulders <Joost.Mulders@Sun.COM>2009-05-05 12:21:01 +0200
committerJoost Mulders <Joost.Mulders@Sun.COM>2009-05-05 12:21:01 +0200
commit2ca5b6595b95478e6568b0e77c6c83c8a870867a (patch)
tree5541c5c53d81d5c11fa783c4161a0cfc79cc64a7 /usr
parent30cbf0d206aed9037082f81dba96b4013bc5f836 (diff)
downloadillumos-gate-2ca5b6595b95478e6568b0e77c6c83c8a870867a.tar.gz
PSARC/2008/619 add a VIA Rhine Ethernet driver to Solaris
6770327 add support for VIA Rhine Fast Ethernet cards
Diffstat (limited to 'usr')
-rw-r--r--usr/src/pkgdefs/Makefile1
-rw-r--r--usr/src/pkgdefs/SUNWvr/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWvr/pkginfo.tmpl45
-rw-r--r--usr/src/pkgdefs/SUNWvr/postinstall.tmpl30
-rw-r--r--usr/src/pkgdefs/SUNWvr/preremove.tmpl28
-rw-r--r--usr/src/pkgdefs/SUNWvr/prototype_com45
-rw-r--r--usr/src/pkgdefs/SUNWvr/prototype_i38644
-rw-r--r--usr/src/pkgdefs/SUNWvr/prototype_sparc43
-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/vr/vr.c3657
-rw-r--r--usr/src/uts/common/io/vr/vr.h499
-rw-r--r--usr/src/uts/common/io/vr/vr_impl.h655
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared1
-rw-r--r--usr/src/uts/sparc/vr/Makefile89
16 files changed, 5184 insertions, 0 deletions
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 5ae740bde3..75eb967c7e 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -506,6 +506,7 @@ COMMON_SUBDIRS= \
SUNWusbs \
SUNWusbu \
SUNWuwb \
+ SUNWvr \
SUNWvscankr \
SUNWvscanr \
SUNWvscanu \
diff --git a/usr/src/pkgdefs/SUNWvr/Makefile b/usr/src/pkgdefs/SUNWvr/Makefile
new file mode 100644
index 0000000000..c6aee2f3cc
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/Makefile
@@ -0,0 +1,37 @@
+#
+# 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.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+TMPLFILES += postinstall preremove
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
diff --git a/usr/src/pkgdefs/SUNWvr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWvr/pkginfo.tmpl
new file mode 100644
index 0000000000..740b707314
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/pkginfo.tmpl
@@ -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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PKG=SUNWvr
+NAME=VIA Rhine Fast 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="VIA Rhine Fast 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"
diff --git a/usr/src/pkgdefs/SUNWvr/postinstall.tmpl b/usr/src/pkgdefs/SUNWvr/postinstall.tmpl
new file mode 100644
index 0000000000..f548ef57af
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/postinstall.tmpl
@@ -0,0 +1,30 @@
+#!/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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+pkg_drvadd -i \
+ '"pci1106,3043" "pci1106,3106" "pci1106,3065" "pci1106,3053"' \
+ -m '* 0666 root sys' vr || exit 1
diff --git a/usr/src/pkgdefs/SUNWvr/preremove.tmpl b/usr/src/pkgdefs/SUNWvr/preremove.tmpl
new file mode 100644
index 0000000000..5e373d0b28
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/preremove.tmpl
@@ -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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+pkg_drvrem vr || exit 1
diff --git a/usr/src/pkgdefs/SUNWvr/prototype_com b/usr/src/pkgdefs/SUNWvr/prototype_com
new file mode 100644
index 0000000000..3fec7212ed
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/prototype_com
@@ -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 2009 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
diff --git a/usr/src/pkgdefs/SUNWvr/prototype_i386 b/usr/src/pkgdefs/SUNWvr/prototype_i386
new file mode 100644
index 0000000000..4525ea1fd1
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/prototype_i386
@@ -0,0 +1,44 @@
+#
+# 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.
+# 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
+#
+f none kernel/drv/vr 0755 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/vr 0755 root sys
diff --git a/usr/src/pkgdefs/SUNWvr/prototype_sparc b/usr/src/pkgdefs/SUNWvr/prototype_sparc
new file mode 100644
index 0000000000..b5a8ccf437
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWvr/prototype_sparc
@@ -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 2009 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/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/vr 0755 root sys
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index d698711935..80a462a002 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1723,6 +1723,8 @@ IDM_SHARED_OBJS += base64.o
IDM_OBJS += $(IDM_SHARED_OBJS) \
idm.o idm_impl.o idm_text.o idm_conn_sm.o idm_so.o
+VR_OBJS += vr.o
+
#
# Build up defines and paths.
#
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 7948f00104..fcac051f96 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -1208,6 +1208,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/inet/kifconf/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vr/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
#
# krtld must refer to its own bzero/bcopy until the kernel is fully linked
#
@@ -2317,3 +2321,6 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hxge/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/tpm/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/vr/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/io/vr/vr.c b/usr/src/uts/common/io/vr/vr.c
new file mode 100644
index 0000000000..187789b569
--- /dev/null
+++ b/usr/src/uts/common/io/vr/vr.c
@@ -0,0 +1,3657 @@
+/*
+ * 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.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/stat.h>
+#include <sys/pci.h>
+#include <sys/modctl.h>
+#include <sys/kstat.h>
+#include <sys/ethernet.h>
+#include <sys/devops.h>
+#include <sys/debug.h>
+#include <sys/conf.h>
+#include <sys/mac.h>
+#include <sys/mac_provider.h>
+#include <sys/mac_ether.h>
+#include <sys/sysmacros.h>
+#include <sys/dditypes.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/miiregs.h>
+#include <sys/byteorder.h>
+#include <sys/note.h>
+#include <sys/vlan.h>
+
+#include "vr.h"
+#include "vr_impl.h"
+
+/*
+ * VR in a nutshell
+ * The card uses two rings of data structures to communicate with the host.
+ * These are referred to as "descriptor rings" and there is one for transmit
+ * (TX) and one for receive (RX).
+ *
+ * The driver uses a "DMA buffer" data type for mapping to those descriptor
+ * rings. This is a structure with handles and a DMA'able buffer attached to it.
+ *
+ * Receive
+ * The receive ring is filled with DMA buffers. Received packets are copied into
+ * a newly allocated mblk's and passed upstream.
+ *
+ * Transmit
+ * Each transmit descriptor has a DMA buffer attached to it. The data of TX
+ * packets is copied into the DMA buffer which is then enqueued for
+ * transmission.
+ *
+ * Reclaim of transmitted packets is done as a result of a transmit completion
+ * interrupt which is generated 3 times per ring at minimum.
+ */
+
+#if defined(DEBUG)
+uint32_t vrdebug = 1;
+#define VR_DEBUG(args) do { \
+ if (vrdebug > 0) \
+ (*vr_debug()) args; \
+ _NOTE(CONSTANTCONDITION) \
+ } while (0)
+static void vr_prt(const char *fmt, ...);
+ void (*vr_debug())(const char *fmt, ...);
+#else
+#define VR_DEBUG(args) do ; _NOTE(CONSTANTCONDITION) while (0)
+#endif
+
+static char vr_ident[] = "VIA Rhine Ethernet v1.42";
+
+/*
+ * Attributes for accessing registers and memory descriptors for this device.
+ */
+static ddi_device_acc_attr_t vr_dev_dma_accattr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+/*
+ * Attributes for accessing data.
+ */
+static ddi_device_acc_attr_t vr_data_dma_accattr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+/*
+ * DMA attributes for descriptors for communication with the device
+ * This driver assumes that all descriptors of one ring fit in one consequitive
+ * memory area of max 4K (256 descriptors) that does not cross a page boundary.
+ * Therefore, we request 4K alignement.
+ */
+static ddi_dma_attr_t vr_dev_dma_attr = {
+ DMA_ATTR_V0, /* version number */
+ 0, /* low DMA address range */
+ 0xFFFFFFFF, /* high DMA address range */
+ 0x7FFFFFFF, /* DMA counter register */
+ 0x1000, /* DMA address alignment */
+ 0x7F, /* DMA burstsizes */
+ 1, /* min effective DMA size */
+ 0xFFFFFFFF, /* max DMA xfer size */
+ 0xFFFFFFFF, /* segment boundary */
+ 1, /* s/g list length */
+ 1, /* granularity of device */
+ 0 /* DMA transfer flags */
+};
+
+/*
+ * DMA attributes for the data moved to/from the device
+ * Note that the alignement is set to 2K so hat a 1500 byte packet never
+ * crosses a page boundary and thus that a DMA transfer is not split up in
+ * multiple cookies with a 4K/8K pagesize
+ */
+static ddi_dma_attr_t vr_data_dma_attr = {
+ DMA_ATTR_V0, /* version number */
+ 0, /* low DMA address range */
+ 0xFFFFFFFF, /* high DMA address range */
+ 0x7FFFFFFF, /* DMA counter register */
+ 0x800, /* DMA address alignment */
+ 0xfff, /* DMA burstsizes */
+ 1, /* min effective DMA size */
+ 0xFFFFFFFF, /* max DMA xfer size */
+ 0xFFFFFFFF, /* segment boundary */
+ 1, /* s/g list length */
+ 1, /* granularity of device */
+ 0 /* DMA transfer flags */
+};
+
+static mac_callbacks_t vr_mac_callbacks = {
+ MC_SETPROP|MC_GETPROP, /* Which callbacks are set */
+ vr_mac_getstat, /* Get the value of a statistic */
+ vr_mac_start, /* Start the device */
+ vr_mac_stop, /* Stop the device */
+ vr_mac_set_promisc, /* Enable or disable promiscuous mode */
+ vr_mac_set_multicast, /* Enable or disable a multicast addr */
+ vr_mac_set_ether_addr, /* Set the unicast MAC address */
+ vr_mac_tx_enqueue_list, /* Transmit a packet */
+ NULL, /* Process an unknown ioctl */
+ NULL, /* Get capability information */
+ NULL, /* Open the device */
+ NULL, /* Close the device */
+ vr_mac_setprop, /* Set properties of the device */
+ vr_mac_getprop /* Get properties of the device */
+};
+
+/*
+ * Table with bugs and features for each incarnation of the card.
+ */
+static const chip_info_t vr_chip_info [] = {
+ {
+ 0x0, 0x0,
+ "VIA Rhine Fast Ethernet",
+ (VR_BUG_NO_MEMIO),
+ (VR_FEATURE_NONE)
+ },
+ {
+ 0x04, 0x21,
+ "VIA VT86C100A Fast Ethernet",
+ (VR_BUG_NEEDMODE2PCEROPT | VR_BUG_NO_TXQUEUEING |
+ VR_BUG_NEEDMODE10T | VR_BUG_TXALIGN | VR_BUG_NO_MEMIO |
+ VR_BUG_MIIPOLLSTOP),
+ (VR_FEATURE_NONE)
+ },
+ {
+ 0x40, 0x41,
+ "VIA VT6102-A Rhine II Fast Ethernet",
+ (VR_BUG_NEEDMODE2PCEROPT),
+ (VR_FEATURE_RX_PAUSE_CAP)
+ },
+ {
+ 0x42, 0x7f,
+ "VIA VT6102-C Rhine II Fast Ethernet",
+ (VR_BUG_NEEDMODE2PCEROPT),
+ (VR_FEATURE_RX_PAUSE_CAP)
+ },
+ {
+ 0x80, 0x82,
+ "VIA VT6105-A Rhine III Fast Ethernet",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
+ },
+ {
+ 0x83, 0x89,
+ "VIA VT6105-B Rhine III Fast Ethernet",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
+ },
+ {
+ 0x8a, 0x8b,
+ "VIA VT6105-LOM Rhine III Fast Ethernet",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
+ },
+ {
+ 0x8c, 0x8c,
+ "VIA VT6107-A0 Rhine III Fast Ethernet",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
+ },
+ {
+ 0x8d, 0x8f,
+ "VIA VT6107-A1 Rhine III Fast Ethernet",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
+ VR_FEATURE_MRDLNMULTIPLE)
+ },
+ {
+ 0x90, 0x93,
+ "VIA VT6105M-A0 Rhine III Fast Ethernet Management Adapter",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
+ VR_FEATURE_TXCHKSUM | VR_FEATURE_RXCHKSUM |
+ VR_FEATURE_CAMSUPPORT | VR_FEATURE_VLANTAGGING |
+ VR_FEATURE_MIBCOUNTER)
+ },
+ {
+ 0x94, 0xff,
+ "VIA VT6105M-B1 Rhine III Fast Ethernet Management Adapter",
+ (VR_BUG_NONE),
+ (VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
+ VR_FEATURE_TXCHKSUM | VR_FEATURE_RXCHKSUM |
+ VR_FEATURE_CAMSUPPORT | VR_FEATURE_VLANTAGGING |
+ VR_FEATURE_MIBCOUNTER)
+ }
+};
+
+/*
+ * Function prototypes
+ */
+static vr_result_t vr_add_intr(vr_t *vrp);
+static void vr_remove_intr(vr_t *vrp);
+static int32_t vr_cam_index(vr_t *vrp, const uint8_t *maddr);
+static uint32_t ether_crc_be(const uint8_t *address);
+static void vr_tx_enqueue_msg(vr_t *vrp, mblk_t *mp);
+static void vr_log(vr_t *vrp, int level, const char *fmt, ...);
+static int vr_resume(dev_info_t *devinfo);
+static int vr_suspend(dev_info_t *devinfo);
+static vr_result_t vr_bus_config(vr_t *vrp);
+static void vr_bus_unconfig(vr_t *vrp);
+static void vr_reset(vr_t *vrp);
+static int vr_start(vr_t *vrp);
+static int vr_stop(vr_t *vrp);
+static vr_result_t vr_rings_init(vr_t *vrp);
+static void vr_rings_fini(vr_t *vrp);
+static vr_result_t vr_alloc_ring(vr_t *vrp, vr_ring_t *r, size_t n);
+static void vr_free_ring(vr_ring_t *r, size_t n);
+static vr_result_t vr_rxring_init(vr_t *vrp);
+static void vr_rxring_fini(vr_t *vrp);
+static vr_result_t vr_txring_init(vr_t *vrp);
+static void vr_txring_fini(vr_t *vrp);
+static vr_result_t vr_alloc_dmabuf(vr_t *vrp, vr_data_dma_t *dmap,
+ uint_t flags);
+static void vr_free_dmabuf(vr_data_dma_t *dmap);
+static void vr_param_init(vr_t *vrp);
+static mblk_t *vr_receive(vr_t *vrp);
+static void vr_tx_reclaim(vr_t *vrp);
+static void vr_periodic(void *p);
+static void vr_error(vr_t *vrp);
+static void vr_phy_read(vr_t *vrp, int offset, uint16_t *value);
+static void vr_phy_write(vr_t *vrp, int offset, uint16_t value);
+static void vr_phy_autopoll_disable(vr_t *vrp);
+static void vr_phy_autopoll_enable(vr_t *vrp);
+static void vr_link_init(vr_t *vrp);
+static void vr_link_state(vr_t *vrp);
+static void vr_kstats_init(vr_t *vrp);
+static int vr_update_kstats(kstat_t *ksp, int access);
+static void vr_remove_kstats(vr_t *vrp);
+
+static int
+vr_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
+{
+ vr_t *vrp;
+ mac_register_t *macreg;
+
+ if (cmd == DDI_RESUME)
+ return (vr_resume(devinfo));
+ else if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ /*
+ * Attach.
+ */
+ vrp = kmem_zalloc(sizeof (vr_t), KM_SLEEP);
+ ddi_set_driver_private(devinfo, vrp);
+ vrp->devinfo = devinfo;
+
+ /*
+ * Store the name+instance of the module.
+ */
+ (void) snprintf(vrp->ifname, sizeof (vrp->ifname), "%s%d",
+ MODULENAME, ddi_get_instance(devinfo));
+
+ /*
+ * Bus initialization.
+ */
+ if (vr_bus_config(vrp) != VR_SUCCESS) {
+ vr_log(vrp, CE_WARN, "vr_bus_config failed");
+ goto fail0;
+ }
+
+ /*
+ * Initialize default parameters.
+ */
+ vr_param_init(vrp);
+
+ /*
+ * Setup the descriptor rings.
+ */
+ if (vr_rings_init(vrp) != VR_SUCCESS) {
+ vr_log(vrp, CE_WARN, "vr_rings_init failed");
+ goto fail1;
+ }
+
+ /*
+ * Initialize kstats.
+ */
+ vr_kstats_init(vrp);
+
+ /*
+ * Add interrupt to the OS.
+ */
+ if (vr_add_intr(vrp) != VR_SUCCESS) {
+ vr_log(vrp, CE_WARN, "vr_add_intr failed in attach");
+ goto fail3;
+ }
+
+ /*
+ * Add mutexes.
+ */
+ mutex_init(&vrp->intrlock, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(vrp->intr_pri));
+ mutex_init(&vrp->oplock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&vrp->tx.lock, NULL, MUTEX_DRIVER, NULL);
+
+ /*
+ * Enable interrupt.
+ */
+ if (ddi_intr_enable(vrp->intr_hdl) != DDI_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "ddi_intr_enable failed");
+ goto fail5;
+ }
+
+ /*
+ * Register with parent, mac.
+ */
+ if ((macreg = mac_alloc(MAC_VERSION)) == NULL) {
+ vr_log(vrp, CE_WARN, "mac_alloc failed in attach");
+ goto fail6;
+ }
+
+ macreg->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ macreg->m_driver = vrp;
+ macreg->m_dip = devinfo;
+ macreg->m_src_addr = vrp->vendor_ether_addr;
+ macreg->m_callbacks = &vr_mac_callbacks;
+ macreg->m_min_sdu = 0;
+ macreg->m_max_sdu = ETHERMTU;
+ macreg->m_margin = VLAN_TAGSZ;
+
+ if (mac_register(macreg, &vrp->machdl) != 0) {
+ vr_log(vrp, CE_WARN, "mac_register failed in attach");
+ goto fail7;
+ }
+ mac_free(macreg);
+ return (DDI_SUCCESS);
+
+fail7:
+ mac_free(macreg);
+fail6:
+ (void) ddi_intr_disable(vrp->intr_hdl);
+fail5:
+ mutex_destroy(&vrp->tx.lock);
+ mutex_destroy(&vrp->oplock);
+ mutex_destroy(&vrp->intrlock);
+ vr_remove_intr(vrp);
+fail3:
+ vr_remove_kstats(vrp);
+fail2:
+ vr_rings_fini(vrp);
+fail1:
+ vr_bus_unconfig(vrp);
+fail0:
+ kmem_free(vrp, sizeof (vr_t));
+ return (DDI_FAILURE);
+}
+
+static int
+vr_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
+{
+ vr_t *vrp;
+
+ vrp = ddi_get_driver_private(devinfo);
+
+ if (cmd == DDI_SUSPEND)
+ return (vr_suspend(devinfo));
+ else if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ if (vrp->chip.state == CHIPSTATE_RUNNING)
+ return (DDI_FAILURE);
+
+ /*
+ * Try to un-register from the MAC layer.
+ */
+ if (mac_unregister(vrp->machdl) != 0)
+ return (DDI_FAILURE);
+
+ (void) ddi_intr_disable(vrp->intr_hdl);
+ vr_remove_intr(vrp);
+ mutex_destroy(&vrp->tx.lock);
+ mutex_destroy(&vrp->oplock);
+ mutex_destroy(&vrp->intrlock);
+ vr_remove_kstats(vrp);
+ vr_rings_fini(vrp);
+ vr_bus_unconfig(vrp);
+ kmem_free(vrp, sizeof (vr_t));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * quiesce the card for fast reboot.
+ */
+int
+vr_quiesce(dev_info_t *dev_info)
+{
+ vr_t *vrp;
+
+ vrp = (vr_t *)ddi_get_driver_private(dev_info);
+
+ /*
+ * Stop interrupts.
+ */
+ VR_PUT16(vrp->acc_reg, VR_ICR0, 0);
+ VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
+
+ /*
+ * Stop DMA.
+ */
+ VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_STOP);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Add an interrupt for our device to the OS.
+ */
+static vr_result_t
+vr_add_intr(vr_t *vrp)
+{
+ int nintrs;
+ int rc;
+
+ rc = ddi_intr_alloc(vrp->devinfo, &vrp->intr_hdl,
+ DDI_INTR_TYPE_FIXED, /* type */
+ 0, /* number */
+ 1, /* count */
+ &nintrs, /* actualp */
+ DDI_INTR_ALLOC_STRICT);
+
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "ddi_intr_alloc failed: %d", rc);
+ return (VR_FAILURE);
+ }
+
+ rc = ddi_intr_add_handler(vrp->intr_hdl, vr_intr, vrp, NULL);
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "ddi_intr_add_handler failed");
+ if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
+ vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
+ return (VR_FAILURE);
+ }
+
+ rc = ddi_intr_get_pri(vrp->intr_hdl, &vrp->intr_pri);
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "ddi_intr_get_pri failed");
+ if (ddi_intr_remove_handler(vrp->intr_hdl) != DDI_SUCCESS)
+ vr_log(vrp, CE_NOTE, "ddi_intr_remove_handler failed");
+
+ if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
+ vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
+
+ return (VR_FAILURE);
+ }
+ return (VR_SUCCESS);
+}
+
+/*
+ * Remove our interrupt from the OS.
+ */
+static void
+vr_remove_intr(vr_t *vrp)
+{
+ if (ddi_intr_remove_handler(vrp->intr_hdl) != DDI_SUCCESS)
+ vr_log(vrp, CE_NOTE, "ddi_intr_remove_handler failed");
+
+ if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
+ vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
+}
+
+/*
+ * Resume operation after suspend.
+ */
+static int
+vr_resume(dev_info_t *devinfo)
+{
+ vr_t *vrp;
+
+ vrp = (vr_t *)ddi_get_driver_private(devinfo);
+ mutex_enter(&vrp->oplock);
+ if (vrp->chip.state == CHIPSTATE_SUSPENDED_RUNNING)
+ vr_start(vrp);
+ mutex_exit(&vrp->oplock);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Suspend operation.
+ */
+static int
+vr_suspend(dev_info_t *devinfo)
+{
+ vr_t *vrp;
+
+ vrp = (vr_t *)ddi_get_driver_private(devinfo);
+ mutex_enter(&vrp->oplock);
+ if (vrp->chip.state == CHIPSTATE_RUNNING) {
+ (void) vr_stop(vrp);
+ vrp->chip.state = CHIPSTATE_SUSPENDED_RUNNING;
+ }
+ mutex_exit(&vrp->oplock);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Initial bus- and device configuration during attach(9E).
+ */
+static vr_result_t
+vr_bus_config(vr_t *vrp)
+{
+ uint32_t addr;
+ int n, nsets, rc;
+ uint_t elem;
+ pci_regspec_t *regs;
+
+ /*
+ * Get the reg property which describes the various access methods.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, vrp->devinfo,
+ 0, "reg", (int **)&regs, &elem) != DDI_PROP_SUCCESS) {
+ vr_log(vrp, CE_WARN, "Can't get reg property");
+ return (VR_FAILURE);
+ }
+ nsets = (elem * sizeof (uint_t)) / sizeof (pci_regspec_t);
+
+ /*
+ * Setup access to all available sets.
+ */
+ vrp->nsets = nsets;
+ vrp->regset = kmem_zalloc(nsets * sizeof (vr_acc_t), KM_SLEEP);
+ for (n = 0; n < nsets; n++) {
+ rc = ddi_regs_map_setup(vrp->devinfo, n,
+ &vrp->regset[n].addr, 0, 0,
+ &vr_dev_dma_accattr,
+ &vrp->regset[n].hdl);
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_NOTE,
+ "Setup of register set %d failed", n);
+ while (--n >= 0)
+ ddi_regs_map_free(&vrp->regset[n].hdl);
+ kmem_free(vrp->regset, nsets * sizeof (vr_acc_t));
+ ddi_prop_free(regs);
+ return (VR_FAILURE);
+ }
+ bcopy(&regs[n], &vrp->regset[n].reg, sizeof (pci_regspec_t));
+ }
+ ddi_prop_free(regs);
+
+ /*
+ * Assign type-named pointers to the register sets.
+ */
+ for (n = 0; n < nsets; n++) {
+ addr = vrp->regset[n].reg.pci_phys_hi & PCI_REG_ADDR_M;
+ if (addr == PCI_ADDR_CONFIG && vrp->acc_cfg == NULL)
+ vrp->acc_cfg = &vrp->regset[n];
+ else if (addr == PCI_ADDR_IO && vrp->acc_io == NULL)
+ vrp->acc_io = &vrp->regset[n];
+ else if (addr == PCI_ADDR_MEM32 && vrp->acc_mem == NULL)
+ vrp->acc_mem = &vrp->regset[n];
+ }
+
+ /*
+ * Assure there is one of each type.
+ */
+ if (vrp->acc_cfg == NULL ||
+ vrp->acc_io == NULL ||
+ vrp->acc_mem == NULL) {
+ for (n = 0; n < nsets; n++)
+ ddi_regs_map_free(&vrp->regset[n].hdl);
+ kmem_free(vrp->regset, nsets * sizeof (vr_acc_t));
+ vr_log(vrp, CE_WARN,
+ "Config-, I/O- and memory sets not available");
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Store vendor/device/revision.
+ */
+ vrp->chip.vendor = VR_GET16(vrp->acc_cfg, PCI_CONF_VENID);
+ vrp->chip.device = VR_GET16(vrp->acc_cfg, PCI_CONF_DEVID);
+ vrp->chip.revision = VR_GET16(vrp->acc_cfg, PCI_CONF_REVID);
+
+ /*
+ * Copy the matching chip_info_t structure.
+ */
+ elem = sizeof (vr_chip_info) / sizeof (chip_info_t);
+ for (n = 0; n < elem; n++) {
+ if (vrp->chip.revision >= vr_chip_info[n].revmin &&
+ vrp->chip.revision <= vr_chip_info[n].revmax) {
+ bcopy((void*)&vr_chip_info[n],
+ (void*)&vrp->chip.info,
+ sizeof (chip_info_t));
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a chip_info_t for this card, copy the first
+ * entry of the info structures. This is a generic Rhine whith no
+ * bugs and no features.
+ */
+ if (vrp->chip.info.name == NULL) {
+ bcopy((void*)&vr_chip_info[0],
+ (void*) &vrp->chip.info,
+ sizeof (chip_info_t));
+ }
+
+ /*
+ * Tell what is found.
+ */
+ vr_log(vrp, CE_NOTE, "pci%d,%d,%d: %s, revision 0x%0x",
+ PCI_REG_BUS_G(vrp->acc_cfg->reg.pci_phys_hi),
+ PCI_REG_DEV_G(vrp->acc_cfg->reg.pci_phys_hi),
+ PCI_REG_FUNC_G(vrp->acc_cfg->reg.pci_phys_hi),
+ vrp->chip.info.name,
+ vrp->chip.revision);
+
+ /*
+ * Assure that the device is prepared for memory space accesses
+ * This should be the default as the device advertises memory
+ * access in it's BAR's. However, my VT6102 on a EPIA CL board doesn't
+ * and thus we explicetely enable it.
+ */
+ VR_SETBIT8(vrp->acc_io, VR_CFGD, VR_CFGD_MMIOEN);
+
+ /*
+ * Setup a handle for regular usage, prefer memory space accesses.
+ */
+ if (vrp->acc_mem != NULL &&
+ (vrp->chip.info.bugs & VR_BUG_NO_MEMIO) == 0)
+ vrp->acc_reg = vrp->acc_mem;
+ else
+ vrp->acc_reg = vrp->acc_io;
+
+ /*
+ * Store the vendor's MAC address.
+ */
+ for (n = 0; n < ETHERADDRL; n++) {
+ vrp->vendor_ether_addr[n] = VR_GET8(vrp->acc_reg,
+ VR_ETHERADDR + n);
+ }
+ return (VR_SUCCESS);
+}
+
+static void
+vr_bus_unconfig(vr_t *vrp)
+{
+ uint_t n;
+
+ /*
+ * Free the register access handles.
+ */
+ for (n = 0; n < vrp->nsets; n++)
+ ddi_regs_map_free(&vrp->regset[n].hdl);
+ kmem_free(vrp->regset, vrp->nsets * sizeof (vr_acc_t));
+}
+
+/*
+ * Initialize parameter structures.
+ */
+static void
+vr_param_init(vr_t *vrp)
+{
+ /*
+ * Initialize default link configuration parameters.
+ */
+ vrp->param.an_en = VR_LINK_AUTONEG_ON;
+ vrp->param.anadv_en = 1; /* Select 802.3 autonegotiation */
+ vrp->param.anadv_en |= MII_ABILITY_100BASE_T4;
+ vrp->param.anadv_en |= MII_ABILITY_100BASE_TX_FD;
+ vrp->param.anadv_en |= MII_ABILITY_100BASE_TX;
+ vrp->param.anadv_en |= MII_ABILITY_10BASE_T_FD;
+ vrp->param.anadv_en |= MII_ABILITY_10BASE_T;
+ /* Not a PHY ability, but advertised on behalf of MAC */
+ vrp->param.anadv_en |= MII_AN_ADVERT_FCS;
+ vrp->param.mtu = ETHERMTU;
+
+ /*
+ * Store the PHY identity.
+ */
+ vr_phy_read(vrp, MII_PHYIDH, &vrp->chip.mii.identh);
+ vr_phy_read(vrp, MII_PHYIDL, &vrp->chip.mii.identl);
+
+ /*
+ * Clear incapabilities imposed by PHY in phymask.
+ */
+ vrp->param.an_phymask = vrp->param.anadv_en;
+ vr_phy_read(vrp, MII_STATUS, &vrp->chip.mii.status);
+ if ((vrp->chip.mii.status & MII_STATUS_10) == 0)
+ vrp->param.an_phymask &= ~MII_ABILITY_10BASE_T;
+
+ if ((vrp->chip.mii.status & MII_STATUS_10_FD) == 0)
+ vrp->param.an_phymask &= ~MII_ABILITY_10BASE_T_FD;
+
+ if ((vrp->chip.mii.status & MII_STATUS_100_BASEX) == 0)
+ vrp->param.an_phymask &= ~MII_ABILITY_100BASE_TX;
+
+ if ((vrp->chip.mii.status & MII_STATUS_100_BASEX_FD) == 0)
+ vrp->param.an_phymask &= ~MII_ABILITY_100BASE_TX_FD;
+
+ if ((vrp->chip.mii.status & MII_STATUS_100_BASE_T4) == 0)
+ vrp->param.an_phymask &= ~MII_ABILITY_100BASE_T4;
+
+ /*
+ * Clear incapabilities imposed by MAC in macmask
+ * Note that flowcontrol (FCS?) is never masked. All of our adapters
+ * have the ability to honor incoming pause frames. Only the newer can
+ * transmit pause frames. Since there's no asym flowcontrol in 100Mbit
+ * Ethernet, we always advertise (symmetric) pause.
+ */
+ vrp->param.an_macmask = vrp->param.anadv_en;
+
+ /*
+ * Advertised capabilities is enabled minus incapable.
+ */
+ vrp->chip.mii.anadv = vrp->param.anadv_en &
+ (vrp->param.an_phymask & vrp->param.an_macmask);
+
+ /*
+ * Ensure that autoneg of the PHY matches our default.
+ */
+ if (vrp->param.an_en == VR_LINK_AUTONEG_ON)
+ vrp->chip.mii.control = MII_CONTROL_ANE;
+ else
+ vrp->chip.mii.control =
+ (MII_CONTROL_100MB | MII_CONTROL_FDUPLEX);
+}
+
+/*
+ * Setup the descriptor rings.
+ */
+static vr_result_t
+vr_rings_init(vr_t *vrp)
+{
+
+ vrp->rx.ndesc = VR_RX_N_DESC;
+ vrp->tx.ndesc = VR_TX_N_DESC;
+
+ /*
+ * Create a ring for receive.
+ */
+ if (vr_alloc_ring(vrp, &vrp->rxring, vrp->rx.ndesc) != VR_SUCCESS)
+ return (VR_FAILURE);
+
+ /*
+ * Create a ring for transmit.
+ */
+ if (vr_alloc_ring(vrp, &vrp->txring, vrp->tx.ndesc) != VR_SUCCESS) {
+ vr_free_ring(&vrp->rxring, vrp->rx.ndesc);
+ return (VR_FAILURE);
+ }
+
+ vrp->rx.ring = vrp->rxring.desc;
+ vrp->tx.ring = vrp->txring.desc;
+ return (VR_SUCCESS);
+}
+
+static void
+vr_rings_fini(vr_t *vrp)
+{
+ vr_free_ring(&vrp->rxring, vrp->rx.ndesc);
+ vr_free_ring(&vrp->txring, vrp->tx.ndesc);
+}
+
+/*
+ * Allocate a descriptor ring
+ * The number of descriptor entries must fit in a single page so that the
+ * whole ring fits in one consequtive space.
+ * i386: 4K page / 16 byte descriptor = 256 entries
+ * sparc: 8K page / 16 byte descriptor = 512 entries
+ */
+static vr_result_t
+vr_alloc_ring(vr_t *vrp, vr_ring_t *ring, size_t n)
+{
+ ddi_dma_cookie_t desc_dma_cookie;
+ uint_t desc_cookiecnt;
+ int i, rc;
+ size_t rbytes;
+
+ /*
+ * Allocate a DMA handle for the chip descriptors.
+ */
+ rc = ddi_dma_alloc_handle(vrp->devinfo,
+ &vr_dev_dma_attr,
+ DDI_DMA_SLEEP,
+ NULL,
+ &ring->handle);
+
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_WARN,
+ "ddi_dma_alloc_handle in vr_alloc_ring failed.");
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Allocate memory for the chip descriptors.
+ */
+ rc = ddi_dma_mem_alloc(ring->handle,
+ n * sizeof (vr_chip_desc_t),
+ &vr_dev_dma_accattr,
+ DDI_DMA_CONSISTENT,
+ DDI_DMA_SLEEP,
+ NULL,
+ (caddr_t *)&ring->cdesc,
+ &rbytes,
+ &ring->acchdl);
+
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_WARN,
+ "ddi_dma_mem_alloc in vr_alloc_ring failed.");
+ ddi_dma_free_handle(&ring->handle);
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Map the descriptor memory.
+ */
+ rc = ddi_dma_addr_bind_handle(ring->handle,
+ NULL,
+ (caddr_t)ring->cdesc,
+ rbytes,
+ DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
+ DDI_DMA_SLEEP,
+ NULL,
+ &desc_dma_cookie,
+ &desc_cookiecnt);
+
+ if (rc != DDI_DMA_MAPPED || desc_cookiecnt > 1) {
+ vr_log(vrp, CE_WARN,
+ "ddi_dma_addr_bind_handle in vr_alloc_ring failed: "
+ "rc = %d, cookiecnt = %d", rc, desc_cookiecnt);
+ ddi_dma_mem_free(&ring->acchdl);
+ ddi_dma_free_handle(&ring->handle);
+ return (VR_FAILURE);
+ }
+ ring->cdesc_paddr = desc_dma_cookie.dmac_address;
+
+ /*
+ * Allocate memory for the host descriptor ring.
+ */
+ ring->desc =
+ (vr_desc_t *)kmem_zalloc(n * sizeof (vr_desc_t), KM_SLEEP);
+
+ /*
+ * Interlink the descriptors and connect host- to chip descriptors.
+ */
+ for (i = 0; i < n; i++) {
+ /*
+ * Connect the host descriptor to a chip descriptor.
+ */
+ ring->desc[i].cdesc = &ring->cdesc[i];
+
+ /*
+ * Store the DMA address and offset in the descriptor
+ * Offset is for ddi_dma_sync() and paddr is for ddi_get/-put().
+ */
+ ring->desc[i].offset = i * sizeof (vr_chip_desc_t);
+ ring->desc[i].paddr = ring->cdesc_paddr + ring->desc[i].offset;
+
+ /*
+ * Link the previous descriptor to this one.
+ */
+ if (i > 0) {
+ /* Host */
+ ring->desc[i-1].next = &ring->desc[i];
+
+ /* Chip */
+ ddi_put32(ring->acchdl,
+ &ring->cdesc[i-1].next,
+ ring->desc[i].paddr);
+ }
+ }
+
+ /*
+ * Make rings out of this list by pointing last to first.
+ */
+ i = n - 1;
+ ring->desc[i].next = &ring->desc[0];
+ ddi_put32(ring->acchdl, &ring->cdesc[i].next, ring->desc[0].paddr);
+ return (VR_SUCCESS);
+}
+
+/*
+ * Free the memory allocated for a ring.
+ */
+static void
+vr_free_ring(vr_ring_t *r, size_t n)
+{
+ /*
+ * Unmap and free the chip descriptors.
+ */
+ (void) ddi_dma_unbind_handle(r->handle);
+ ddi_dma_mem_free(&r->acchdl);
+ ddi_dma_free_handle(&r->handle);
+
+ /*
+ * Free the memory for storing host descriptors
+ */
+ kmem_free(r->desc, n * sizeof (vr_desc_t));
+}
+
+/*
+ * Initialize the receive ring.
+ */
+static vr_result_t
+vr_rxring_init(vr_t *vrp)
+{
+ int i, rc;
+ vr_desc_t *rp;
+
+ /*
+ * Set the read pointer at the start of the ring.
+ */
+ vrp->rx.rp = &vrp->rx.ring[0];
+
+ /*
+ * Assign a DMA buffer to each receive descriptor.
+ */
+ for (i = 0; i < vrp->rx.ndesc; i++) {
+ rp = &vrp->rx.ring[i];
+ rc = vr_alloc_dmabuf(vrp,
+ &vrp->rx.ring[i].dmabuf,
+ DDI_DMA_STREAMING | DDI_DMA_READ);
+
+ if (rc != VR_SUCCESS) {
+ while (--i >= 0)
+ vr_free_dmabuf(&vrp->rx.ring[i].dmabuf);
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Store the address of the dma buffer in the chip descriptor
+ */
+ ddi_put32(vrp->rxring.acchdl,
+ &rp->cdesc->data,
+ rp->dmabuf.paddr);
+
+ /*
+ * Put the buffer length in the chip descriptor. Ensure that
+ * length fits in the 11 bits of stat1 (2047/0x7FF)
+ */
+ ddi_put32(vrp->rxring.acchdl, &rp->cdesc->stat1,
+ MIN(VR_MAX_PKTSZ, rp->dmabuf.bufsz));
+
+ /*
+ * Set descriptor ownership to the card
+ */
+ ddi_put32(vrp->rxring.acchdl, &rp->cdesc->stat0, VR_RDES0_OWN);
+
+ /*
+ * Sync the descriptor with main memory
+ */
+ (void) ddi_dma_sync(vrp->rxring.handle, rp->offset,
+ sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
+ }
+ return (VR_SUCCESS);
+}
+
+/*
+ * Free the DMA buffers assigned to the receive ring.
+ */
+static void
+vr_rxring_fini(vr_t *vrp)
+{
+ int i;
+
+ for (i = 0; i < vrp->rx.ndesc; i++)
+ vr_free_dmabuf(&vrp->rx.ring[i].dmabuf);
+}
+
+static vr_result_t
+vr_txring_init(vr_t *vrp)
+{
+ vr_desc_t *wp;
+ int i, rc;
+
+ /*
+ * Set the write- and claim pointer.
+ */
+ vrp->tx.wp = &vrp->tx.ring[0];
+ vrp->tx.cp = &vrp->tx.ring[0];
+
+ /*
+ * (Re)set the TX bookkeeping.
+ */
+ vrp->tx.stallticks = 0;
+ vrp->tx.resched = 0;
+
+ /*
+ * Every transmit decreases nfree. Every reclaim increases nfree.
+ */
+ vrp->tx.nfree = vrp->tx.ndesc;
+
+ /*
+ * Attach a DMA buffer to each transmit descriptor.
+ */
+ for (i = 0; i < vrp->tx.ndesc; i++) {
+ rc = vr_alloc_dmabuf(vrp,
+ &vrp->tx.ring[i].dmabuf,
+ DDI_DMA_STREAMING | DDI_DMA_WRITE);
+
+ if (rc != VR_SUCCESS) {
+ while (--i >= 0)
+ vr_free_dmabuf(&vrp->tx.ring[i].dmabuf);
+ return (VR_FAILURE);
+ }
+ }
+
+ /*
+ * Init & sync the TX descriptors so the device sees a valid ring.
+ */
+ for (i = 0; i < vrp->tx.ndesc; i++) {
+ wp = &vrp->tx.ring[i];
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat0, 0);
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat1, 0);
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->data,
+ wp->dmabuf.paddr);
+ (void) ddi_dma_sync(vrp->txring.handle, wp->offset,
+ sizeof (vr_chip_desc_t),
+ DDI_DMA_SYNC_FORDEV);
+ }
+ return (VR_SUCCESS);
+}
+
+/*
+ * Free the DMA buffers attached to the TX ring.
+ */
+static void
+vr_txring_fini(vr_t *vrp)
+{
+ int i;
+
+ /*
+ * Free the DMA buffers attached to the TX ring
+ */
+ for (i = 0; i < vrp->tx.ndesc; i++)
+ vr_free_dmabuf(&vrp->tx.ring[i].dmabuf);
+}
+
+/*
+ * Allocate a DMA buffer.
+ */
+static vr_result_t
+vr_alloc_dmabuf(vr_t *vrp, vr_data_dma_t *dmap, uint_t dmaflags)
+{
+ ddi_dma_cookie_t dma_cookie;
+ uint_t cookiecnt;
+ int rc;
+
+ /*
+ * Allocate a DMA handle for the buffer
+ */
+ rc = ddi_dma_alloc_handle(vrp->devinfo,
+ &vr_data_dma_attr,
+ DDI_DMA_DONTWAIT, NULL,
+ &dmap->handle);
+
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_WARN,
+ "ddi_dma_alloc_handle failed in vr_alloc_dmabuf");
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Allocate the buffer
+ * The allocated buffer is aligned on 2K boundary. This ensures that
+ * a 1500 byte frame never cross a page boundary and thus that the DMA
+ * mapping can be established in 1 fragment.
+ */
+ rc = ddi_dma_mem_alloc(dmap->handle,
+ VR_DMABUFSZ,
+ &vr_data_dma_accattr,
+ DDI_DMA_RDWR | DDI_DMA_STREAMING,
+ DDI_DMA_DONTWAIT, NULL,
+ &dmap->buf,
+ &dmap->bufsz,
+ &dmap->acchdl);
+
+ if (rc != DDI_SUCCESS) {
+ vr_log(vrp, CE_WARN,
+ "ddi_dma_mem_alloc failed in vr_alloc_dmabuf");
+ ddi_dma_free_handle(&dmap->handle);
+ return (VR_FAILURE);
+ }
+
+ /*
+ * Map the memory
+ */
+ rc = ddi_dma_addr_bind_handle(dmap->handle,
+ NULL,
+ (caddr_t)dmap->buf,
+ dmap->bufsz,
+ dmaflags,
+ DDI_DMA_DONTWAIT,
+ NULL,
+ &dma_cookie,
+ &cookiecnt);
+
+ /*
+ * The cookiecount should never > 1 because we requested 2K alignment
+ */
+ if (rc != DDI_DMA_MAPPED || cookiecnt > 1) {
+ vr_log(vrp, CE_WARN,
+ "dma_addr_bind_handle failed in vr_alloc_dmabuf: "
+ "rc = %d, cookiecnt = %d", rc, cookiecnt);
+ ddi_dma_mem_free(&dmap->acchdl);
+ ddi_dma_free_handle(&dmap->handle);
+ return (VR_FAILURE);
+ }
+ dmap->paddr = dma_cookie.dmac_address;
+ return (VR_SUCCESS);
+}
+
+/*
+ * Destroy a DMA buffer.
+ */
+static void
+vr_free_dmabuf(vr_data_dma_t *dmap)
+{
+ (void) ddi_dma_unbind_handle(dmap->handle);
+ ddi_dma_mem_free(&dmap->acchdl);
+ ddi_dma_free_handle(&dmap->handle);
+}
+
+/*
+ * Interrupt service routine
+ * When our vector is shared with another device, av_dispatch_autovect calls
+ * all service routines for the vector until *none* of them return claimed
+ * That means that, when sharing vectors, this routine is called at least
+ * twice for each interrupt.
+ */
+uint_t
+vr_intr(caddr_t arg1, caddr_t arg2)
+{
+ vr_t *vrp;
+ uint16_t status;
+ mblk_t *lp = NULL;
+ uint32_t tx_resched;
+ uint32_t link_change;
+
+ tx_resched = 0;
+ link_change = 0;
+ vrp = (void *)arg1;
+ _NOTE(ARGUNUSED(arg2))
+
+ /*
+ * Read the status register to see if the interrupt is from our device
+ * This read also ensures that posted writes are brought to main memory.
+ */
+ mutex_enter(&vrp->intrlock);
+ status = VR_GET16(vrp->acc_reg, VR_ISR0) & VR_ICR0_CFG;
+ if (status == 0) {
+ /*
+ * Status contains no configured interrupts
+ * The interrupt was not generated by our device.
+ */
+ vrp->stats.intr_unclaimed++;
+ mutex_exit(&vrp->intrlock);
+ return (DDI_INTR_UNCLAIMED);
+ }
+ vrp->stats.intr_claimed++;
+
+ /*
+ * Acknowledge the event(s) that caused interruption.
+ */
+ VR_PUT16(vrp->acc_reg, VR_ISR0, status);
+
+ /*
+ * Receive completion.
+ */
+ if ((status & (VR_ISR0_RX_DONE | VR_ISR_RX_ERR_BITS)) != 0) {
+ /*
+ * Received some packets.
+ */
+ lp = vr_receive(vrp);
+
+ /*
+ * DMA stops after a conflict in the FIFO.
+ */
+ if ((status & VR_ISR_RX_ERR_BITS) != 0)
+ VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
+ status &= ~(VR_ISR0_RX_DONE | VR_ISR_RX_ERR_BITS);
+ }
+
+ /*
+ * Transmit completion.
+ */
+ if ((status & (VR_ISR0_TX_DONE | VR_ISR_TX_ERR_BITS)) != 0) {
+ /*
+ * Card done with transmitting some packets
+ * TX_DONE is generated 3 times per ring but it appears
+ * more often because it is also set when an RX_DONE
+ * interrupt is generated.
+ */
+ mutex_enter(&vrp->tx.lock);
+ vr_tx_reclaim(vrp);
+ tx_resched = vrp->tx.resched;
+ vrp->tx.resched = 0;
+ mutex_exit(&vrp->tx.lock);
+ status &= ~(VR_ISR0_TX_DONE | VR_ISR_TX_ERR_BITS);
+ }
+
+ /*
+ * Link status change.
+ */
+ if ((status & VR_ICR0_LINKSTATUS) != 0) {
+ /*
+ * Get new link state and inform the mac layer.
+ */
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->tx.lock);
+ vr_link_state(vrp);
+ mutex_exit(&vrp->tx.lock);
+ mutex_exit(&vrp->oplock);
+ status &= ~VR_ICR0_LINKSTATUS;
+ vrp->stats.linkchanges++;
+ link_change = 1;
+ }
+
+ /*
+ * Bus error.
+ */
+ if ((status & VR_ISR0_BUSERR) != 0) {
+ vr_log(vrp, CE_WARN, "bus error occured");
+ vrp->reset = 1;
+ status &= ~VR_ISR0_BUSERR;
+ }
+
+ /*
+ * We must have handled all things here.
+ */
+ ASSERT(status == 0);
+ mutex_exit(&vrp->intrlock);
+
+ /*
+ * Reset the device if requested
+ * The request can come from the periodic tx check or from the interrupt
+ * status.
+ */
+ if (vrp->reset != 0) {
+ vr_error(vrp);
+ vrp->reset = 0;
+ }
+
+ /*
+ * Pass up the list with received packets.
+ */
+ if (lp != NULL)
+ mac_rx(vrp->machdl, 0, lp);
+
+ /*
+ * Inform the upper layer on the linkstatus if there was a change.
+ */
+ if (link_change != 0)
+ mac_link_update(vrp->machdl,
+ (link_state_t)vrp->chip.link.state);
+ /*
+ * Restart transmissions if we were waiting for tx descriptors.
+ */
+ if (tx_resched == 1)
+ mac_tx_update(vrp->machdl);
+
+ /*
+ * Read something from the card to ensure that all of our configuration
+ * writes are delivered to the device before the interrupt is ended.
+ */
+ (void) VR_GET8(vrp->acc_reg, VR_ETHERADDR);
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Respond to an unforseen situation by resetting the card and our bookkeeping.
+ */
+static void
+vr_error(vr_t *vrp)
+{
+ vr_log(vrp, CE_WARN, "resetting MAC.");
+ mutex_enter(&vrp->intrlock);
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->tx.lock);
+ (void) vr_stop(vrp);
+ vr_reset(vrp);
+ (void) vr_start(vrp);
+ mutex_exit(&vrp->tx.lock);
+ mutex_exit(&vrp->oplock);
+ mutex_exit(&vrp->intrlock);
+ vrp->stats.resets++;
+}
+
+/*
+ * Collect received packets in a list.
+ */
+static mblk_t *
+vr_receive(vr_t *vrp)
+{
+ mblk_t *lp, *mp, *np;
+ vr_desc_t *rxp;
+ vr_data_dma_t *dmap;
+ uint32_t pklen;
+ uint32_t rxstat0;
+ uint32_t n;
+
+ lp = NULL;
+ n = 0;
+ for (rxp = vrp->rx.rp; ; rxp = rxp->next, n++) {
+ /*
+ * Sync the descriptor before looking at it.
+ */
+ (void) ddi_dma_sync(vrp->rxring.handle, rxp->offset,
+ sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORKERNEL);
+
+ /*
+ * Get the status from the descriptor.
+ */
+ rxstat0 = ddi_get32(vrp->rxring.acchdl, &rxp->cdesc->stat0);
+
+ /*
+ * We're done if the descriptor is owned by the card.
+ */
+ if ((rxstat0 & VR_RDES0_OWN) != 0)
+ break;
+ else if ((rxstat0 & VR_RDES0_RXOK) != 0) {
+ /*
+ * Received a good packet
+ */
+ dmap = &rxp->dmabuf;
+ pklen = (rxstat0 >> 16) - ETHERFCSL;
+
+ /*
+ * Sync the data.
+ */
+ (void) ddi_dma_sync(dmap->handle, 0,
+ pklen, DDI_DMA_SYNC_FORKERNEL);
+
+ /*
+ * Send a new copied message upstream.
+ */
+ np = allocb(pklen, 0);
+ if (np != NULL) {
+ bcopy(dmap->buf, np->b_rptr, pklen);
+ np->b_wptr = np->b_rptr + pklen;
+
+ vrp->stats.mac_stat_ipackets++;
+ vrp->stats.mac_stat_rbytes += pklen;
+
+ if ((rxstat0 & VR_RDES0_BAR) != 0)
+ vrp->stats.mac_stat_brdcstrcv++;
+ else if ((rxstat0 & VR_RDES0_MAR) != 0)
+ vrp->stats.mac_stat_multircv++;
+
+ /*
+ * Link this packet in the list.
+ */
+ np->b_next = NULL;
+ if (lp == NULL)
+ lp = mp = np;
+ else {
+ mp->b_next = np;
+ mp = np;
+ }
+ } else {
+ vrp->stats.allocbfail++;
+ vrp->stats.mac_stat_norcvbuf++;
+ }
+
+ } else {
+ /*
+ * Received with errors.
+ */
+ vrp->stats.mac_stat_ierrors++;
+ if ((rxstat0 & VR_RDES0_FAE) != 0)
+ vrp->stats.ether_stat_align_errors++;
+ if ((rxstat0 & VR_RDES0_CRCERR) != 0)
+ vrp->stats.ether_stat_fcs_errors++;
+ if ((rxstat0 & VR_RDES0_LONG) != 0)
+ vrp->stats.ether_stat_toolong_errors++;
+ if ((rxstat0 & VR_RDES0_RUNT) != 0)
+ vrp->stats.ether_stat_tooshort_errors++;
+ if ((rxstat0 & VR_RDES0_FOV) != 0)
+ vrp->stats.mac_stat_overflows++;
+ }
+
+ /*
+ * Reset descriptor ownership to the MAC.
+ */
+ ddi_put32(vrp->rxring.acchdl,
+ &rxp->cdesc->stat0,
+ VR_RDES0_OWN);
+ (void) ddi_dma_sync(vrp->rxring.handle,
+ rxp->offset,
+ sizeof (vr_chip_desc_t),
+ DDI_DMA_SYNC_FORDEV);
+ }
+ vrp->rx.rp = rxp;
+
+ /*
+ * If we do flowcontrol and if the card can transmit pause frames,
+ * increment the "available receive descriptors" register.
+ */
+ if (n > 0 && vrp->chip.link.flowctrl == VR_PAUSE_BIDIRECTIONAL) {
+ /*
+ * Whenever the card moves a fragment to host memory it
+ * decrements the RXBUFCOUNT register. If the value in the
+ * register reaches a low watermark, the card transmits a pause
+ * frame. If the value in this register reaches a high
+ * watermark, the card sends a "cancel pause" frame
+ *
+ * Non-zero values written to this byte register are added
+ * by the chip to the register's contents, so we must write
+ * the number of descriptors free'd.
+ */
+ VR_PUT8(vrp->acc_reg, VR_FCR0_RXBUFCOUNT, MIN(n, 0xFF));
+ }
+ return (lp);
+}
+
+/*
+ * Enqueue a list of packets for transmission
+ * Return the packets not transmitted.
+ */
+mblk_t *
+vr_mac_tx_enqueue_list(void *p, mblk_t *mp)
+{
+ vr_t *vrp;
+ mblk_t *nextp;
+
+ vrp = (vr_t *)p;
+ mutex_enter(&vrp->tx.lock);
+ do {
+ if (vrp->tx.nfree == 0) {
+ vrp->stats.ether_stat_defer_xmts++;
+ vrp->tx.resched = 1;
+ break;
+ }
+ nextp = mp->b_next;
+ mp->b_next = mp->b_prev = NULL;
+ vr_tx_enqueue_msg(vrp, mp);
+ mp = nextp;
+ vrp->tx.nfree--;
+ } while (mp != NULL);
+ mutex_exit(&vrp->tx.lock);
+
+ /*
+ * Tell the chip to poll the TX ring.
+ */
+ VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
+ return (mp);
+}
+
+/*
+ * Enqueue a message for transmission.
+ */
+static void
+vr_tx_enqueue_msg(vr_t *vrp, mblk_t *mp)
+{
+ vr_desc_t *wp;
+ vr_data_dma_t *dmap;
+ uint32_t pklen;
+ uint32_t nextp;
+ int padlen;
+
+ if ((uchar_t)mp->b_rptr[0] == 0xff &&
+ (uchar_t)mp->b_rptr[1] == 0xff &&
+ (uchar_t)mp->b_rptr[2] == 0xff &&
+ (uchar_t)mp->b_rptr[3] == 0xff &&
+ (uchar_t)mp->b_rptr[4] == 0xff &&
+ (uchar_t)mp->b_rptr[5] == 0xff)
+ vrp->stats.mac_stat_brdcstxmt++;
+ else if ((uchar_t)mp->b_rptr[0] == 1)
+ vrp->stats.mac_stat_multixmt++;
+
+ pklen = msgsize(mp);
+ wp = vrp->tx.wp;
+ dmap = &wp->dmabuf;
+
+ /*
+ * Copy the message into the pre-mapped buffer and free mp
+ */
+ mcopymsg(mp, dmap->buf);
+
+ /*
+ * Clean padlen bytes of short packet.
+ */
+ padlen = ETHERMIN - pklen;
+ if (padlen > 0) {
+ bzero(dmap->buf + pklen, padlen);
+ pklen += padlen;
+ }
+
+ /*
+ * Most of the statistics are updated on reclaim, after the actual
+ * transmit. obytes is maintained here because the length is cleared
+ * after transmission
+ */
+ vrp->stats.mac_stat_obytes += pklen;
+
+ /*
+ * Sync the data so the device sees the new content too.
+ */
+ (void) ddi_dma_sync(dmap->handle, 0, pklen, DDI_DMA_SYNC_FORDEV);
+
+ /*
+ * If we have reached the TX interrupt distance, enable a TX interrupt
+ * for this packet. The Interrupt Control (IC) bit in the transmit
+ * descriptor doesn't have any effect on the interrupt generation
+ * despite the vague statements in the datasheet. Thus, we use the
+ * more obscure interrupt suppress bit which is probably part of the
+ * MAC's bookkeeping for TX interrupts and fragmented packets.
+ */
+ vrp->tx.intr_distance++;
+ nextp = ddi_get32(vrp->txring.acchdl, &wp->cdesc->next);
+ if (vrp->tx.intr_distance >= VR_TX_MAX_INTR_DISTANCE) {
+ /*
+ * Don't suppress the interrupt for this packet.
+ */
+ vrp->tx.intr_distance = 0;
+ nextp &= (~VR_TDES3_SUPPRESS_INTR);
+ } else {
+ /*
+ * Suppress the interrupt for this packet.
+ */
+ nextp |= VR_TDES3_SUPPRESS_INTR;
+ }
+
+ /*
+ * Write and sync the chip's descriptor
+ */
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat1,
+ pklen | (VR_TDES1_STP | VR_TDES1_EDP | VR_TDES1_CHN));
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->next, nextp);
+ ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat0, VR_TDES0_OWN);
+ (void) ddi_dma_sync(vrp->txring.handle, wp->offset,
+ sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
+
+ /*
+ * The ticks counter is cleared by reclaim when it reclaimed some
+ * descriptors and incremented by the periodic TX stall check.
+ */
+ vrp->tx.stallticks = 1;
+ vrp->tx.wp = wp->next;
+}
+
+/*
+ * Free transmitted descriptors.
+ */
+static void
+vr_tx_reclaim(vr_t *vrp)
+{
+ vr_desc_t *cp;
+ uint32_t stat0, stat1, freed, dirty;
+
+ ASSERT(mutex_owned(&vrp->tx.lock));
+
+ freed = 0;
+ dirty = vrp->tx.ndesc - vrp->tx.nfree;
+ for (cp = vrp->tx.cp; dirty > 0; cp = cp->next) {
+ /*
+ * Sync & get descriptor status.
+ */
+ (void) ddi_dma_sync(vrp->txring.handle, cp->offset,
+ sizeof (vr_chip_desc_t),
+ DDI_DMA_SYNC_FORKERNEL);
+ stat0 = ddi_get32(vrp->txring.acchdl, &cp->cdesc->stat0);
+
+ if ((stat0 & VR_TDES0_OWN) != 0)
+ break;
+
+ /*
+ * Do stats for the first descriptor in a chain.
+ */
+ stat1 = ddi_get32(vrp->txring.acchdl, &cp->cdesc->stat1);
+ if ((stat1 & VR_TDES1_STP) != 0) {
+ if ((stat0 & VR_TDES0_TERR) != 0) {
+ vrp->stats.ether_stat_macxmt_errors++;
+ if ((stat0 & VR_TDES0_UDF) != 0)
+ vrp->stats.mac_stat_underflows++;
+ if ((stat0 & VR_TDES0_ABT) != 0)
+ vrp-> stats.ether_stat_ex_collisions++;
+ /*
+ * Abort and FIFO underflow stop the MAC.
+ * Packet queueing must be disabled with HD
+ * links because otherwise the MAC is also lost
+ * after a few of these events.
+ */
+ VR_PUT8(vrp->acc_reg, VR_CTRL0,
+ VR_CTRL0_DMA_GO);
+ } else
+ vrp->stats.mac_stat_opackets++;
+
+ if ((stat0 & VR_TDES0_COL) != 0) {
+ if ((stat0 & VR_TDES0_NCR) == 1) {
+ vrp->stats.
+ ether_stat_first_collisions++;
+ } else {
+ vrp->stats.
+ ether_stat_multi_collisions++;
+ }
+ vrp->stats.mac_stat_collisions +=
+ (stat0 & VR_TDES0_NCR);
+ }
+
+ if ((stat0 & VR_TDES0_CRS) != 0)
+ vrp->stats.ether_stat_carrier_errors++;
+
+ if ((stat0 & VR_TDES0_OWC) != 0)
+ vrp->stats.ether_stat_tx_late_collisions++;
+ }
+ freed += 1;
+ dirty -= 1;
+ }
+ vrp->tx.cp = cp;
+
+ if (freed > 0) {
+ vrp->tx.nfree += freed;
+ vrp->tx.stallticks = 0;
+ vrp->stats.txreclaims += 1;
+ } else
+ vrp->stats.txreclaim0 += 1;
+}
+
+/*
+ * Check TX health every 2 seconds.
+ */
+static void
+vr_periodic(void *p)
+{
+ vr_t *vrp;
+
+ vrp = (vr_t *)p;
+ if (vrp->chip.state == CHIPSTATE_RUNNING &&
+ vrp->chip.link.state == VR_LINK_STATE_UP && vrp->reset == 0) {
+ if (mutex_tryenter(&vrp->intrlock) != 0) {
+ mutex_enter(&vrp->tx.lock);
+ if (vrp->tx.resched == 1) {
+ if (vrp->tx.stallticks >= VR_MAXTXCHECKS) {
+ /*
+ * No succesful reclaim in the last n
+ * intervals. Reset the MAC.
+ */
+ vrp->reset = 1;
+ vr_log(vrp, CE_WARN,
+ "TX stalled, resetting MAC");
+ vrp->stats.txstalls++;
+ } else {
+ /*
+ * Increase until we find that we've
+ * waited long enough.
+ */
+ vrp->tx.stallticks += 1;
+ }
+ }
+ mutex_exit(&vrp->tx.lock);
+ mutex_exit(&vrp->intrlock);
+ vrp->stats.txchecks++;
+ }
+ }
+ vrp->stats.cyclics++;
+}
+
+/*
+ * Bring the device to our desired initial state.
+ */
+static void
+vr_reset(vr_t *vrp)
+{
+ uint32_t time;
+
+ /*
+ * Reset the MAC
+ * If we don't wait long enough for the forced reset to complete,
+ * MAC looses sync with PHY. Result link up, no link change interrupt
+ * and no data transfer.
+ */
+ time = 0;
+ VR_PUT8(vrp->acc_io, VR_CTRL1, VR_CTRL1_RESET);
+ do {
+ drv_usecwait(100);
+ time += 100;
+ if (time >= 100000) {
+ VR_PUT8(vrp->acc_io, VR_MISC1, VR_MISC1_RESET);
+ delay(drv_usectohz(200000));
+ }
+ } while ((VR_GET8(vrp->acc_io, VR_CTRL1) & VR_CTRL1_RESET) != 0);
+ delay(drv_usectohz(10000));
+
+ /*
+ * Load the PROM contents into the MAC again.
+ */
+ VR_SETBIT8(vrp->acc_io, VR_PROMCTL, VR_PROMCTL_RELOAD);
+ delay(drv_usectohz(100000));
+
+ /*
+ * Tell the MAC via IO space that we like to use memory space for
+ * accessing registers.
+ */
+ VR_SETBIT8(vrp->acc_io, VR_CFGD, VR_CFGD_MMIOEN);
+}
+
+/*
+ * Prepare and enable the card (MAC + PHY + PCI).
+ */
+static int
+vr_start(vr_t *vrp)
+{
+ uint8_t pci_latency, pci_mode;
+
+ ASSERT(mutex_owned(&vrp->oplock));
+
+ /*
+ * Allocate DMA buffers for RX.
+ */
+ if (vr_rxring_init(vrp) != VR_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "vr_rxring_init() failed");
+ return (ENOMEM);
+ }
+
+ /*
+ * Allocate DMA buffers for TX.
+ */
+ if (vr_txring_init(vrp) != VR_SUCCESS) {
+ vr_log(vrp, CE_NOTE, "vr_txring_init() failed");
+ vr_rxring_fini(vrp);
+ return (ENOMEM);
+ }
+
+ /*
+ * Changes of the chip specific registers as done in VIA's fet driver
+ * These bits are not in the datasheet and controlled by vr_chip_info.
+ */
+ pci_mode = VR_GET8(vrp->acc_reg, VR_MODE2);
+ if ((vrp->chip.info.bugs & VR_BUG_NEEDMODE10T) != 0)
+ pci_mode |= VR_MODE2_MODE10T;
+
+ if ((vrp->chip.info.bugs & VR_BUG_NEEDMODE2PCEROPT) != 0)
+ pci_mode |= VR_MODE2_PCEROPT;
+
+ if ((vrp->chip.info.features & VR_FEATURE_MRDLNMULTIPLE) != 0)
+ pci_mode |= VR_MODE2_MRDPL;
+ VR_PUT8(vrp->acc_reg, VR_MODE2, pci_mode);
+
+ pci_mode = VR_GET8(vrp->acc_reg, VR_MODE3);
+ if ((vrp->chip.info.bugs & VR_BUG_NEEDMIION) != 0)
+ pci_mode |= VR_MODE3_MIION;
+ VR_PUT8(vrp->acc_reg, VR_MODE3, pci_mode);
+
+ /*
+ * RX: Accept broadcast packets.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTBROAD);
+
+ /*
+ * RX: Start DMA when there are 256 bytes in the FIFO.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_FIFO_THRESHOLD_BITS,
+ VR_RXCFG_FIFO_THRESHOLD_256);
+ VR_SETBITS8(vrp->acc_reg, VR_BCR0, VR_BCR0_RX_FIFO_THRESHOLD_BITS,
+ VR_BCR0_RX_FIFO_THRESHOLD_256);
+
+ /*
+ * TX: Start transmit when there are 256 bytes in the FIFO.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_TXCFG, VR_TXCFG_FIFO_THRESHOLD_BITS,
+ VR_TXCFG_FIFO_THRESHOLD_256);
+ VR_SETBITS8(vrp->acc_reg, VR_BCR1, VR_BCR1_TX_FIFO_THRESHOLD_BITS,
+ VR_BCR1_TX_FIFO_THRESHOLD_256);
+
+ /*
+ * Burst transfers up to 256 bytes.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_BCR0, VR_BCR0_DMABITS, VR_BCR0_DMA256);
+
+ /*
+ * Disable TX autopolling as it is bad for RX performance
+ * I assume this is because the RX process finds the bus often occupied
+ * by the polling process.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_NOAUTOPOLL);
+
+ /*
+ * Honor the PCI latency timer if it is reasonable.
+ */
+ pci_latency = VR_GET8(vrp->acc_cfg, PCI_CONF_LATENCY_TIMER);
+ if (pci_latency != 0 && pci_latency != 0xFF)
+ VR_SETBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_LATENCYTIMER);
+ else
+ VR_CLRBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_LATENCYTIMER);
+
+ /*
+ * Ensure that VLAN filtering is off, because this strips the tag.
+ */
+ if ((vrp->chip.info.features & VR_FEATURE_VLANTAGGING) != 0) {
+ VR_CLRBIT8(vrp->acc_reg, VR_BCR1, VR_BCR1_VLANFILTER);
+ VR_CLRBIT8(vrp->acc_reg, VR_TXCFG, VR_TXCFG_8021PQ_EN);
+ }
+
+ /*
+ * Clear the CAM filter.
+ */
+ if ((vrp->chip.info.features & VR_FEATURE_CAMSUPPORT) != 0) {
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_ENABLE);
+ VR_PUT32(vrp->acc_reg, VR_CAM_MASK, 0);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
+
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_ENABLE|VR_CAM_CTRL_SELECT_VLAN);
+ VR_PUT8(vrp->acc_reg, VR_VCAM0, 0);
+ VR_PUT8(vrp->acc_reg, VR_VCAM1, 0);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_WRITE);
+ VR_PUT32(vrp->acc_reg, VR_CAM_MASK, 1);
+ drv_usecwait(2);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
+ }
+
+ /*
+ * Give the start addresses of the descriptor rings to the DMA
+ * controller on the MAC.
+ */
+ VR_PUT32(vrp->acc_reg, VR_RXADDR, vrp->rx.rp->paddr);
+ VR_PUT32(vrp->acc_reg, VR_TXADDR, vrp->tx.wp->paddr);
+
+ /*
+ * We don't use the additionally invented interrupt ICR1 register,
+ * so make sure these are disabled.
+ */
+ VR_PUT8(vrp->acc_reg, VR_ISR1, 0xFF);
+ VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
+
+ /*
+ * Enable interrupts.
+ */
+ VR_PUT16(vrp->acc_reg, VR_ISR0, 0xFFFF);
+ VR_PUT16(vrp->acc_reg, VR_ICR0, VR_ICR0_CFG);
+
+ /*
+ * Enable the DMA controller.
+ */
+ VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
+
+ /*
+ * Configure the link. Rely on the link change interrupt for getting
+ * the link state into the driver.
+ */
+ vr_link_init(vrp);
+
+ /*
+ * Set the software view on the state to 'running'.
+ */
+ vrp->chip.state = CHIPSTATE_RUNNING;
+ return (0);
+}
+
+/*
+ * Stop DMA and interrupts.
+ */
+static int
+vr_stop(vr_t *vrp)
+{
+ ASSERT(mutex_owned(&vrp->oplock));
+
+ /*
+ * Stop interrupts.
+ */
+ VR_PUT16(vrp->acc_reg, VR_ICR0, 0);
+ VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
+
+ /*
+ * Stop DMA.
+ */
+ VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_STOP);
+
+ /*
+ * Set the software view on the state to stopped.
+ */
+ vrp->chip.state = CHIPSTATE_STOPPED;
+
+ /*
+ * Remove DMA buffers from the rings.
+ */
+ vr_rxring_fini(vrp);
+ vr_txring_fini(vrp);
+ return (0);
+}
+
+int
+vr_mac_start(void *p)
+{
+ vr_t *vrp;
+ int rc;
+
+ vrp = (vr_t *)p;
+ mutex_enter(&vrp->oplock);
+
+ /*
+ * Reset the card.
+ */
+ vr_reset(vrp);
+
+ /*
+ * Prepare and enable the card.
+ */
+ rc = vr_start(vrp);
+
+ /*
+ * Configure a cyclic function to keep the card & driver from diverting.
+ */
+ vrp->periodic_id =
+ ddi_periodic_add(vr_periodic, vrp, VR_CHECK_INTERVAL, DDI_IPL_0);
+
+ mutex_exit(&vrp->oplock);
+ return (rc);
+}
+
+void
+vr_mac_stop(void *p)
+{
+ vr_t *vrp = p;
+
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->tx.lock);
+
+ /*
+ * Stop the device.
+ */
+ (void) vr_stop(vrp);
+ mutex_exit(&vrp->tx.lock);
+
+ /*
+ * Remove the cyclic from the system.
+ */
+ ddi_periodic_delete(vrp->periodic_id);
+ mutex_exit(&vrp->oplock);
+}
+
+/*
+ * Add or remove a multicast address to/from the filter
+ *
+ * From the 21143 manual:
+ * The 21143 can store 512 bits serving as hash bucket heads, and one physical
+ * 48-bit Ethernet address. Incoming frames with multicast destination
+ * addresses are subjected to imperfect filtering. Frames with physical
+ * destination addresses are checked against the single physical address.
+ * For any incoming frame with a multicast destination address, the 21143
+ * applies the standard Ethernet cyclic redundancy check (CRC) function to the
+ * first 6 bytes containing the destination address, then it uses the most
+ * significant 9 bits of the result as a bit index into the table. If the
+ * indexed bit is set, the frame is accepted. If the bit is cleared, the frame
+ * is rejected. This filtering mode is called imperfect because multicast
+ * frames not addressed to this station may slip through, but it still
+ * decreases the number of frames that the host can receive.
+ * I assume the above is also the way the VIA chips work. There's not a single
+ * word about the multicast filter in the datasheet.
+ *
+ * Another word on the CAM filter on VT6105M controllers:
+ * The VT6105M has content addressable memory which can be used for perfect
+ * filtering of 32 multicast addresses and a few VLAN id's
+ *
+ * I think it works like this: When the controller receives a multicast
+ * address, it looks up the address using CAM. When it is found, it takes the
+ * matching cell address (index) and compares this to the bit position in the
+ * cam mask. If the bit is set, the packet is passed up. If CAM lookup does not
+ * result in a match, the packet is filtered using the hash based filter,
+ * if that matches, the packet is passed up and dropped otherwise
+ * Also, there's not a single word in the datasheet on how this cam is supposed
+ * to work ...
+ */
+int
+vr_mac_set_multicast(void *p, boolean_t add, const uint8_t *mca)
+{
+ vr_t *vrp;
+ uint32_t crc_index;
+ int32_t cam_index;
+ uint32_t cam_mask;
+ boolean_t use_hash_filter;
+ ether_addr_t taddr;
+ uint32_t a;
+
+ vrp = (vr_t *)p;
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->intrlock);
+ use_hash_filter = B_FALSE;
+
+ if ((vrp->chip.info.features & VR_FEATURE_CAMSUPPORT) != 0) {
+ /*
+ * Program the perfect filter.
+ */
+ cam_mask = VR_GET32(vrp->acc_reg, VR_CAM_MASK);
+ if (add == B_TRUE) {
+ /*
+ * Get index of first empty slot.
+ */
+ bzero(&taddr, sizeof (taddr));
+ cam_index = vr_cam_index(vrp, taddr);
+ if (cam_index != -1) {
+ /*
+ * Add address at cam_index.
+ */
+ cam_mask |= (1 << cam_index);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_ENABLE);
+ VR_PUT8(vrp->acc_reg, VR_CAM_ADDR, cam_index);
+ VR_PUT32(vrp->acc_reg, VR_CAM_MASK, cam_mask);
+ for (a = 0; a < ETHERADDRL; a++) {
+ VR_PUT8(vrp->acc_reg,
+ VR_MCAM0 + a, mca[a]);
+ }
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_WRITE);
+ drv_usecwait(2);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_DONE);
+ } else {
+ /*
+ * No free CAM slots available
+ * Add mca to the imperfect filter.
+ */
+ use_hash_filter = B_TRUE;
+ }
+ } else {
+ /*
+ * Find the index of the entry to remove
+ * If the entry was not found (-1), the addition was
+ * probably done when the table was full.
+ */
+ cam_index = vr_cam_index(vrp, mca);
+ if (cam_index != -1) {
+ /*
+ * Disable the corresponding mask bit.
+ */
+ cam_mask &= ~(1 << cam_index);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_ENABLE);
+ VR_PUT32(vrp->acc_reg, VR_CAM_MASK, cam_mask);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
+ VR_CAM_CTRL_DONE);
+ } else {
+ /*
+ * The entry to be removed was not found
+ * The likely cause is that the CAM was full
+ * during addition. The entry is added to the
+ * hash filter in that case and needs to be
+ * removed there too.
+ */
+ use_hash_filter = B_TRUE;
+ }
+ }
+ } else {
+ /*
+ * No CAM in the MAC, thus we need the hash filter.
+ */
+ use_hash_filter = B_TRUE;
+ }
+
+ if (use_hash_filter == B_TRUE) {
+ /*
+ * Get the CRC-32 of the multicast address
+ * The card uses the "MSB first" direction when calculating the
+ * the CRC. This is odd because ethernet is "LSB first"
+ * We have to use that "big endian" approach as well.
+ */
+ crc_index = ether_crc_be(mca) >> (32 - 6);
+ if (add == B_TRUE) {
+ /*
+ * Turn bit[crc_index] on.
+ */
+ if (crc_index < 32)
+ vrp->mhash0 |= (1 << crc_index);
+ else
+ vrp->mhash1 |= (1 << (crc_index - 32));
+ } else {
+ /*
+ * Turn bit[crc_index] off.
+ */
+ if (crc_index < 32)
+ vrp->mhash0 &= ~(0 << crc_index);
+ else
+ vrp->mhash1 &= ~(0 << (crc_index - 32));
+ }
+
+ /*
+ * When not promiscuous write the filter now. When promiscuous,
+ * the filter is open and will be written when promiscuous ends.
+ */
+ if (vrp->promisc == B_FALSE) {
+ VR_PUT32(vrp->acc_reg, VR_MAR0, vrp->mhash0);
+ VR_PUT32(vrp->acc_reg, VR_MAR1, vrp->mhash1);
+ }
+ }
+
+ /*
+ * Enable/disable multicast receivements based on mcount.
+ */
+ if (add == B_TRUE)
+ vrp->mcount++;
+ else if (vrp->mcount != 0)
+ vrp->mcount --;
+ if (vrp->mcount != 0)
+ VR_SETBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTMULTI);
+ else
+ VR_CLRBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTMULTI);
+
+ mutex_exit(&vrp->intrlock);
+ mutex_exit(&vrp->oplock);
+ return (0);
+}
+
+/*
+ * Calculate the CRC32 for 6 bytes of multicast address in MSB(it) first order.
+ * The MSB first order is a bit odd because Ethernet standard is LSB first
+ */
+static uint32_t
+ether_crc_be(const uint8_t *data)
+{
+ uint32_t crc = (uint32_t)0xFFFFFFFFU;
+ uint32_t carry;
+ uint32_t bit;
+ uint32_t length;
+ uint8_t c;
+
+ for (length = 0; length < ETHERADDRL; length++) {
+ c = data[length];
+ for (bit = 0; bit < 8; bit++) {
+ carry = ((crc & 0x80000000U) ? 1 : 0) ^ (c & 0x01);
+ crc <<= 1;
+ c >>= 1;
+ if (carry)
+ crc = (crc ^ 0x04C11DB6) | carry;
+ }
+ }
+ return (crc);
+}
+
+
+/*
+ * Return the CAM index (base 0) of maddr or -1 if maddr is not found
+ * If maddr is 0, return the index of an empty slot in CAM or -1 when no free
+ * slots available.
+ */
+static int32_t
+vr_cam_index(vr_t *vrp, const uint8_t *maddr)
+{
+ ether_addr_t taddr;
+ int32_t index;
+ uint32_t mask;
+ uint32_t a;
+
+ bzero(&taddr, sizeof (taddr));
+
+ /*
+ * Read the CAM mask from the controller.
+ */
+ mask = VR_GET32(vrp->acc_reg, VR_CAM_MASK);
+
+ /*
+ * If maddr is 0, return the first unused slot or -1 for no unused.
+ */
+ if (bcmp(maddr, taddr, ETHERADDRL) == 0) {
+ /*
+ * Look for the first unused position in mask.
+ */
+ for (index = 0; index < VR_CAM_SZ; index++) {
+ if (((mask >> index) & 1) == 0)
+ return (index);
+ }
+ return (-1);
+ } else {
+ /*
+ * Look for maddr in CAM.
+ */
+ for (index = 0; index < VR_CAM_SZ; index++) {
+ /* Look at enabled entries only */
+ if (((mask >> index) & 1) == 0)
+ continue;
+
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_ENABLE);
+ VR_PUT8(vrp->acc_reg, VR_CAM_ADDR, index);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_READ);
+ drv_usecwait(2);
+ for (a = 0; a < ETHERADDRL; a++)
+ taddr[a] = VR_GET8(vrp->acc_reg, VR_MCAM0 + a);
+ VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
+ if (bcmp(maddr, taddr, ETHERADDRL) == 0)
+ return (index);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Set promiscuous mode on or off.
+ */
+int
+vr_mac_set_promisc(void *p, boolean_t promiscflag)
+{
+ vr_t *vrp;
+ uint8_t rxcfg;
+
+ vrp = (vr_t *)p;
+
+ mutex_enter(&vrp->intrlock);
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->tx.lock);
+
+ /*
+ * Get current receive configuration.
+ */
+ rxcfg = VR_GET8(vrp->acc_reg, VR_RXCFG);
+ vrp->promisc = promiscflag;
+
+ if (promiscflag == B_TRUE) {
+ /*
+ * Enable promiscuous mode and open the multicast filter.
+ */
+ rxcfg |= (VR_RXCFG_PROMISC | VR_RXCFG_ACCEPTMULTI);
+ VR_PUT32(vrp->acc_reg, VR_MAR0, 0xffffffff);
+ VR_PUT32(vrp->acc_reg, VR_MAR1, 0xffffffff);
+ } else {
+ /*
+ * Restore the multicast filter and disable promiscuous mode.
+ */
+ VR_PUT32(vrp->acc_reg, VR_MAR0, vrp->mhash0);
+ VR_PUT32(vrp->acc_reg, VR_MAR1, vrp->mhash1);
+ rxcfg &= ~VR_RXCFG_PROMISC;
+ if (vrp->mcount != 0)
+ rxcfg |= VR_RXCFG_ACCEPTMULTI;
+ }
+ VR_PUT8(vrp->acc_reg, VR_RXCFG, rxcfg);
+ mutex_exit(&vrp->tx.lock);
+ mutex_exit(&vrp->oplock);
+ mutex_exit(&vrp->intrlock);
+ return (0);
+}
+
+int
+vr_mac_getstat(void *arg, uint_t stat, uint64_t *val)
+{
+ vr_t *vrp;
+ uint64_t v;
+
+ vrp = (void *) arg;
+
+ switch (stat) {
+ default:
+ return (ENOTSUP);
+
+ case ETHER_STAT_ADV_CAP_100T4:
+ v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_T4) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_100FDX:
+ v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_TX_FD) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_100HDX:
+ v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_TX) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_10FDX:
+ v = (vrp->chip.mii.anadv & MII_ABILITY_10BASE_T_FD) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_10HDX:
+ v = (vrp->chip.mii.anadv & MII_ABILITY_10BASE_T) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_ASMPAUSE:
+ v = 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_AUTONEG:
+ v = (vrp->chip.mii.control & MII_CONTROL_ANE) != 0;
+ break;
+
+ case ETHER_STAT_ADV_CAP_PAUSE:
+ v = (vrp->chip.mii.anadv & MII_AN_ADVERT_FCS) != 0;
+ break;
+
+ case ETHER_STAT_ADV_REMFAULT:
+ v = (vrp->chip.mii.anadv & MII_AN_ADVERT_REMFAULT) != 0;
+ break;
+
+ case ETHER_STAT_ALIGN_ERRORS:
+ v = vrp->stats.ether_stat_align_errors;
+ break;
+
+ case ETHER_STAT_CAP_100T4:
+ v = (vrp->chip.mii.status & MII_STATUS_100_BASE_T4) != 0;
+ break;
+
+ case ETHER_STAT_CAP_100FDX:
+ v = (vrp->chip.mii.status & MII_STATUS_100_BASEX_FD) != 0;
+ break;
+
+ case ETHER_STAT_CAP_100HDX:
+ v = (vrp->chip.mii.status & MII_STATUS_100_BASEX) != 0;
+ break;
+
+ case ETHER_STAT_CAP_10FDX:
+ v = (vrp->chip.mii.status & MII_STATUS_10_FD) != 0;
+ break;
+
+ case ETHER_STAT_CAP_10HDX:
+ v = (vrp->chip.mii.status & MII_STATUS_10) != 0;
+ break;
+
+ case ETHER_STAT_CAP_ASMPAUSE:
+ v = 0;
+ break;
+
+ case ETHER_STAT_CAP_AUTONEG:
+ v = (vrp->chip.mii.status & MII_STATUS_CANAUTONEG) != 0;
+ break;
+
+ case ETHER_STAT_CAP_PAUSE:
+ v = 1;
+ break;
+
+ case ETHER_STAT_CAP_REMFAULT:
+ v = (vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0;
+ break;
+
+ case ETHER_STAT_CARRIER_ERRORS:
+ /*
+ * Number of times carrier was lost or never detected on a
+ * transmission attempt.
+ */
+ v = vrp->stats.ether_stat_carrier_errors;
+ break;
+
+ case ETHER_STAT_JABBER_ERRORS:
+ return (ENOTSUP);
+
+ case ETHER_STAT_DEFER_XMTS:
+ /*
+ * Packets without collisions where first transmit attempt was
+ * delayed because the medium was busy.
+ */
+ v = vrp->stats.ether_stat_defer_xmts;
+ break;
+
+ case ETHER_STAT_EX_COLLISIONS:
+ /*
+ * Frames where excess collisions occurred on transmit, causing
+ * transmit failure.
+ */
+ v = vrp->stats.ether_stat_ex_collisions;
+ break;
+
+ case ETHER_STAT_FCS_ERRORS:
+ /*
+ * Packets received with CRC errors.
+ */
+ v = vrp->stats.ether_stat_fcs_errors;
+ break;
+
+ case ETHER_STAT_FIRST_COLLISIONS:
+ /*
+ * Packets successfully transmitted with exactly one collision.
+ */
+ v = vrp->stats.ether_stat_first_collisions;
+ break;
+
+ case ETHER_STAT_LINK_ASMPAUSE:
+ v = 0;
+ break;
+
+ case ETHER_STAT_LINK_AUTONEG:
+ v = (vrp->chip.mii.control & MII_CONTROL_ANE) != 0 &&
+ (vrp->chip.mii.status & MII_STATUS_ANDONE) != 0;
+ break;
+
+ case ETHER_STAT_LINK_DUPLEX:
+ v = vrp->chip.link.duplex;
+ break;
+
+ case ETHER_STAT_LINK_PAUSE:
+ v = vrp->chip.link.flowctrl;
+ break;
+
+ case ETHER_STAT_LP_CAP_100T4:
+ v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_T4) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_1000FDX:
+ v = 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_1000HDX:
+ v = 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_100FDX:
+ v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_TX_FD) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_100HDX:
+ v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_TX) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_10FDX:
+ v = (vrp->chip.mii.lpable & MII_ABILITY_10BASE_T_FD) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_10HDX:
+ v = (vrp->chip.mii.lpable & MII_ABILITY_10BASE_T) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_ASMPAUSE:
+ v = 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_AUTONEG:
+ v = (vrp->chip.mii.anexp & MII_AN_EXP_LPCANAN) != 0;
+ break;
+
+ case ETHER_STAT_LP_CAP_PAUSE:
+ v = (vrp->chip.mii.lpable & MII_AN_ADVERT_FCS) != 0;
+ break;
+
+ case ETHER_STAT_LP_REMFAULT:
+ v = (vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0;
+ break;
+
+ case ETHER_STAT_MACRCV_ERRORS:
+ /*
+ * Packets received with MAC errors, except align_errors,
+ * fcs_errors, and toolong_errors.
+ */
+ v = vrp->stats.ether_stat_macrcv_errors;
+ break;
+
+ case ETHER_STAT_MACXMT_ERRORS:
+ /*
+ * Packets encountering transmit MAC failures, except carrier
+ * and collision failures.
+ */
+ v = vrp->stats.ether_stat_macxmt_errors;
+ break;
+
+ case ETHER_STAT_MULTI_COLLISIONS:
+ /*
+ * Packets successfully transmitted with multiple collisions.
+ */
+ v = vrp->stats.ether_stat_multi_collisions;
+ break;
+
+ case ETHER_STAT_SQE_ERRORS:
+ /*
+ * Number of times signal quality error was reported
+ * This one is reported by the PHY.
+ */
+ return (ENOTSUP);
+
+ case ETHER_STAT_TOOLONG_ERRORS:
+ /*
+ * Packets received larger than the maximum permitted length.
+ */
+ v = vrp->stats.ether_stat_toolong_errors;
+ break;
+
+ case ETHER_STAT_TOOSHORT_ERRORS:
+ v = vrp->stats.ether_stat_tooshort_errors;
+ break;
+
+ case ETHER_STAT_TX_LATE_COLLISIONS:
+ /*
+ * Number of times a transmit collision occurred late
+ * (after 512 bit times).
+ */
+ v = vrp->stats.ether_stat_tx_late_collisions;
+ break;
+
+ case ETHER_STAT_XCVR_ADDR:
+ /*
+ * MII address in the 0 to 31 range of the physical layer
+ * device in use for a given Ethernet device.
+ */
+ v = vrp->chip.phyaddr;
+ break;
+
+ case ETHER_STAT_XCVR_ID:
+ /*
+ * MII transceiver manufacturer and device ID.
+ */
+ v = (vrp->chip.mii.identh << 16) | vrp->chip.mii.identl;
+ break;
+
+ case ETHER_STAT_XCVR_INUSE:
+ v = vrp->chip.link.mau;
+ break;
+
+ case MAC_STAT_BRDCSTRCV:
+ v = vrp->stats.mac_stat_brdcstrcv;
+ break;
+
+ case MAC_STAT_BRDCSTXMT:
+ v = vrp->stats.mac_stat_brdcstxmt;
+ break;
+
+ case MAC_STAT_MULTIXMT:
+ v = vrp->stats.mac_stat_multixmt;
+ break;
+
+ case MAC_STAT_COLLISIONS:
+ v = vrp->stats.mac_stat_collisions;
+ break;
+
+ case MAC_STAT_IERRORS:
+ v = vrp->stats.mac_stat_ierrors;
+ break;
+
+ case MAC_STAT_IFSPEED:
+ if (vrp->chip.link.speed == VR_LINK_SPEED_100MBS)
+ v = 100 * 1000 * 1000;
+ else if (vrp->chip.link.speed == VR_LINK_SPEED_10MBS)
+ v = 10 * 1000 * 1000;
+ else
+ v = 0;
+ break;
+
+ case MAC_STAT_IPACKETS:
+ v = vrp->stats.mac_stat_ipackets;
+ break;
+
+ case MAC_STAT_MULTIRCV:
+ v = vrp->stats.mac_stat_multircv;
+ break;
+
+ case MAC_STAT_NORCVBUF:
+ vrp->stats.mac_stat_norcvbuf +=
+ VR_GET16(vrp->acc_reg, VR_TALLY_MPA);
+ VR_PUT16(vrp->acc_reg, VR_TALLY_MPA, 0);
+ v = vrp->stats.mac_stat_norcvbuf;
+ break;
+
+ case MAC_STAT_NOXMTBUF:
+ v = vrp->stats.mac_stat_noxmtbuf;
+ break;
+
+ case MAC_STAT_OBYTES:
+ v = vrp->stats.mac_stat_obytes;
+ break;
+
+ case MAC_STAT_OERRORS:
+ v = vrp->stats.ether_stat_macxmt_errors +
+ vrp->stats.mac_stat_underflows +
+ vrp->stats.ether_stat_align_errors +
+ vrp->stats.ether_stat_carrier_errors +
+ vrp->stats.ether_stat_fcs_errors;
+ break;
+
+ case MAC_STAT_OPACKETS:
+ v = vrp->stats.mac_stat_opackets;
+ break;
+
+ case MAC_STAT_RBYTES:
+ v = vrp->stats.mac_stat_rbytes;
+ break;
+
+ case MAC_STAT_UNKNOWNS:
+ /*
+ * Isn't this something for the MAC layer to maintain?
+ */
+ return (ENOTSUP);
+
+ case MAC_STAT_UNDERFLOWS:
+ v = vrp->stats.mac_stat_underflows;
+ break;
+
+ case MAC_STAT_OVERFLOWS:
+ v = vrp->stats.mac_stat_overflows;
+ break;
+ }
+ *val = v;
+ return (0);
+}
+
+int
+vr_mac_set_ether_addr(void *p, const uint8_t *ea)
+{
+ vr_t *vrp;
+ int i;
+
+ vrp = (vr_t *)p;
+ mutex_enter(&vrp->oplock);
+ mutex_enter(&vrp->intrlock);
+
+ /*
+ * Set a new station address.
+ */
+ for (i = 0; i < ETHERADDRL; i++)
+ VR_PUT8(vrp->acc_reg, VR_ETHERADDR + i, ea[i]);
+
+ mutex_exit(&vrp->intrlock);
+ mutex_exit(&vrp->oplock);
+ return (0);
+}
+
+/*
+ * Configure the ethernet link according to param and chip.mii.
+ */
+static void
+vr_link_init(vr_t *vrp)
+{
+ ASSERT(mutex_owned(&vrp->oplock));
+ if ((vrp->chip.mii.control & MII_CONTROL_ANE) != 0) {
+ /*
+ * If we do autoneg, ensure restart autoneg is ON.
+ */
+ vrp->chip.mii.control |= MII_CONTROL_RSAN;
+
+ /*
+ * The advertisements are prepared by param_init.
+ */
+ vr_phy_write(vrp, MII_AN_ADVERT, vrp->chip.mii.anadv);
+ } else {
+ /*
+ * If we don't autoneg, we need speed, duplex and flowcontrol
+ * to configure the link. However, dladm doesn't allow changes
+ * to speed and duplex (readonly). The way this is solved
+ * (ahem) is to select the highest enabled combination
+ * Speed and duplex should be r/w when autoneg is off.
+ */
+ if ((vrp->param.anadv_en &
+ MII_ABILITY_100BASE_TX_FD) != 0) {
+ vrp->chip.mii.control |= MII_CONTROL_100MB;
+ vrp->chip.mii.control |= MII_CONTROL_FDUPLEX;
+ } else if ((vrp->param.anadv_en &
+ MII_ABILITY_100BASE_TX) != 0) {
+ vrp->chip.mii.control |= MII_CONTROL_100MB;
+ vrp->chip.mii.control &= ~MII_CONTROL_FDUPLEX;
+ } else if ((vrp->param.anadv_en &
+ MII_ABILITY_10BASE_T_FD) != 0) {
+ vrp->chip.mii.control |= MII_CONTROL_FDUPLEX;
+ vrp->chip.mii.control &= ~MII_CONTROL_100MB;
+ } else {
+ vrp->chip.mii.control &= ~MII_CONTROL_100MB;
+ vrp->chip.mii.control &= ~MII_CONTROL_FDUPLEX;
+ }
+ }
+ /*
+ * Write the control register.
+ */
+ vr_phy_write(vrp, MII_CONTROL, vrp->chip.mii.control);
+
+ /*
+ * With autoneg off we cannot rely on the link_change interrupt for
+ * for getting the status into the driver.
+ */
+ if ((vrp->chip.mii.control & MII_CONTROL_ANE) == 0) {
+ vr_link_state(vrp);
+ mac_link_update(vrp->machdl,
+ (link_state_t)vrp->chip.link.state);
+ }
+}
+
+/*
+ * Get link state in the driver and configure the MAC accordingly.
+ */
+static void
+vr_link_state(vr_t *vrp)
+{
+ uint16_t mask;
+
+ ASSERT(mutex_owned(&vrp->oplock));
+
+ vr_phy_read(vrp, MII_STATUS, &vrp->chip.mii.status);
+ vr_phy_read(vrp, MII_CONTROL, &vrp->chip.mii.control);
+ vr_phy_read(vrp, MII_AN_ADVERT, &vrp->chip.mii.anadv);
+ vr_phy_read(vrp, MII_AN_LPABLE, &vrp->chip.mii.lpable);
+ vr_phy_read(vrp, MII_AN_EXPANSION, &vrp->chip.mii.anexp);
+
+ /*
+ * If we did autongeg, deduce the link type/speed by selecting the
+ * highest common denominator.
+ */
+ if ((vrp->chip.mii.control & MII_CONTROL_ANE) != 0) {
+ mask = vrp->chip.mii.anadv & vrp->chip.mii.lpable;
+ if ((mask & MII_ABILITY_100BASE_TX_FD) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
+ vrp->chip.link.mau = VR_MAU_100X;
+ } else if ((mask & MII_ABILITY_100BASE_T4) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
+ vrp->chip.link.mau = VR_MAU_100T4;
+ } else if ((mask & MII_ABILITY_100BASE_TX) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
+ vrp->chip.link.mau = VR_MAU_100X;
+ } else if ((mask & MII_ABILITY_10BASE_T_FD) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
+ vrp->chip.link.mau = VR_MAU_10;
+ } else if ((mask & MII_ABILITY_10BASE_T) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
+ vrp->chip.link.mau = VR_MAU_10;
+ } else {
+ vrp->chip.link.speed = VR_LINK_SPEED_UNKNOWN;
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_UNKNOWN;
+ vrp->chip.link.mau = VR_MAU_UNKNOWN;
+ }
+
+ /*
+ * Did we negotiate pause?
+ */
+ if ((mask & MII_AN_ADVERT_FCS) != 0 &&
+ vrp->chip.link.duplex == VR_LINK_DUPLEX_FULL)
+ vrp->chip.link.flowctrl = VR_PAUSE_BIDIRECTIONAL;
+ else
+ vrp->chip.link.flowctrl = VR_PAUSE_NONE;
+
+ /*
+ * Did either one detect a AN fault?
+ */
+ if ((vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0)
+ vr_log(vrp, CE_WARN,
+ "AN remote fault reported by LP.");
+
+ if ((vrp->chip.mii.lpable & MII_AN_ADVERT_REMFAULT) != 0)
+ vr_log(vrp, CE_WARN, "AN remote fault caused for LP.");
+ } else {
+ /*
+ * We didn't autoneg
+ * The link type is defined by the control register.
+ */
+ if ((vrp->chip.mii.control & MII_CONTROL_100MB) != 0) {
+ vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
+ vrp->chip.link.mau = VR_MAU_100X;
+ } else {
+ vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
+ vrp->chip.link.mau = VR_MAU_10;
+ }
+
+ if ((vrp->chip.mii.control & MII_CONTROL_FDUPLEX) != 0)
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
+ else {
+ vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
+ /*
+ * No pause on HDX links.
+ */
+ vrp->chip.link.flowctrl = VR_PAUSE_NONE;
+ }
+ }
+
+ /*
+ * Set the duplex mode on the MAC according to that of the PHY.
+ */
+ if (vrp->chip.link.duplex == VR_LINK_DUPLEX_FULL) {
+ VR_SETBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_MACFULLDUPLEX);
+ /*
+ * Enable packet queueing on FDX links.
+ */
+ if ((vrp->chip.info.bugs & VR_BUG_NO_TXQUEUEING) == 0)
+ VR_CLRBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_QPKTDIS);
+ } else {
+ VR_CLRBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_MACFULLDUPLEX);
+ /*
+ * Disable packet queueing on HDX links. With queueing enabled,
+ * this MAC get's lost after a TX abort (too many colisions).
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_QPKTDIS);
+ }
+
+ /*
+ * Set pause options on the MAC.
+ */
+ if (vrp->chip.link.flowctrl == VR_PAUSE_BIDIRECTIONAL) {
+ /*
+ * All of our MAC's can receive pause frames.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_MISC0, VR_MISC0_FDXRFEN);
+
+ /*
+ * VT6105 and above can transmit pause frames.
+ */
+ if ((vrp->chip.info.features & VR_FEATURE_TX_PAUSE_CAP) != 0) {
+ /*
+ * Set the number of available receive descriptors
+ * Non-zero values written to this register are added
+ * to the register's contents. Careful: Writing zero
+ * clears the register and thus causes a (long) pause
+ * request.
+ */
+ VR_PUT8(vrp->acc_reg, VR_FCR0_RXBUFCOUNT,
+ MIN(vrp->rx.ndesc, 0xFF) -
+ VR_GET8(vrp->acc_reg,
+ VR_FCR0_RXBUFCOUNT));
+
+ /*
+ * Request pause when we have 4 descs left.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_FCR1,
+ VR_FCR1_PAUSEONBITS, VR_FCR1_PAUSEON_04);
+
+ /*
+ * Cancel the pause when there are 24 descriptors again.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_FCR1,
+ VR_FCR1_PAUSEOFFBITS, VR_FCR1_PAUSEOFF_24);
+
+ /*
+ * Request a pause of FFFF bit-times. This long pause
+ * is cancelled when the high watermark is reached.
+ */
+ VR_PUT16(vrp->acc_reg, VR_FCR2_PAUSE, 0xFFFF);
+
+ /*
+ * Enable flow control on the MAC.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_MISC0, VR_MISC0_FDXTFEN);
+ VR_SETBIT8(vrp->acc_reg, VR_FCR1, VR_FCR1_FD_RX_EN |
+ VR_FCR1_FD_TX_EN | VR_FCR1_XONXOFF_EN);
+ }
+ } else {
+ /*
+ * Turn flow control OFF.
+ */
+ VR_CLRBIT8(vrp->acc_reg,
+ VR_MISC0, VR_MISC0_FDXRFEN | VR_MISC0_FDXTFEN);
+ if ((vrp->chip.info.features & VR_FEATURE_TX_PAUSE_CAP) != 0) {
+ VR_CLRBIT8(vrp->acc_reg, VR_FCR1,
+ VR_FCR1_FD_RX_EN | VR_FCR1_FD_TX_EN |
+ VR_FCR1_XONXOFF_EN);
+ }
+ }
+
+ /*
+ * Set link state.
+ */
+ if ((vrp->chip.mii.status & MII_STATUS_LINKUP) != 0)
+ vrp->chip.link.state = VR_LINK_STATE_UP;
+ else
+ vrp->chip.link.state = VR_LINK_STATE_DOWN;
+}
+
+/*
+ * The PHY is automatically polled by the MAC once per 1024 MD clock cycles
+ * MD is clocked once per 960ns so polling happens about every 1M ns, some
+ * 1000 times per second
+ * This polling process is required for the functionality of the link change
+ * interrupt. Polling process must be disabled in order to access PHY registers
+ * using MDIO
+ *
+ * Turn off PHY polling so that the PHY registers can be accessed.
+ */
+static void
+vr_phy_autopoll_disable(vr_t *vrp)
+{
+ uint32_t time;
+ uint8_t miicmd, miiaddr;
+
+ /*
+ * Special procedure to stop the autopolling.
+ */
+ if ((vrp->chip.info.bugs & VR_BUG_MIIPOLLSTOP) != 0) {
+ /*
+ * If polling is enabled.
+ */
+ miicmd = VR_GET8(vrp->acc_reg, VR_MIICMD);
+ if ((miicmd & VR_MIICMD_MD_AUTO) != 0) {
+ /*
+ * Wait for the end of a cycle (mdone set).
+ */
+ time = 0;
+ do {
+ drv_usecwait(10);
+ if (time >= VR_MMI_WAITMAX) {
+ vr_log(vrp, CE_WARN,
+ "Timeout in "
+ "disable MII polling");
+ break;
+ }
+ time += VR_MMI_WAITINCR;
+ miiaddr = VR_GET8(vrp->acc_reg, VR_MIIADDR);
+ } while ((miiaddr & VR_MIIADDR_MDONE) == 0);
+ }
+ /*
+ * Once paused, we can disable autopolling.
+ */
+ VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
+ } else {
+ /*
+ * Turn off MII polling.
+ */
+ VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
+
+ /*
+ * Wait for MIDLE in MII address register.
+ */
+ time = 0;
+ do {
+ drv_usecwait(VR_MMI_WAITINCR);
+ if (time >= VR_MMI_WAITMAX) {
+ vr_log(vrp, CE_WARN,
+ "Timeout in disable MII polling");
+ break;
+ }
+ time += VR_MMI_WAITINCR;
+ miiaddr = VR_GET8(vrp->acc_reg, VR_MIIADDR);
+ } while ((miiaddr & VR_MIIADDR_MIDLE) == 0);
+ }
+}
+
+/*
+ * Turn on PHY polling. PHY's registers cannot be accessed.
+ */
+static void
+vr_phy_autopoll_enable(vr_t *vrp)
+{
+ uint32_t time;
+
+ VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
+ VR_PUT8(vrp->acc_reg, VR_MIIADDR, MII_STATUS|VR_MIIADDR_MAUTO);
+ VR_PUT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_AUTO);
+
+ /*
+ * Wait for the polling process to finish.
+ */
+ time = 0;
+ do {
+ drv_usecwait(VR_MMI_WAITINCR);
+ if (time >= VR_MMI_WAITMAX) {
+ vr_log(vrp, CE_NOTE, "Timeout in enable MII polling");
+ break;
+ }
+ time += VR_MMI_WAITINCR;
+ } while ((VR_GET8(vrp->acc_reg, VR_MIIADDR) & VR_MIIADDR_MDONE) == 0);
+
+ /*
+ * Initiate a polling.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_MAUTO);
+}
+
+/*
+ * Read a register from the PHY using MDIO.
+ */
+static void
+vr_phy_read(vr_t *vrp, int offset, uint16_t *value)
+{
+ uint32_t time;
+
+ vr_phy_autopoll_disable(vrp);
+
+ /*
+ * Write the register number to the lower 5 bits of the MII address
+ * register.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_BITS, offset);
+
+ /*
+ * Write a READ command to the MII control register
+ * This bit will be cleared when the read is finished.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_READ);
+
+ /*
+ * Wait until the read is done.
+ */
+ time = 0;
+ do {
+ drv_usecwait(VR_MMI_WAITINCR);
+ if (time >= VR_MMI_WAITMAX) {
+ vr_log(vrp, CE_NOTE, "Timeout in MII read command");
+ break;
+ }
+ time += VR_MMI_WAITINCR;
+ } while ((VR_GET8(vrp->acc_reg, VR_MIICMD) & VR_MIICMD_MD_READ) != 0);
+
+ *value = VR_GET16(vrp->acc_reg, VR_MIIDATA);
+ vr_phy_autopoll_enable(vrp);
+}
+
+/*
+ * Write to a PHY's register.
+ */
+static void
+vr_phy_write(vr_t *vrp, int offset, uint16_t value)
+{
+ uint32_t time;
+
+ vr_phy_autopoll_disable(vrp);
+
+ /*
+ * Write the register number to the MII address register.
+ */
+ VR_SETBITS8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_BITS, offset);
+
+ /*
+ * Write the value to the data register.
+ */
+ VR_PUT16(vrp->acc_reg, VR_MIIDATA, value);
+
+ /*
+ * Issue the WRITE command to the command register.
+ * This bit will be cleared when the write is finished.
+ */
+ VR_SETBIT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_WRITE);
+
+ time = 0;
+ do {
+ drv_usecwait(VR_MMI_WAITINCR);
+ if (time >= VR_MMI_WAITMAX) {
+ vr_log(vrp, CE_NOTE, "Timeout in MII write command");
+ break;
+ }
+ time += VR_MMI_WAITINCR;
+ } while ((VR_GET8(vrp->acc_reg, VR_MIICMD) & VR_MIICMD_MD_WRITE) != 0);
+ vr_phy_autopoll_enable(vrp);
+}
+
+/*
+ * Initialize and install some private kstats.
+ */
+typedef struct {
+ char *name;
+ uchar_t type;
+} vr_kstat_t;
+
+static const vr_kstat_t vr_driver_stats [] = {
+ {"allocbfail", KSTAT_DATA_INT32},
+ {"intr_claimed", KSTAT_DATA_INT64},
+ {"intr_unclaimed", KSTAT_DATA_INT64},
+ {"linkchanges", KSTAT_DATA_INT64},
+ {"txnfree", KSTAT_DATA_INT32},
+ {"txstalls", KSTAT_DATA_INT32},
+ {"resets", KSTAT_DATA_INT32},
+ {"txreclaims", KSTAT_DATA_INT64},
+ {"txreclaim0", KSTAT_DATA_INT64},
+ {"cyclics", KSTAT_DATA_INT64},
+ {"txchecks", KSTAT_DATA_INT64},
+};
+
+static void
+vr_kstats_init(vr_t *vrp)
+{
+ kstat_t *ksp;
+ struct kstat_named *knp;
+ int i;
+ int nstats;
+
+ nstats = sizeof (vr_driver_stats) / sizeof (vr_kstat_t);
+
+ ksp = kstat_create(MODULENAME, ddi_get_instance(vrp->devinfo),
+ "driver", "net", KSTAT_TYPE_NAMED, nstats, 0);
+
+ if (ksp == NULL)
+ vr_log(vrp, CE_WARN, "kstat_create failed");
+
+ ksp->ks_update = vr_update_kstats;
+ ksp->ks_private = (void*) vrp;
+ knp = ksp->ks_data;
+
+ for (i = 0; i < nstats; i++, knp++) {
+ kstat_named_init(knp, vr_driver_stats[i].name,
+ vr_driver_stats[i].type);
+ }
+ kstat_install(ksp);
+ vrp->ksp = ksp;
+}
+
+static int
+vr_update_kstats(kstat_t *ksp, int access)
+{
+ vr_t *vrp;
+ struct kstat_named *knp;
+
+ vrp = (vr_t *)ksp->ks_private;
+ knp = ksp->ks_data;
+
+ if (access != KSTAT_READ)
+ return (EACCES);
+
+ (knp++)->value.ui32 = vrp->stats.allocbfail;
+ (knp++)->value.ui64 = vrp->stats.intr_claimed;
+ (knp++)->value.ui64 = vrp->stats.intr_unclaimed;
+ (knp++)->value.ui64 = vrp->stats.linkchanges;
+ (knp++)->value.ui32 = vrp->tx.nfree;
+ (knp++)->value.ui32 = vrp->stats.txstalls;
+ (knp++)->value.ui32 = vrp->stats.resets;
+ (knp++)->value.ui64 = vrp->stats.txreclaims;
+ (knp++)->value.ui64 = vrp->stats.txreclaim0;
+ (knp++)->value.ui64 = vrp->stats.cyclics;
+ (knp++)->value.ui64 = vrp->stats.txchecks;
+ return (0);
+}
+
+/*
+ * Remove 'private' kstats.
+ */
+static void
+vr_remove_kstats(vr_t *vrp)
+{
+ if (vrp->ksp != NULL)
+ kstat_delete(vrp->ksp);
+}
+
+/*
+ * Get a property of the device/driver
+ * Remarks:
+ * - pr_val is always an integer of size pr_valsize
+ * - ENABLED (EN) is what is configured via dladm
+ * - ADVERTISED (ADV) is ENABLED minus constraints, like PHY/MAC capabilities
+ * - DEFAULT are driver- and hardware defaults (DEFAULT is implemented as a
+ * flag in pr_flags instead of MAC_PROP_DEFAULT_)
+ * - perm is the permission printed on ndd -get /.. \?
+ */
+int
+vr_mac_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
+{
+ vr_t *vrp;
+ uint32_t err;
+ uint64_t val;
+
+ /* Since we have no private properties */
+ _NOTE(ARGUNUSED(pr_name))
+
+ err = 0;
+ vrp = (vr_t *)arg;
+ if ((pr_flags & MAC_PROP_DEFAULT) != 0) {
+ /*
+ * Defaults depend on the PHY/MAC's capabilities
+ * All defaults are read/write, otherwise reset-linkprop fails
+ * with enotsup ....
+ */
+ *perm = MAC_PROP_PERM_RW;
+ switch (pr_num) {
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_EN_1000FDX_CAP:
+ case MAC_PROP_ADV_1000HDX_CAP:
+ case MAC_PROP_EN_1000HDX_CAP:
+ val = 0;
+ break;
+
+ case MAC_PROP_ADV_100FDX_CAP:
+ case MAC_PROP_EN_100FDX_CAP:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_100_BASEX_FD) != 0;
+ break;
+
+ case MAC_PROP_ADV_100HDX_CAP:
+ case MAC_PROP_EN_100HDX_CAP:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_100_BASEX) != 0;
+ break;
+
+ case MAC_PROP_ADV_100T4_CAP:
+ case MAC_PROP_EN_100T4_CAP:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_100_BASE_T4) != 0;
+ break;
+
+ case MAC_PROP_ADV_10FDX_CAP:
+ case MAC_PROP_EN_10FDX_CAP:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_10_FD) != 0;
+ break;
+
+ case MAC_PROP_ADV_10HDX_CAP:
+ case MAC_PROP_EN_10HDX_CAP:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_10) != 0;
+ break;
+
+ case MAC_PROP_AUTONEG:
+ case MAC_PROP_EN_AUTONEG:
+ val = (vrp->chip.mii.status &
+ MII_STATUS_CANAUTONEG) != 0;
+ break;
+
+ case MAC_PROP_DUPLEX:
+ val = VR_LINK_DUPLEX_FULL;
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ val = VR_PAUSE_BIDIRECTIONAL;
+ break;
+
+ case MAC_PROP_MTU:
+ val = ETHERMTU;
+ break;
+
+ case MAC_PROP_SPEED:
+ val = 100 * 1000 * 1000;
+ break;
+
+ case MAC_PROP_STATUS:
+ val = VR_LINK_STATE_UP;
+ break;
+
+ default:
+ return (ENOTSUP);
+ }
+ } else {
+ switch (pr_num) {
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_ADV_1000HDX_CAP:
+ val = 0;
+ *perm = MAC_PROP_PERM_READ;
+ break;
+
+ case MAC_PROP_EN_1000FDX_CAP:
+ case MAC_PROP_EN_1000HDX_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = 0;
+ break;
+
+ case MAC_PROP_ADV_100FDX_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->chip.mii.anadv &
+ MII_ABILITY_100BASE_TX_FD) != 0;
+ break;
+
+ case MAC_PROP_ADV_100HDX_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->chip.mii.anadv &
+ MII_ABILITY_100BASE_TX) != 0;
+ break;
+
+ case MAC_PROP_ADV_100T4_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->chip.mii.anadv &
+ MII_ABILITY_100BASE_T4) != 0;
+ break;
+
+ case MAC_PROP_ADV_10FDX_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->chip.mii.anadv &
+ MII_ABILITY_10BASE_T_FD) != 0;
+ break;
+
+ case MAC_PROP_ADV_10HDX_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->chip.mii.anadv &
+ MII_ABILITY_10BASE_T) != 0;
+ break;
+
+ case MAC_PROP_AUTONEG:
+ *perm = MAC_PROP_PERM_RW;
+ val = (vrp->chip.mii.control &
+ MII_CONTROL_ANE) != 0;
+ break;
+
+ case MAC_PROP_DUPLEX:
+ /*
+ * Writability depends on autoneg.
+ */
+ if ((vrp->chip.mii.control &
+ MII_CONTROL_ANE) == 0)
+ *perm = MAC_PROP_PERM_RW;
+ else
+ *perm = MAC_PROP_PERM_READ;
+ val = vrp->chip.link.duplex;
+ break;
+
+ case MAC_PROP_EN_100FDX_CAP:
+ *perm = MAC_PROP_PERM_RW;
+ val = (vrp->param.anadv_en &
+ MII_ABILITY_100BASE_TX_FD) != 0;
+ break;
+
+ case MAC_PROP_EN_100HDX_CAP:
+ *perm = MAC_PROP_PERM_RW;
+ val = (vrp->param.anadv_en &
+ MII_ABILITY_100BASE_TX) != 0;
+ break;
+
+ case MAC_PROP_EN_100T4_CAP:
+ *perm = MAC_PROP_PERM_READ;
+ val = (vrp->param.anadv_en &
+ MII_ABILITY_100BASE_T4) != 0;
+ break;
+
+ case MAC_PROP_EN_10FDX_CAP:
+ *perm = MAC_PROP_PERM_RW;
+ val = (vrp->param.anadv_en &
+ MII_ABILITY_10BASE_T_FD) != 0;
+ break;
+
+ case MAC_PROP_EN_10HDX_CAP:
+ *perm = MAC_PROP_PERM_RW;
+ val = (vrp->param.anadv_en &
+ MII_ABILITY_10BASE_T) != 0;
+ break;
+
+ case MAC_PROP_EN_AUTONEG:
+ *perm = MAC_PROP_PERM_RW;
+ val = vrp->param.an_en == VR_LINK_AUTONEG_ON;
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ *perm = MAC_PROP_PERM_RW;
+ val = vrp->chip.link.flowctrl;
+ break;
+
+ case MAC_PROP_MTU:
+ *perm = MAC_PROP_PERM_RW;
+ val = vrp->param.mtu;
+ break;
+
+ case MAC_PROP_SPEED:
+ /*
+ * Writability depends on autoneg.
+ */
+ if ((vrp->chip.mii.control &
+ MII_CONTROL_ANE) == 0)
+ *perm = MAC_PROP_PERM_RW;
+ else
+ *perm = MAC_PROP_PERM_READ;
+ if (vrp->chip.link.speed ==
+ VR_LINK_SPEED_100MBS)
+ val = 100 * 1000 * 1000;
+ else if (vrp->chip.link.speed ==
+ VR_LINK_SPEED_10MBS)
+ val = 10 * 1000 * 1000;
+ else
+ val = 0;
+ break;
+
+ case MAC_PROP_STATUS:
+ val = vrp->chip.link.state;
+ break;
+
+ default:
+ err = ENOTSUP;
+ break;
+ }
+ }
+ if (err == 0 && pr_num != MAC_PROP_PRIVATE) {
+ if (pr_valsize == sizeof (uint64_t))
+ *(uint64_t *)pr_val = val;
+ else if (pr_valsize == sizeof (uint32_t))
+ *(uint32_t *)pr_val = val;
+ else if (pr_valsize == sizeof (uint16_t))
+ *(uint16_t *)pr_val = val;
+ else if (pr_valsize == sizeof (uint8_t))
+ *(uint8_t *)pr_val = val;
+ else
+ err = EINVAL;
+ }
+ return (err);
+}
+
+/*
+ * Set a property of the device.
+ */
+int
+vr_mac_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_valsize, const void *pr_val)
+{
+ vr_t *vrp;
+ uint32_t err;
+ uint64_t val;
+
+ /* Since we have no private properties */
+ _NOTE(ARGUNUSED(pr_name))
+
+ err = 0;
+ vrp = (vr_t *)arg;
+ mutex_enter(&vrp->oplock);
+
+ /*
+ * The current set of public property values are passed as integers
+ * Private properties are passed as strings in pr_val length pr_valsize.
+ */
+ if (pr_num != MAC_PROP_PRIVATE) {
+ if (pr_valsize == sizeof (uint64_t))
+ val = *(uint64_t *)pr_val;
+ else if (pr_valsize == sizeof (uint32_t))
+ val = *(uint32_t *)pr_val;
+ else if (pr_valsize == sizeof (uint16_t))
+ val = *(uint32_t *)pr_val;
+ else if (pr_valsize == sizeof (uint8_t))
+ val = *(uint8_t *)pr_val;
+ else {
+ mutex_exit(&vrp->oplock);
+ return (EINVAL);
+ }
+ }
+
+ switch (pr_num) {
+ case MAC_PROP_DUPLEX:
+ if ((vrp->chip.mii.control & MII_CONTROL_ANE) == 0) {
+ if (val == LINK_DUPLEX_FULL)
+ vrp->chip.mii.control |=
+ MII_CONTROL_FDUPLEX;
+ else if (val == LINK_DUPLEX_HALF)
+ vrp->chip.mii.control &=
+ ~MII_CONTROL_FDUPLEX;
+ else
+ err = EINVAL;
+ } else
+ err = EINVAL;
+ break;
+
+ case MAC_PROP_EN_100FDX_CAP:
+ if (val == 0)
+ vrp->param.anadv_en &=
+ ~MII_ABILITY_100BASE_TX_FD;
+ else
+ vrp->param.anadv_en |=
+ MII_ABILITY_100BASE_TX_FD;
+ break;
+
+ case MAC_PROP_EN_100HDX_CAP:
+ if (val == 0)
+ vrp->param.anadv_en &=
+ ~MII_ABILITY_100BASE_TX;
+ else
+ vrp->param.anadv_en |=
+ MII_ABILITY_100BASE_TX;
+ break;
+
+ case MAC_PROP_EN_100T4_CAP:
+ if (val == 0)
+ vrp->param.anadv_en &=
+ ~MII_ABILITY_100BASE_T4;
+ else
+ vrp->param.anadv_en |=
+ MII_ABILITY_100BASE_T4;
+ break;
+
+ case MAC_PROP_EN_10FDX_CAP:
+ if (val == 0)
+ vrp->param.anadv_en &=
+ ~MII_ABILITY_10BASE_T_FD;
+ else
+ vrp->param.anadv_en |=
+ MII_ABILITY_10BASE_T_FD;
+ break;
+
+ case MAC_PROP_EN_10HDX_CAP:
+ if (val == 0)
+ vrp->param.anadv_en &=
+ ~MII_ABILITY_10BASE_T;
+ else
+ vrp->param.anadv_en |=
+ MII_ABILITY_10BASE_T;
+ break;
+
+ case MAC_PROP_AUTONEG:
+ case MAC_PROP_EN_AUTONEG:
+ if (val == 0) {
+ vrp->param.an_en = VR_LINK_AUTONEG_OFF;
+ vrp->chip.mii.control &= ~MII_CONTROL_ANE;
+ } else {
+ vrp->param.an_en = VR_LINK_AUTONEG_ON;
+ if ((vrp->chip.mii.status &
+ MII_STATUS_CANAUTONEG) != 0)
+ vrp->chip.mii.control |=
+ MII_CONTROL_ANE;
+ else
+ err = EINVAL;
+ }
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ if (val == LINK_FLOWCTRL_NONE)
+ vrp->param.anadv_en &= ~MII_AN_ADVERT_FCS;
+ else if (val == LINK_FLOWCTRL_BI)
+ vrp->param.anadv_en |= MII_AN_ADVERT_FCS;
+ else
+ err = EINVAL;
+ break;
+
+ case MAC_PROP_MTU:
+ if (val >= ETHERMIN && val <= ETHERMTU)
+ vrp->param.mtu = (uint32_t)val;
+ else
+ err = EINVAL;
+ break;
+
+ case MAC_PROP_SPEED:
+ if (val == 10 * 1000 * 1000)
+ vrp->chip.link.speed =
+ VR_LINK_SPEED_10MBS;
+ else if (val == 100 * 1000 * 1000)
+ vrp->chip.link.speed =
+ VR_LINK_SPEED_100MBS;
+ else
+ err = EINVAL;
+ break;
+
+ default:
+ err = ENOTSUP;
+ break;
+ }
+ if (err == 0 && pr_num != MAC_PROP_PRIVATE) {
+ vrp->chip.mii.anadv = vrp->param.anadv_en &
+ (vrp->param.an_phymask & vrp->param.an_macmask);
+ vr_link_init(vrp);
+ }
+ mutex_exit(&vrp->oplock);
+ return (err);
+}
+
+
+/*
+ * Logging and debug functions.
+ */
+static struct {
+ kmutex_t mutex[1];
+ const char *ifname;
+ const char *fmt;
+ int level;
+} prtdata;
+
+static void
+vr_vprt(const char *fmt, va_list args)
+{
+ char buf[512];
+
+ ASSERT(mutex_owned(prtdata.mutex));
+ (void) vsnprintf(buf, sizeof (buf), fmt, args);
+ cmn_err(prtdata.level, prtdata.fmt, prtdata.ifname, buf);
+}
+
+static void
+vr_log(vr_t *vrp, int level, const char *fmt, ...)
+{
+ va_list args;
+
+ mutex_enter(prtdata.mutex);
+ prtdata.ifname = vrp->ifname;
+ prtdata.fmt = "!%s: %s";
+ prtdata.level = level;
+
+ va_start(args, fmt);
+ vr_vprt(fmt, args);
+ va_end(args);
+
+ mutex_exit(prtdata.mutex);
+}
+
+#if defined(DEBUG)
+static void
+vr_prt(const char *fmt, ...)
+{
+ va_list args;
+
+ ASSERT(mutex_owned(prtdata.mutex));
+
+ va_start(args, fmt);
+ vr_vprt(fmt, args);
+ va_end(args);
+
+ mutex_exit(prtdata.mutex);
+}
+
+void
+(*vr_debug())(const char *fmt, ...)
+{
+ mutex_enter(prtdata.mutex);
+ prtdata.ifname = MODULENAME;
+ prtdata.fmt = "^%s: %s\n";
+ prtdata.level = CE_CONT;
+
+ return (vr_prt);
+}
+#endif /* DEBUG */
+
+DDI_DEFINE_STREAM_OPS(vr_dev_ops, nulldev, nulldev, vr_attach, vr_detach,
+nodev, NULL, D_MP, NULL, vr_quiesce);
+
+static struct modldrv vr_modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ vr_ident, /* short description */
+ &vr_dev_ops /* driver specific ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&vr_modldrv, NULL
+};
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_init(void)
+{
+ int status;
+
+ mac_init_ops(&vr_dev_ops, MODULENAME);
+ status = mod_install(&modlinkage);
+ if (status == DDI_SUCCESS)
+ mutex_init(prtdata.mutex, NULL, MUTEX_DRIVER, NULL);
+ else
+ mac_fini_ops(&vr_dev_ops);
+ return (status);
+}
+
+int
+_fini(void)
+{
+ int status;
+
+ status = mod_remove(&modlinkage);
+ if (status == 0) {
+ mac_fini_ops(&vr_dev_ops);
+ mutex_destroy(prtdata.mutex);
+ }
+ return (status);
+}
diff --git a/usr/src/uts/common/io/vr/vr.h b/usr/src/uts/common/io/vr/vr.h
new file mode 100644
index 0000000000..f120895b8f
--- /dev/null
+++ b/usr/src/uts/common/io/vr/vr.h
@@ -0,0 +1,499 @@
+/*
+ * 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.
+ * Use is subject to license terms.
+ */
+
+#ifndef _VR_H
+#define _VR_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*
+ * Number of descriptor entries for each ring. The no. of descriptors is bound
+ * to 4K per ring (256 entries a 16 bytes).
+ */
+#define VR_TX_N_DESC 128
+#define VR_RX_N_DESC 256
+
+/*
+ * The number of TX interrupts to "schedule" on the ring.
+ */
+#define VR_TX_INTRS_RING 3
+
+/*
+ * The the periodic check interval of 2 seconds, in nano seconds
+ */
+#define VR_CHECK_INTERVAL (2000 * 1000 * 1000)
+
+/*
+ * The number of TX checks that must pass without progress before we decide
+ * to reset the adapter.
+ */
+#define VR_MAXTXCHECKS 12
+
+/*
+ * All possible interrupts with the unwanted commented.
+ */
+#define VR_ICR0_CFG (VR_ICR0_RX_DONE | \
+ VR_ICR0_TX_DONE | \
+ VR_ICR0_RX_ERR | \
+ VR_ICR0_TX_ERR | \
+ VR_ICR0_TX_BUF_UFLOW| \
+ VR_ICR0_RX_LINKERR | \
+ VR_ICR0_BUSERR | \
+ /* VR_ICR0_STATSMAX | */ \
+ /* VR_ICR0_RX_EARLY | */ \
+ VR_ICR0_TX_FIFO_UFLOW | \
+ VR_ICR0_RX_FIFO_OFLOW | \
+ VR_ICR0_RX_DROPPED | \
+ VR_ICR0_RX_NOBUF | \
+ VR_ICR0_TX_ABORT | \
+ VR_ICR0_LINKSTATUS | \
+ VR_ICR0_GENERAL)
+
+#define VR_ICR1_CFG (/* VR_ICR1_TIMER0 | */ \
+ /* VR_ICR1_TIMER1 | */ \
+ /* VR_ICR1_PHYEVENT | */ \
+ /* VR_ICR1_TDERR | */ \
+ /* VR_ICR1_SSRCI | */ \
+ /* VR_ICR1_UINTR_SET| */ \
+ /* VR_ICR1_UINTR_CLR| */ \
+ /* VR_ICR1_PWEI */)
+
+/*
+ * Our definitions of RX and TX errors.
+ */
+#define VR_ISR_TX_ERR_BITS (VR_ICR0_TX_ERR | \
+ VR_ICR0_TX_BUF_UFLOW | \
+ VR_ICR0_TX_FIFO_UFLOW | \
+ VR_ICR0_TX_ABORT)
+
+#define VR_ISR_RX_ERR_BITS (VR_ICR0_RX_ERR | \
+ VR_ICR0_RX_LINKERR | \
+ VR_ICR0_RX_FIFO_OFLOW | \
+ VR_ICR0_RX_DROPPED | \
+ VR_ICR0_RX_NOBUF)
+
+#define VR_ISR_SYS_ERR_BITS (VR_ICR0_BUSERR)
+
+#define VR_ISR_ERR_BITS (VR_ISR_TX_ERR_BITS | \
+ VR_ISR_RX_ERR_BITS | \
+ VR_ISR_SYS_ERR_BITS)
+#define VR_TX_MAX_INTR_DISTANCE \
+ (VR_TX_N_DESC / VR_TX_INTRS_RING)
+
+
+#define MODULENAME "vr" /* Our name */
+#define VR_SLOPSZ 2
+#define VR_MAX_PKTSZ (ETHERMAX + ETHERFCSL + VLAN_TAGSZ + VR_SLOPSZ)
+#define VR_DMABUFSZ (VR_MAX_PKTSZ)
+#define VR_MMI_WAITINCR (10)
+#define VR_MMI_WAITMAX (10000)
+#define VR_CAM_SZ (32)
+
+/*
+ * PCI identification for the Rhine's.
+ */
+#define VR_PCI_VIA_VENID 0x1106
+#define VR_PCI_DEVID_RHINE 0x3043
+#define VR_PCI_DEVID_RHINE_IIIM 0x3053
+#define VR_PCI_DEVID_RHINE_II2 0x3065
+#define VR_PCI_DEVID_RHINE_III 0x3106
+#define VR_PCI_DEVID_RHINE_II 0x6100
+
+#define VR_PCI_REVID_VT86C100A_E 0x04
+#define VR_PCI_REVID_VT6102_A 0x40
+#define VR_PCI_REVID_VT6102_C 0x42
+#define VR_PCI_REVID_VT6105_A0 0x80
+#define VR_PCI_REVID_VT6105_B0 0x83
+#define VR_PCI_REVID_VT6105_LOM 0x8A
+#define VR_PCI_REVID_VT6107_A0 0x8C
+#define VR_PCI_REVID_VT6107_A1 0x8D
+#define VR_PCI_REVID_VT6105M_A0 0x90
+#define VR_PCI_REVID_VT6105M_B1 0x94
+
+/*
+ * Feature bits for the different cards.
+ */
+#define VR_FEATURE_NONE (0)
+#define VR_FEATURE_RX_PAUSE_CAP (1 << 0) /* can receive pauses */
+#define VR_FEATURE_TX_PAUSE_CAP (1 << 1) /* can transmit pauses */
+#define VR_FEATURE_MRDLNMULTIPLE (1 << 2) /* can read mult cache lines */
+#define VR_FEATURE_TXCHKSUM (1 << 3) /* can do TX TCP checksum */
+#define VR_FEATURE_RXCHKSUM (1 << 4) /* can do RX TCP checksum */
+#define VR_FEATURE_CAMSUPPORT (1 << 5) /* has a CAM filter */
+#define VR_FEATURE_VLANTAGGING (1 << 6) /* can do VLAN tagging */
+#define VR_FEATURE_MIBCOUNTER (1 << 7) /* has a MIB counter */
+
+/*
+ * Bug bits for the different cards.
+ */
+#define VR_BUG_NONE (0)
+#define VR_BUG_TXALIGN (1 << 0) /* needs aligned TX */
+#define VR_BUG_NEEDMODE10T (1 << 1) /* chip needs mode10t secret */
+#define VR_BUG_NEEDMIION (1 << 2) /* chip needs miion secret */
+#define VR_BUG_NEEDMODE2PCEROPT (1 << 3) /* chip needs pceropt */
+#define VR_BUG_NO_TXQUEUEING (1 << 4) /* chip cannot queue tx */
+#define VR_BUG_NO_MEMIO (1 << 5) /* chip cannot memory space */
+#define VR_BUG_MIIPOLLSTOP (1 << 6) /* special to stop polling */
+
+#define VR_GET8(acc, p) \
+ ddi_get8((acc)->hdl, \
+ (uint8_t *)((void *)((acc)->addr + (p))))
+#define VR_GET16(acc, p) \
+ ddi_get16((acc)->hdl, \
+ (uint16_t *)((void *)((acc)->addr + (p))))
+#define VR_GET32(acc, p) \
+ ddi_get32((acc)->hdl, \
+ (uint32_t *)((void *)((acc)->addr + (p))))
+
+#define VR_PUT8(acc, p, v) \
+ ddi_put8((acc)->hdl, \
+ (uint8_t *)((void *)((acc)->addr + (p))), v)
+#define VR_PUT16(acc, p, v) \
+ ddi_put16((acc)->hdl, \
+ (uint16_t *)((void *)((acc)->addr + (p))), v)
+#define VR_PUT32(acc, p, v) \
+ ddi_put32((acc)->hdl, \
+ (uint32_t *)((void *)((acc)->addr + (p))), v)
+
+/*
+ * Clear bit b in register r.
+ */
+#define VR_CLRBIT8(acc, r, b) \
+ VR_PUT8(acc, r, VR_GET8(acc, r) & ~(b))
+#define VR_CLRBIT16(acc, r, b) \
+ VR_PUT16(acc, r, VR_GET16(acc, r) & ~(b))
+#define VR_CLRBIT32(acc, r, b) \
+ VR_PUT32(acc, r, VR_GET32(acc, r) & ~(b))
+
+/*
+ * Set bit b in register r.
+ */
+#define VR_SETBIT8(acc, r, b) \
+ VR_PUT8(acc, r, (VR_GET8(acc, r) | (b)))
+#define VR_SETBIT16(acc, r, b) \
+ VR_PUT16(acc, r, (VR_GET16(acc, r) | (b)))
+#define VR_SETBIT32(acc, r, b) \
+ VR_PUT32(acc, r, (VR_GET32(acc, r) | (b)))
+
+/*
+ * Set bits b in register r to value v.
+ */
+#define VR_SETBITS8(acc, r, b, v) \
+ VR_PUT8(acc, r, (VR_GET8(acc, r) & ~(b)) | (v))
+#define VR_SETBITS16(acc, r, b, v) \
+ VR_PUT16(acc, r, (VR_GET16(acc, r) & ~(b)) | (v))
+#define VR_SETBITS32(acc, r, b, v) \
+ VR_PUT32(acc, r, (VR_GET32(acc, r) & ~(b)) | (v))
+
+/*
+ * The descriptor as used by the MAC.
+ */
+typedef struct {
+ uint32_t stat0;
+ uint32_t stat1;
+ uint32_t data;
+ uint32_t next;
+} vr_chip_desc_t;
+
+/*
+ * A structure describing an DMA object.
+ */
+typedef struct data_dma {
+ ddi_dma_handle_t handle;
+ ddi_acc_handle_t acchdl;
+ uint32_t paddr;
+ char *buf;
+ size_t bufsz;
+} vr_data_dma_t;
+
+/*
+ * A descriptor as used by the host.
+ */
+typedef struct vr_desc {
+ vr_chip_desc_t *cdesc;
+ uint32_t paddr; /* paddr of cdesc */
+ uint32_t offset; /* offset to paddr */
+ struct vr_desc *next;
+ vr_data_dma_t dmabuf;
+} vr_desc_t;
+
+typedef struct vr_ring {
+ vr_desc_t *desc;
+ vr_chip_desc_t *cdesc;
+ uint32_t cdesc_paddr;
+ ddi_dma_handle_t handle;
+ ddi_acc_handle_t acchdl;
+} vr_ring_t;
+
+typedef struct {
+ kmutex_t lock;
+ uint32_t ndesc;
+ uint32_t nfree;
+ uint32_t stallticks;
+ uint32_t resched;
+ uint32_t intr_distance;
+ vr_desc_t *ring;
+ vr_desc_t *wp; /* write pointer */
+ vr_desc_t *cp; /* claim pointer */
+} vr_tx_t;
+
+typedef struct {
+ uint32_t ndesc;
+ vr_desc_t *ring;
+ vr_desc_t *rp; /* read pointer */
+} vr_rx_t;
+
+typedef enum {
+ VR_LINK_STATE_UNKNOWN = LINK_STATE_UNKNOWN,
+ VR_LINK_STATE_DOWN = LINK_STATE_DOWN,
+ VR_LINK_STATE_UP = LINK_STATE_UP
+} vr_link_state_t;
+
+typedef enum {
+ VR_LINK_SPEED_UNKNOWN,
+ VR_LINK_SPEED_10MBS,
+ VR_LINK_SPEED_100MBS
+} vr_link_speed_t;
+
+typedef enum {
+ VR_LINK_DUPLEX_UNKNOWN = LINK_DUPLEX_UNKNOWN,
+ VR_LINK_DUPLEX_FULL = LINK_DUPLEX_FULL,
+ VR_LINK_DUPLEX_HALF = LINK_DUPLEX_HALF
+} vr_link_duplex_t;
+
+typedef enum {
+ VR_LINK_AUTONEG_UNKNOWN,
+ VR_LINK_AUTONEG_OFF,
+ VR_LINK_AUTONEG_ON
+} vr_link_autoneg_t;
+
+/*
+ * Pause variations.
+ */
+typedef enum {
+ VR_PAUSE_UNKNOWN,
+ VR_PAUSE_NONE = LINK_FLOWCTRL_NONE,
+ VR_PAUSE_TRANSMIT = LINK_FLOWCTRL_TX,
+ VR_PAUSE_RECEIVE = LINK_FLOWCTRL_RX,
+ VR_PAUSE_BIDIRECTIONAL = LINK_FLOWCTRL_BI
+} vr_link_flowctrl_t;
+
+/*
+ * Type of medium attachement unit.
+ */
+typedef enum {
+ VR_MAU_UNKNOWN = XCVR_UNDEFINED,
+ VR_MAU_NONE = XCVR_NONE,
+ VR_MAU_10 = XCVR_10,
+ VR_MAU_100T4 = XCVR_100T4,
+ VR_MAU_100X = XCVR_100X,
+ VR_MAU_100T2 = XCVR_100T2,
+ VR_MAU_1000X = XCVR_1000X,
+ VR_MAU_1000T = XCVR_1000T
+} vr_mau_t;
+
+typedef struct {
+ vr_link_state_t state;
+ vr_link_speed_t speed;
+ vr_link_duplex_t duplex;
+ vr_link_flowctrl_t flowctrl;
+ vr_mau_t mau;
+} vr_link_t;
+
+typedef enum {
+ CHIPSTATE_UNKNOWN,
+ CHIPSTATE_INITIALIZED,
+ CHIPSTATE_RUNNING,
+ CHIPSTATE_STOPPED,
+ CHIPSTATE_SLEEPING,
+ CHIPSTATE_SUSPENDED,
+ CHIPSTATE_SUSPENDED_RUNNING,
+ CHIPSTATE_ERROR
+} vr_chip_state_t;
+
+typedef struct {
+ uint16_t control;
+ uint16_t status;
+ uint16_t identh;
+ uint16_t identl;
+ uint16_t anadv;
+ uint16_t lpable;
+ uint16_t anexp;
+} mii_t;
+
+/*
+ * A structure defining the various types of cards and their habits.
+ */
+typedef struct {
+ uint8_t revmin;
+ uint8_t revmax;
+ char name[128];
+ uint32_t bugs;
+ uint32_t features;
+} chip_info_t;
+
+/*
+ * A structure describing the card.
+ */
+typedef struct {
+ uint16_t vendor;
+ uint16_t device;
+ uint8_t revision;
+ vr_chip_state_t state;
+ mii_t mii;
+ vr_link_t link;
+ chip_info_t info;
+ uint32_t phyaddr;
+} vr_chip_t;
+
+/*
+ * Operational parameters.
+ */
+typedef struct {
+ uint16_t anadv_en;
+ uint16_t an_phymask;
+ uint16_t an_macmask;
+ vr_link_autoneg_t an_en;
+ uint32_t mtu;
+} vr_param_t;
+
+typedef enum {
+ VR_SUCCESS = 0,
+ VR_FAILURE = 1
+} vr_result_t;
+
+typedef struct {
+ uint64_t ether_stat_align_errors;
+ uint64_t ether_stat_carrier_errors;
+ uint64_t ether_stat_ex_collisions;
+ uint64_t ether_stat_fcs_errors;
+ uint64_t ether_stat_first_collisions;
+ uint64_t ether_stat_macrcv_errors;
+ uint64_t ether_stat_macxmt_errors;
+ uint64_t ether_stat_multi_collisions;
+ uint64_t ether_stat_toolong_errors;
+ uint64_t ether_stat_tooshort_errors;
+ uint64_t ether_stat_tx_late_collisions;
+ uint64_t ether_stat_defer_xmts;
+ uint64_t mac_stat_brdcstrcv;
+ uint64_t mac_stat_brdcstxmt;
+ uint64_t mac_stat_multixmt;
+ uint64_t mac_stat_collisions;
+ uint64_t mac_stat_ierrors;
+ uint64_t mac_stat_ipackets;
+ uint64_t mac_stat_multircv;
+ uint64_t mac_stat_norcvbuf;
+ uint64_t mac_stat_noxmtbuf;
+ uint64_t mac_stat_obytes;
+ uint64_t mac_stat_opackets;
+ uint64_t mac_stat_rbytes;
+ uint64_t mac_stat_underflows;
+ uint64_t mac_stat_overflows;
+ uint64_t cyclics;
+ uint64_t txchecks;
+ uint64_t intr_claimed;
+ uint64_t intr_unclaimed;
+ uint64_t linkchanges;
+ uint64_t txcpybytes;
+ uint64_t txmapbytes;
+ uint64_t rxcpybytes;
+ uint64_t rxmapbytes;
+ uint64_t txreclaim0;
+ uint64_t txreclaims;
+ uint32_t txstalls;
+ uint32_t resets;
+ uint32_t allocbfail;
+} vr_stats_t;
+
+/*
+ * Access attributes for the card.
+ */
+typedef struct {
+ ddi_acc_handle_t hdl;
+ caddr_t addr;
+ pci_regspec_t reg;
+} vr_acc_t;
+
+/*
+ * Instance state structure.
+ */
+typedef struct {
+ kmutex_t oplock;
+ dev_info_t *devinfo;
+ uint8_t vendor_ether_addr [ETHERADDRL];
+ char ifname[12];
+ mac_handle_t machdl;
+ ddi_intr_handle_t intr_hdl;
+ uint_t intr_pri;
+ kmutex_t intrlock;
+ vr_chip_t chip;
+ vr_ring_t txring;
+ vr_ring_t rxring;
+ vr_rx_t rx;
+ vr_tx_t tx;
+ ddi_periodic_t periodic_id;
+ int nsets;
+ vr_acc_t *regset;
+ vr_acc_t *acc_mem;
+ vr_acc_t *acc_io;
+ vr_acc_t *acc_cfg;
+ vr_acc_t *acc_reg;
+ vr_param_t param;
+ vr_stats_t stats;
+ struct kstat *ksp;
+ vr_param_t defaults;
+ uint32_t promisc;
+ uint32_t mhash0;
+ uint32_t mhash1;
+ uint32_t mcount;
+ uint32_t reset;
+} vr_t;
+
+/*
+ * Function prototypes.
+ */
+int vr_mac_getstat(void *arg, uint_t stat, uint64_t *val);
+int vr_mac_start(void *vrp);
+void vr_mac_stop(void *vrp);
+int vr_mac_set_promisc(void *vrp, boolean_t promiscflag);
+int vr_mac_set_multicast(void *vrp, boolean_t add,
+ const uint8_t *mca);
+int vr_mac_set_ether_addr(void *vrp,
+ const uint8_t *macaddr);
+mblk_t *vr_mac_tx_enqueue_list(void *p, mblk_t *mp);
+int vr_mac_getprop(void *arg, const char *pr_name,
+ mac_prop_id_t pr_num, uint_t pr_flags,
+ uint_t pr_valsize, void *pr_val, uint_t *perm);
+int vr_mac_setprop(void *arg, const char *pr_name,
+ mac_prop_id_t pr_num,
+ uint_t pr_valsize, const void *pr_val);
+uint_t vr_intr(caddr_t arg1, caddr_t arg2);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _VR_H */
diff --git a/usr/src/uts/common/io/vr/vr_impl.h b/usr/src/uts/common/io/vr/vr_impl.h
new file mode 100644
index 0000000000..a50b90aa08
--- /dev/null
+++ b/usr/src/uts/common/io/vr/vr_impl.h
@@ -0,0 +1,655 @@
+/*
+ * 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.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Register definitions for the VIA Rhine ethernet adapters
+ */
+#ifndef _VRREG_H
+#define _VRREG_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*
+ * Some definitions for the MII because miiregs doesn't have them
+ */
+#define MII_STATUS_100_BASE_T2_FD (1 << 10)
+#define MII_STATUS_100_BASE_T2 (1 << 9)
+#define MII_STATUS_CAPEXT (1 << 8)
+#define MII_ABILITY_ASMDIR (1 << 6)
+#define MII_EXTSTATUS 0x9
+#define MII_EXTSTATUS_1000BASE_X_FD 0x8000
+#define MII_EXTSTATUS_1000BASE_X 0x4000
+#define MII_EXTSTATUS_1000BASE_T_FD 0x2000
+#define MII_EXTSTATUS_1000BASE_T 0x1000
+
+/*
+ * MAC address
+ */
+#define VR_ETHERADDR 0x00
+
+/*
+ * Receive Configuration
+ * The thresholds denote the level in the FIFO before transmission
+ * to host memory starts.
+ */
+#define VR_RXCFG 0x06
+#define VR_RXCFG_ACCEPTERROR (1 << 0)
+#define VR_RXCFG_ACCEPTRUNT (1 << 1)
+#define VR_RXCFG_ACCEPTMULTI (1 << 2)
+#define VR_RXCFG_ACCEPTBROAD (1 << 3)
+#define VR_RXCFG_PROMISC (1 << 4)
+#define VR_RXCFG_FIFO_THRESHOLD_0 (1 << 5)
+#define VR_RXCFG_FIFO_THRESHOLD_1 (1 << 6)
+#define VR_RXCFG_FIFO_THRESHOLD_2 (1 << 7)
+#define VR_RXCFG_FIFO_THRESHOLD_BITS (VR_RXCFG_FIFO_THRESHOLD_0 | \
+ VR_RXCFG_FIFO_THRESHOLD_1 | \
+ VR_RXCFG_FIFO_THRESHOLD_2)
+#define VR_RXCFG_FIFO_THRESHOLD_64 (0)
+#define VR_RXCFG_FIFO_THRESHOLD_32 (VR_RXCFG_FIFO_THRESHOLD_0)
+#define VR_RXCFG_FIFO_THRESHOLD_128 (VR_RXCFG_FIFO_THRESHOLD_1)
+#define VR_RXCFG_FIFO_THRESHOLD_256 (VR_RXCFG_FIFO_THRESHOLD_0 | \
+ VR_RXCFG_FIFO_THRESHOLD_1)
+#define VR_RXCFG_FIFO_THRESHOLD_512 (VR_RXCFG_FIFO_THRESHOLD_2)
+#define VR_RXCFG_FIFO_THRESHOLD_768 (VR_RXCFG_FIFO_THRESHOLD_0 | \
+ VR_RXCFG_FIFO_THRESHOLD_2)
+#define VR_RXCFG_FIFO_THRESHOLD_1024 (VR_RXCFG_FIFO_THRESHOLD_2 | \
+ VR_RXCFG_FIFO_THRESHOLD_1)
+#define VR_RXCFG_FIFO_THRESHOLD_STFW (VR_RXCFG_FIFO_THRESHOLD_BITS)
+
+/*
+ * Transmit Configuration
+ * The transmission starts when the data in the FIFO reaches the threshold.
+ * Store and Forward means that a transmission starts when a complete frame
+ * is in the FIFO.
+ */
+#define VR_TXCFG 0x07
+#define VR_TXCFG_8021PQ_EN (1 << 0) /* VT6105M */
+#define VR_TXCFG_LOOPBACK_0 (1 << 1)
+#define VR_TXCFG_LOOPBACK_1 (2 << 2)
+#define VR_TXCFG_BACKOFF_NATIONAL (1 << 3) /* < VT6105M */
+#define VR_TXCFG_FIFO_THRESHOLD_0 (1 << 5)
+#define VR_TXCFG_FIFO_THRESHOLD_1 (1 << 6)
+#define VR_TXCFG_FIFO_THRESHOLD_2 (1 << 7)
+#define VR_TXCFG_FIFO_THRESHOLD_BITS (VR_TXCFG_FIFO_THRESHOLD_0 | \
+ VR_TXCFG_FIFO_THRESHOLD_1 | \
+ VR_TXCFG_FIFO_THRESHOLD_2)
+#define VR_TXCFG_FIFO_THRESHOLD_128 (0)
+#define VR_TXCFG_FIFO_THRESHOLD_256 (VR_TXCFG_FIFO_THRESHOLD_0)
+#define VR_TXCFG_FIFO_THRESHOLD_512 (VR_TXCFG_FIFO_THRESHOLD_1)
+#define VR_TXCFG_FIFO_THRESHOLD_1024 (VR_TXCFG_FIFO_THRESHOLD_0 | \
+ VR_TXCFG_FIFO_THRESHOLD_1)
+#define VR_TXCFG_FIFO_THRESHOLD_STFW (VR_TXCFG_FIFO_THRESHOLD_BITS)
+
+/*
+ * Chip control
+ */
+#define VR_CTRL0 0x08
+#define VR_CTRL0_RESERVED (1 << 0)
+#define VR_CTRL0_DMA_ENABLE (1 << 1)
+#define VR_CTRL0_DMA_STOP (1 << 2)
+#define VR_CTRL0_RX_DMA_ENABLE (1 << 3)
+#define VR_CTRL0_TX_DMA_ENABLE (1 << 4)
+#define VR_CTRL0_TXPOLL (1 << 5) /* < 6105M */
+#define VR_CTRL0_RXPOLL (1 << 6) /* < 6105M */
+
+#define VR_CTRL0_DMA_GO (VR_CTRL0_DMA_ENABLE | \
+ VR_CTRL0_RX_DMA_ENABLE | \
+ VR_CTRL0_TX_DMA_ENABLE | \
+ VR_CTRL0_TXPOLL)
+#define VR_CTRL1 0x09
+#define VR_CTRL1_RESERVED (1 << 0)
+#define VR_CTRL1_UNICAST_EN (1 << 1)
+#define VR_CTRL1_MACFULLDUPLEX (1 << 2)
+#define VR_CTRL1_NOAUTOPOLL (1 << 3)
+#define VR_CTRL1_RESERVED2 (1 << 4)
+#define VR_CTRL1_TXPOLL (1 << 5) /* VT6105M */
+#define VR_CTRL1_RXPOLL (1 << 6) /* VT6105M */
+#define VR_CTRL1_RESET (1 << 7)
+
+#define VR_T_XQNWAKE 0x0a /* VT6105M */
+
+/*
+ * Interrupt Status
+ * This register reflects NIC status
+ * The host reads it to determine the cause of the interrupt
+ * This register must be cleared after power-up
+ */
+#define VR_ISR0 0x0C
+#define VR_ISR0_RX_DONE (1 << 0)
+#define VR_ISR0_TX_DONE (1 << 1)
+#define VR_ISR0_RX_ERR (1 << 2)
+#define VR_ISR0_TX_ERR (1 << 3)
+#define VR_ISR0_TX_BUF_UFLOW (1 << 4)
+#define VR_ISR0_RX_LINKERR (1 << 5)
+#define VR_ISR0_BUSERR (1 << 6)
+#define VR_ISR0_STATSMAX (1 << 7)
+#define VR_ISR0_RX_EARLY (1 << 8)
+#define VR_ISR0_TX_FIFO_UFLOW (1 << 9)
+#define VR_ISR0_RX_FIFO_OFLOW (1 << 10)
+#define VR_ISR0_RX_DROPPED (1 << 11)
+#define VR_ISR0_RX_NOBUF (1 << 12)
+#define VR_ISR0_TX_ABORT (1 << 13)
+#define VR_ISR0_LINKSTATUS (1 << 14)
+#define VR_ISR0_GENERAL (1 << 15)
+
+/*
+ * Interrupt Configuration
+ * All bits in this register correspond to the bits in the Interrupt Status
+ * register Setting individual bits will enable the corresponding interrupt
+ * This register defaults to all zeros on power up
+ */
+#define VR_ICR0 0x0E
+#define VR_ICR0_RX_DONE VR_ISR0_RX_DONE
+#define VR_ICR0_TX_DONE VR_ISR0_TX_DONE
+#define VR_ICR0_RX_ERR VR_ISR0_RX_ERR
+#define VR_ICR0_TX_ERR VR_ISR0_TX_ERR
+#define VR_ICR0_TX_BUF_UFLOW VR_ISR0_TX_BUF_UFLOW
+#define VR_ICR0_RX_LINKERR VR_ISR0_RX_LINKERR
+#define VR_ICR0_BUSERR VR_ISR0_BUSERR
+#define VR_ICR0_STATSMAX VR_ISR0_STATSMAX
+#define VR_ICR0_RX_EARLY VR_ISR0_RX_EARLY
+#define VR_ICR0_TX_FIFO_UFLOW VR_ISR0_TX_FIFO_UFLOW
+#define VR_ICR0_RX_FIFO_OFLOW VR_ISR0_RX_FIFO_OFLOW
+#define VR_ICR0_RX_DROPPED VR_ISR0_RX_DROPPED
+#define VR_ICR0_RX_NOBUF VR_ISR0_RX_NOBUF
+#define VR_ICR0_TX_ABORT VR_ISR0_TX_ABORT
+#define VR_ICR0_LINKSTATUS VR_ISR0_LINKSTATUS
+#define VR_ICR0_GENERAL VR_ISR0_GENERAL
+
+/*
+ * Mulicast address registers (MAR), 8 bytes
+ */
+#define VR_MAR0 0x10 /* - 0x13 */
+#define VR_MAR1 0x14 /* - 0x17 */
+
+/*
+ * VT6105M has a multicast/vlan filter and the hash bits are also used as
+ * CAM data port
+ */
+#define VR_MCAM0 0x10 /* VT6105M */
+#define VR_MCAM1 0x11
+#define VR_MCAM2 0x12
+#define VR_MCAM3 0x13
+#define VR_MCAM4 0x14
+#define VR_MCAM5 0x15
+#define VR_VCAM0 0x16
+#define VR_VCAM1 0x17
+
+/*
+ * Start addresses of receive and transmit ring
+ */
+#define VR_RXADDR 0x18 /* - 0x1B */
+#define VR_TXADDR 0x1C /* - 0x1F */
+
+/*
+ * VT6105M has 8 TX queues
+ */
+#define VR_TX7_ADDR 0x1C
+#define VR_TX6_ADDR 0x20
+#define VR_TX5_ADDR 0x24
+#define VR_TX4_ADDR 0x28
+#define VR_TX3_ADDR 0x2C
+#define VR_TX2_ADDR 0x30
+#define VR_TX1_ADDR 0x34
+#define VR_TX0_ADDR 0x38
+
+/*
+ * Current and receive- and transmit descriptors.
+ * These are listed in the VT6102 manual but not in the VT6105.
+ */
+#define VR_RXCUR_DES0 0x20 /* - 0x23 */
+#define VR_RXCUR_DES1 0x24 /* - 0x27 */
+#define VR_RXCUR_DES2 0x28 /* - 0x2B */
+#define VR_RXCUR_DES3 0x2C /* - 0x2F */
+
+/* VIA secrets here */
+
+#define VR_INTRLINE 0x3c
+#define VR_INTRPIN 0x3d
+
+/* VIA secrets here */
+
+#define VR_TXCUR_DES0 0x40 /* - 0x43 */
+#define VR_TXCUR_DES1 0x44 /* - 0x47 */
+#define VR_TXCUR_DES2 0x48 /* - 0x4B */
+#define VR_TXCUR_DES3 0x4C /* - 0x4F */
+
+#define VR_MODE0 0x50
+#define VR_MODE0_QPKTDS 0x80
+
+#define VR_MODE1 0x51
+#define VR_FIFOTST 0x51
+
+/*
+ * These are not in the datasheet but used in the 'fet' driver
+ */
+#define VR_MODE2 0x52
+#define VR_MODE2_PCEROPT 0x80 /* VT6102 only */
+#define VR_MODE2_DISABT 0x40
+#define VR_MODE2_MRDPL 0x08 /* VT6107A1 and above */
+#define VR_MODE2_MODE10T 0x02
+
+#define VR_MODE3 0x53
+#define VR_MODE3_XONOPT 0x80
+#define VR_MODE3_TPACEN 0x40
+#define VR_MODE3_BACKOPT 0x20
+#define VR_MODE3_DLTSEL 0x10
+#define VR_MODE3_MIIDMY 0x08
+#define VR_MODE3_MIION 0x04
+
+#define VR_PCI_DELAY_TIMER 0x54
+#define VR_FIFOCMD 0x56
+#define VR_FIFOSTA 0x57
+
+/* VIA secrets here */
+
+/*
+ * MII Configuration
+ */
+#define VR_MIIPHYADDR 0x6C
+#define VR_MIIPHYADDR_ADDR0 (1 << 0)
+#define VR_MIIPHYADDR_ADDR1 (1 << 1)
+#define VR_MIIPHYADDR_ADDR2 (1 << 2)
+#define VR_MIIPHYADDR_ADDR3 (1 << 3)
+#define VR_MIIPHYADDR_ADDR4 (1 << 4)
+#define VR_MIIPHYADDR_ADDRBITS (VR_MIIPHYADDR_ADDR0 | \
+ VR_MIIPHYADDR_ADDR1 | \
+ VR_MIIPHYADDR_ADDR2 | \
+ VR_MIIPHYADDR_ADDR3 | \
+ VR_MIIPHYADDR_ADDR4)
+#define VR_MIIPHYADDR_MD_CLOCK_FAST (1 << 5)
+#define VR_MIIPHYADDR_POLLBITS ((1 << 7) | (1 << 6))
+#define VR_MIIPHYADDR_POLL1024 ((0 << 7) | (0 << 6))
+#define VR_MIIPHYADDR_POLL512 ((0 << 7) | (1 << 6))
+#define VR_MIIPHYADDR_POLL128 ((1 << 7) | (0 << 6))
+#define VR_MIIPHYADDR_POLL64 ((1 << 7) | (1 << 6))
+
+/*
+ * MII status
+ */
+#define VR_MIISR 0x6D
+#define VR_MIISR_SPEED (1 << 0) /* VT6102 and VT6105 */
+#define VR_MIISR_LINKFAIL (1 << 1) /* VT6102 and VT6105 */
+#define VR_MIISR_DUPLEX (1 << 2) /* VT6105 only */
+#define VR_MIISR_PHYERR (1 << 3) /* VT6102 and VT6105 */
+#define VR_MIISR_PHYOPT (1 << 4) /* VT6102 only */
+#define VR_MIISR_NWAYLINKOK (1 << 4) /* VT6105 only */
+#define VR_MIISR_NWAYPAUSE (1 << 5) /* VT6105M */
+#define VR_MIISR_NWAYASMPAUSE (1 << 6) /* VT6105M */
+#define VR_MIISR_PHYRST (1 << 7)
+
+/*
+ * Bus control
+ */
+#define VR_BCR0 0x6E /* receive */
+#define VR_BCR0_DMA0 (1 << 0)
+#define VR_BCR0_DMA1 (1 << 1)
+#define VR_BCR0_DMA2 (1 << 2)
+#define VR_BCR0_DMABITS (VR_BCR0_DMA0|VR_BCR0_DMA1 | \
+ VR_BCR0_DMA2)
+#define VR_BCR0_DMA32 (0)
+#define VR_BCR0_DMA64 (VR_BCR0_DMA0)
+#define VR_BCR0_DMA128 (VR_BCR0_DMA1)
+#define VR_BCR0_DMA256 (VR_BCR0_DMA0|VR_BCR0_DMA1)
+#define VR_BCR0_DMA512 (VR_BCR0_DMA2)
+#define VR_BCR0_DMA1024 (VR_BCR0_DMA0|VR_BCR0_DMA2)
+#define VR_BCR0_DMASTFW (VR_BCR0_DMABITS)
+#define VR_BCR0_RX_FIFO_THRESHOLD_0 (1 << 3)
+#define VR_BCR0_RX_FIFO_THRESHOLD_1 (1 << 4)
+#define VR_BCR0_RX_FIFO_THRESHOLD_2 (1 << 5)
+#define VR_BCR0_RX_FIFO_THRESHOLD_BITS (VR_BCR0_RX_FIFO_THRESHOLD_0 | \
+ VR_BCR0_RX_FIFO_THRESHOLD_1 | \
+ VR_BCR0_RX_FIFO_THRESHOLD_2)
+#define VR_BCR0_RX_FIFO_THRESHOLD_64 (0)
+#define VR_BCR0_RX_FIFO_THRESHOLD_32 (VR_BCR0_RX_FIFO_THRESHOLD_0)
+#define VR_BCR0_RX_FIFO_THRESHOLD_128 (VR_BCR0_RX_FIFO_THRESHOLD_1)
+#define VR_BCR0_RX_FIFO_THRESHOLD_256 (VR_BCR0_RX_FIFO_THRESHOLD_0 | \
+ VR_BCR0_RX_FIFO_THRESHOLD_1)
+#define VR_BCR0_RX_FIFO_THRESHOLD_512 (VR_BCR0_RX_FIFO_THRESHOLD_2)
+#define VR_BCR0_RX_FIFO_THRESHOLD_768 (VR_BCR0_RX_FIFO_THRESHOLD_0 | \
+ VR_BCR0_RX_FIFO_THRESHOLD_2)
+#define VR_BCR0_RX_FIFO_THRESHOLD_1024 (VR_BCR0_RX_FIFO_THRESHOLD_1 | \
+ VR_BCR0_RX_FIFO_THRESHOLD_2)
+#define VR_BCR0_RX_FIFO_THRESHOLD_STFW (VR_BCR0_RX_FIFO_THRESHOLD_BITS)
+#define VR_BCR0_LEDCR (1 << 6)
+#define VR_BCR0_MSEL (1 << 7)
+
+#define VR_BCR1 0x6F /* transmit */
+#define VR_BCR1_POLLT_0 (1 << 0)
+#define VR_BCR1_POLLT_1 (1 << 1)
+#define VR_BCR1_POLLT_2 (1 << 2)
+#define VR_BCR1_TX_FIFO_THRESHOLD_0 (1 << 3)
+#define VR_BCR1_TX_FIFO_THRESHOLD_1 (1 << 4)
+#define VR_BCR1_TX_FIFO_THRESHOLD_2 (1 << 5)
+#define VR_BCR1_TX_FIFO_THRESHOLD_BITS (VR_BCR1_TX_FIFO_THRESHOLD_0 | \
+ VR_BCR1_TX_FIFO_THRESHOLD_1 | \
+ VR_BCR1_TX_FIFO_THRESHOLD_2)
+#define VR_BCR1_TX_FIFO_THRESHOLD_128 (0)
+#define VR_BCR1_TX_FIFO_THRESHOLD_256 (VR_BCR1_TX_FIFO_THRESHOLD_0)
+#define VR_BCR1_TX_FIFO_THRESHOLD_512 (VR_BCR1_TX_FIFO_THRESHOLD_1)
+#define VR_BCR1_TX_FIFO_THRESHOLD_1024 (VR_BCR1_TX_FIFO_THRESHOLD_0 | \
+ VR_BCR1_FIFO_THRESHOLD_1)
+#define VR_BCR1_TX_FIFO_THRESHOLD_STFW (VR_BCR1_FIFO_THRESHOLD_BITS)
+#define VR_BCR1_TXQPRIO (1 << 6) /* VT6105M */
+#define VR_BCR1_VLANFILTER (1 << 7) /* VT6105M */
+
+/*
+ * MII Configuration
+ */
+#define VR_MIICMD 0x70
+#define VR_MIICMD_MD_CLOCK (1 << 0)
+#define VR_MIICMD_MD_CLOCK_READ (1 << 1)
+#define VR_MIICMD_MD_CLOCK_WRITE (1 << 2)
+#define VR_MIICMD_MD_OUT (1 << 3)
+#define VR_MIICMD_MD_MODE_AUTO (1 << 4)
+#define VR_MIICMD_MD_WRITE (1 << 5)
+#define VR_MIICMD_MD_READ (1 << 6)
+#define VR_MIICMD_MD_AUTO (1 << 7)
+
+#define VR_MIIADDR 0x71
+#define VR_MIIADDR_MAD0 (1 << 0)
+#define VR_MIIADDR_MAD1 (1 << 1)
+#define VR_MIIADDR_MAD2 (1 << 2)
+#define VR_MIIADDR_MAD3 (1 << 3)
+#define VR_MIIADDR_MAD4 (1 << 4)
+#define VR_MIIADDR_BITS (VR_MIIADDR_MAD0 | \
+ VR_MIIADDR_MAD1 | \
+ VR_MIIADDR_MAD2 | \
+ VR_MIIADDR_MAD3 | \
+ VR_MIIADDR_MAD4)
+#define VR_MIIADDR_MDONE (1 << 5)
+#define VR_MIIADDR_MAUTO (1 << 6)
+#define VR_MIIADDR_MIDLE (1 << 7)
+
+#define VR_MIIDATA 0x72
+#define VR_MIIDATA_1 0x72
+#define VR_MIIDATA_2 0x73
+
+/*
+ * EEPROM Config / Status
+ */
+#define VR_PROMCTL 0x74
+#define VR_PROMCTL_DATAOUT (1 << 0)
+#define VR_PROMCTL_DATAIN (1 << 1)
+#define VR_PROMCTL_CLOCK (1 << 2)
+#define VR_PROMCTL_CHIPSELECT (1 << 3)
+#define VR_PROMCTL_DIRPROG (1 << 4)
+#define VR_PROMCTL_RELOAD (1 << 5)
+#define VR_PROMCTL_PROGRAM (1 << 6)
+#define VR_PROMCTL_PRGSTATUS (1 << 7)
+
+/*
+ * Chip Configuration A
+ */
+#define VR_CFGA 0x78
+#define VR_CFGA_PRE_ACPI_WAKEUP (1 << 0) /* VT6105M */
+#define VR_CFGA_WAKEUP_PANIC (1 << 1) /* VT6105M */
+#define VR_CFGA_VLANTAG_INCRC (1 << 5) /* VT6105M */
+#define VR_CFGA_MIIOPT (1 << 6)
+#define VR_CFGA_EELOAD (1 << 7)
+
+/*
+ * Chip Configuration B
+ */
+#define VR_CFGB 0x79
+#define VR_CFGB_LATENCYTIMER (1 << 0)
+#define VR_CFGB_WWAIT (1 << 1)
+#define VR_CFGB_RWAIT (1 << 2)
+#define VR_CFGB_RXARBIT (1 << 3)
+#define VR_CFGB_TXARBIT (1 << 4)
+#define VR_CFGB_MRLDIS (1 << 5)
+#define VR_CFGB_PERRDIS (1 << 6)
+#define VR_CFGB_QPKTDIS (1 << 7)
+
+/*
+ * Chip Configuration C
+ */
+#define VR_CFGC 0x7A
+#define VR_CFGC_BPS0 (1 << 0)
+#define VR_CFGC_BPS1 (1 << 1)
+#define VR_CFGC_BPS2 (1 << 2)
+#define VR_CFGC_BTSEL (1 << 3)
+#define VR_CFGC_DLYEN (1 << 5)
+#define VR_CFGC_BROPT (1 << 6)
+#define VR_CFGC_MED3 (1 << 7) /* VT6102 */
+
+/*
+ * Chip Configuration D
+ */
+#define VR_CFGD 0x7B
+#define VR_CFGD_BAKOPT (1 << 0)
+#define VR_CFGD_MBA (1 << 1)
+#define VR_CFGD_CAP (1 << 2)
+#define VR_CFGD_CRADOM (1 << 3)
+#define VR_CFGD_PMCDIG (1 << 4)
+#define VR_CFGD_MRLEN (1 << 5)
+#define VR_CFGD_TAG_ON_SNAP (1 << 5) /* VT6105M */
+#define VR_CFGD_DIAG (1 << 6)
+#define VR_CFGD_MMIOEN (1 << 7)
+
+/*
+ * Tally counters
+ */
+#define VR_TALLY_MPA 0x7c /* 16 bits */
+#define VR_TALLY_CRC 0x7e /* 16 bits */
+
+/*
+ * Misceleneous register 0
+ */
+#define VR_MISC0 0x80
+#define VR_MISC0_TIMER0_EN (1 << 0)
+#define VR_MISC0_TIMER0_SUSP (1 << 1)
+#define VR_MISC0_HDXFEN (1 << 2)
+#define VR_MISC0_FDXRFEN (1 << 3)
+#define VR_MISC0_FDXTFEN (1 << 4)
+#define VR_MISC0_TIMER0_USEC_EN (1 << 5)
+
+/*
+ * Misceleneous register 1
+ */
+#define VR_MISC1 0x81
+#define VR_MISC1_TIMER1_EN (1 << 0)
+#define VR_MISC1_VAXJMP (1 << 5)
+#define VR_MISC1_RESET (1 << 6)
+
+/*
+ * Power management
+ */
+#define VR_PWR 0x83
+#define VR_PWR_DS0 (1 << 0)
+#define VR_PWR_DS1 (1 << 1)
+#define VR_PWR_WOLEN (1 << 2)
+#define VR_PWR_WOLSR (1 << 3)
+#define VR_PWR_LGWOL (1 << 7)
+
+/*
+ * Second interrupt register status
+ */
+#define VR_ISR1 0x84
+#define VR_ISR1_TIMER0 (1 << 0)
+#define VR_ISR1_TIMER1 (1 << 1)
+#define VR_ISR1_PHYEVENT (1 << 2)
+#define VR_ISR1_TDERR (1 << 3)
+#define VR_ISR1_SSRCI (1 << 4)
+#define VR_ISR1_UINTR_SET (1 << 5)
+#define VR_ISR1_UINTR_CLR (1 << 6)
+#define VR_ISR1_PWEI (1 << 7)
+
+/*
+ * Second interrupt register configuration
+ */
+#define VR_ICR1 0x86
+#define VR_ICR1_TIMER0 VR_ISR1_TIMER0
+#define VR_ICR1_TIMER1 VR_ISR1_TIMER1
+#define VR_ICR1_PHYEVENT VR_ISR1_PHYEVENT
+#define VR_ICR1_TDERR VR_ISR1_TDERR
+#define VR_ICR1_SSRCI VR_ISR1_SSRCI
+#define VR_ICR1_UINTR_SET VR_ISR1_UINTR_SET
+#define VR_ICR1_UINTR_CLR VR_ISR1_UINTR_CLR
+#define VR_ICR1_PWEI VR_ISR1_PWEI
+
+/*
+ * Content Addressable Memory (CAM) stuff for the VT6105M
+ */
+#define VR_CAM_MASK 0x88
+
+#define VR_CAM_CTRL 0x92
+#define VR_CAM_CTRL_RD (1 << 3)
+#define VR_CAM_CTRL_WR (1 << 2)
+#define VR_CAM_CTRL_SELECT_VLAN (1 << 1)
+#define VR_CAM_CTRL_ENABLE (1 << 0)
+#define VR_CAM_CTRL_WRITE (VR_CAM_CTRL_ENABLE | VR_CAM_CTRL_WR)
+#define VR_CAM_CTRL_READ (VR_CAM_CTRL_ENABLE | VR_CAM_CTRL_RD)
+#define VR_CAM_CTRL_RW (VR_CAM_CTRL_ENABLE | \
+ VR_CAM_CTRL_RD | VR_CAM_CTRL_WR)
+#define VR_CAM_CTRL_DONE (0)
+
+#define VR_CAM_ADDR 0x93
+
+/*
+ * MIB Control register
+ */
+#define VR_MIB_CTRL 0x94
+#define VR_MIB_CTRL_ENABLE (1 << 4)
+#define VR_MIB_CTRL_HDUPLEX (1 << 5)
+#define VR_MIB_CTRL_INCR (1 << 6)
+#define VR_MIB_CTRL_RTN (1 << 7)
+
+/*
+ * MIB port
+ */
+#define VR_MIB_PORT 0x96
+
+/*
+ * MIB data
+ */
+#define VR_MIB_DATA 0x97
+
+
+/*
+ * Power configuration
+ */
+#define VR_PWRCFG 0xA1 /* VT6105LOM */
+#define VR_PWRCFG_WOLEN (1 << 0)
+#define VR_PWRCFG_WOLSR (1 << 1)
+#define VR_PWRCFG_PHYPOWERDOWN (7 << 1)
+
+/*
+ * Flow control, VT6105 and above
+ */
+#define VR_FCR0 0x98
+#define VR_FCR0_RXBUFCOUNT VR_FCR0
+
+#define VR_FCR1 0x99
+#define VR_FCR1_HD_EN (1 << 0)
+#define VR_FCR1_FD_RX_EN (1 << 1)
+#define VR_FCR1_FD_TX_EN (1 << 2)
+#define VR_FCR1_XONXOFF_EN (1 << 3)
+
+#define VR_FCR1_PAUSEOFFBITS ((1 << 5) | (1 << 4))
+#define VR_FCR1_PAUSEOFF_24 ((0 << 5) | (0 << 4))
+#define VR_FCR1_PAUSEOFF_32 ((0 << 5) | (1 << 4))
+#define VR_FCR1_PAUSEOFF_48 ((1 << 5) | (0 << 4))
+#define VR_FCR1_PAUSEOFF_64 ((1 << 5) | (1 << 4))
+
+#define VR_FCR1_PAUSEONBITS ((1 << 7) | (1 << 6))
+#define VR_FCR1_PAUSEON_04 ((0 << 7) | (0 << 6))
+#define VR_FCR1_PAUSEON_08 ((0 << 7) | (1 << 6))
+#define VR_FCR1_PAUSEON_16 ((1 << 7) | (0 << 6))
+#define VR_FCR1_PAUSEON_24 ((1 << 7) | (1 << 6))
+
+#define VR_FCR2 0x9a
+#define VR_FCR2_PAUSE (VR_FCR2)
+
+#define VR_TIMER0 0x9c
+#define VR_TIMER0_TIMEOUT VR_TIMER0 /* 16 bits */
+
+#define VR_TIMER1 0x9e
+#define VR_TIMER1_TIMEOUT VR_TIMER1 /* 16 bits */
+
+#define VR_CRC_PATTERN0 0xb0 /* 32 bits, VT6105M */
+#define VR_CRC_PATTERN1 0xb4 /* 32 bits, VT6105M */
+#define VR_CRC_PATTERN2 0xb8 /* 32 bits, VT6105M */
+#define VR_CRC_PATTERN3 0xbC /* 32 bits, VT6105M */
+
+/*
+ * Receive desctriptor
+ */
+#define VR_RDES0_RXERR (1 << 0)
+#define VR_RDES0_CRCERR (1 << 1)
+#define VR_RDES0_FAE (1 << 2)
+#define VR_RDES0_FOV (1 << 3)
+#define VR_RDES0_LONG (1 << 4)
+#define VR_RDES0_RUNT (1 << 5)
+#define VR_RDES0_SERR (1 << 6)
+#define VR_RDES0_BUFF (1 << 7)
+
+#define VR_RDES0_EDP (1 << 8)
+#define VR_RDES0_STP (1 << 9)
+#define VR_RDES0_CHN (1 << 10)
+#define VR_RDES0_PHY (1 << 11)
+#define VR_RDES0_BAR (1 << 12)
+#define VR_RDES0_MAR (1 << 13)
+#define VR_RDES0_VIDHIT (1 << 14) /* VT6105M or reserved */
+#define VR_RDES0_RXOK (1 << 15)
+
+#define VR_RDES0_ABN ((1 << 27) | (1 << 28) | (1 << 29) | (1 << 30))
+#define VR_RDES0_OWN (1U << 31)
+
+/*
+ * Transmit descriptor
+ */
+#define VR_TDES0_NCR ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3))
+#define VR_TDES0_COL (1 << 4)
+#define VR_TDES0_CDH (1 << 7)
+#define VR_TDES0_ABT (1 << 8)
+#define VR_TDES0_OWC (1 << 9)
+#define VR_TDES0_CRS (1 << 10)
+#define VR_TDES0_UDF (1 << 11)
+#define VR_TDES0_TERR (1 << 15)
+/* VLAN stuff is for VT6105M only */
+#define VR_TDES0_VLANID ((1 << 27) | (1 << 26) | (1 << 25) | (1 << 24) \
+ (1 << 23) | (1 << 22) | (1 << 21) | \
+ (1 << 20) | (1 << 19) | (1 << 18) | \
+ (1 << 17) | (1 << 16))
+#define VR_TDES0_VLANPRI ((1 << 30) | (1 << 29) | (1 << 28))
+#define VR_TDES0_OWN (1U << 31)
+
+#define VR_TDES1_LEN ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \
+ (1 << 4) | (1 << 5) | (1 << 6) | \
+ (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10))
+
+#define VR_TDES1_CHN (1 << 15)
+#define VR_TDES1_CRC (1 << 16)
+#define VR_TDES1_STP (1 << 21) /* EDP/STP are flipped in DS6105! */
+#define VR_TDES1_EDP (1 << 22)
+#define VR_TDES1_INTR (1 << 23)
+
+#define VR_TDES3_SUPPRESS_INTR (1 << 0)
+
+#endif /* _VRREG_H */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index cc5dff0f14..5bb44c38c0 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -387,6 +387,7 @@ DRV_KMODS += amd8111s
DRV_KMODS += amd_iommu
DRV_KMODS += igb
DRV_KMODS += ixgbe
+DRV_KMODS += vr
$(CLOSED_BUILD)CLOSED_DRV_KMODS += ixgb
#
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index a3e9728042..0e39017b17 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -256,6 +256,7 @@ DRV_KMODS += sfe
DRV_KMODS += aac
DRV_KMODS += igb
DRV_KMODS += ixgbe
+DRV_KMODS += vr
$(CLOSED_BUILD)CLOSED_DRV_KMODS += ixgb
#
diff --git a/usr/src/uts/sparc/vr/Makefile b/usr/src/uts/sparc/vr/Makefile
new file mode 100644
index 0000000000..3399d9f1a8
--- /dev/null
+++ b/usr/src/uts/sparc/vr/Makefile
@@ -0,0 +1,89 @@
+#
+# 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.
+# Use is subject to license terms.
+#
+
+#
+# This makefile drives the production of the VIA Rhine Ethernet
+# (vr) driver module in sparc systems
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = vr
+OBJECTS = $(VR_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(VR_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Overrides
+#
+
+#
+# Driver depends on GLD
+#
+LDFLAGS += -dy -N misc/mac
+
+#
+# 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)/sparc/Makefile.targ