diff options
author | Joost Mulders <Joost.Mulders@Sun.COM> | 2009-05-05 12:21:01 +0200 |
---|---|---|
committer | Joost Mulders <Joost.Mulders@Sun.COM> | 2009-05-05 12:21:01 +0200 |
commit | 2ca5b6595b95478e6568b0e77c6c83c8a870867a (patch) | |
tree | 5541c5c53d81d5c11fa783c4161a0cfc79cc64a7 /usr | |
parent | 30cbf0d206aed9037082f81dba96b4013bc5f836 (diff) | |
download | illumos-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/Makefile | 1 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/Makefile | 37 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/pkginfo.tmpl | 45 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/postinstall.tmpl | 30 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/preremove.tmpl | 28 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/prototype_com | 45 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/prototype_i386 | 44 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWvr/prototype_sparc | 43 | ||||
-rw-r--r-- | usr/src/uts/common/Makefile.files | 2 | ||||
-rw-r--r-- | usr/src/uts/common/Makefile.rules | 7 | ||||
-rw-r--r-- | usr/src/uts/common/io/vr/vr.c | 3657 | ||||
-rw-r--r-- | usr/src/uts/common/io/vr/vr.h | 499 | ||||
-rw-r--r-- | usr/src/uts/common/io/vr/vr_impl.h | 655 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.intel.shared | 1 | ||||
-rw-r--r-- | usr/src/uts/sparc/Makefile.sparc.shared | 1 | ||||
-rw-r--r-- | usr/src/uts/sparc/vr/Makefile | 89 |
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 **)®s, &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(®s[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 |