Mercurial > illumos > illumos-gate
changeset 4978:7bb29ac056b9
PSARC 2007/291 mxfe ethernet driver
6562372 add mxfe driver
author | gd78059 |
---|---|
date | Fri, 31 Aug 2007 17:00:55 -0700 |
parents | 6ff1c7caf2c9 |
children | 2881fac3aa3e |
files | usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWmxfe/Makefile usr/src/pkgdefs/SUNWmxfe/pkginfo.tmpl usr/src/pkgdefs/SUNWmxfe/postinstall usr/src/pkgdefs/SUNWmxfe/postremove usr/src/pkgdefs/SUNWmxfe/prototype_com usr/src/pkgdefs/SUNWmxfe/prototype_i386 usr/src/pkgdefs/SUNWmxfe/prototype_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/mxfe/mxfe.c usr/src/uts/common/io/mxfe/mxfe.h usr/src/uts/common/io/mxfe/mxfeimpl.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/mxfe/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/mxfe/Makefile |
diffstat | 17 files changed, 4559 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile Fri Aug 31 16:49:49 2007 -0700 +++ b/usr/src/pkgdefs/Makefile Fri Aug 31 17:00:55 2007 -0700 @@ -260,6 +260,7 @@ SUNWmdr \ SUNWmdu \ SUNWmibii \ + SUNWmxfe \ SUNWncar \ SUNWncau \ SUNWnfsckr \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/Makefile Fri Aug 31 17:00:55 2007 -0700 @@ -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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +DATAFILES += depend + +.KEEP_STATE: + +all: $(FILES) postinstall postremove +install: all pkg + +include ../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/pkginfo.tmpl Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PKG=SUNWmxfe +NAME=Macronix 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="Macronix Fast Ethernet Network Adapter Driver" +CLASSES="none preserve" +HOTLINE="Please contact your local service provider" +EMAIL="" +BASEDIR=/ +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="true" +SUNW_PKG_THISZONE="false"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/postinstall Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,138 @@ +#!/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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Function: check_add_drv() +# +# This function will check if the module has an entry in etc/name_to_major +# If not simply calls add_drv with the arguments given. If there is +# such an entry in name_to_major file, it adds entries in driver_aliases +# driver_classes and minor_perm if necessary. +# The syntax of this function is the same as add_drv. + +check_add_drv() +{ + if [ "$BASEDIR" = "" ] + then + BASEDIR=/ + fi + alias="" + class="" + ADD_ALIAS=0 + ADD_CLASS=0 + ADD_MINOR=0 + OPTIND=1 + IS_NET_DRIVER=0 + + cmd="add_drv" + + NO_CMD= + while getopts i:b:m:c:N opt + do + case $opt in + N ) NO_CMD=1;; + i ) ADD_ALIAS=1 + alias=$OPTARG + cmd=$cmd" -i '$alias'" + ;; + m ) ADD_MINOR=1 + minor=$OPTARG + cmd=$cmd" -m '$minor'" + ;; + c) ADD_CLASS=1 + class=$OPTARG + cmd=$cmd" -c $class" + ;; + b) BASEDIR=$OPTARG + cmd=$cmd" -b $BASEDIR" + ;; + \?) echo "check_add_drv can not handle this option" + return + ;; + esac + done + shift `/usr/bin/expr $OPTIND - 1` + + drvname=$1 + + cmd=$cmd" "$drvname + + drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'` + + /usr/bin/grep "^$drvname[ ]" $BASEDIR/etc/name_to_major > /dev/null 2>&1 + + # + # NB: We really would have liked to use update_drv here, but + # since we can't do that (see CR 6281386), we have this code. + # Note that if we've never added this driver before, the add_drv + # below takes care of worrying about conflicting entries in + # /etc/driver_aliases. (It will fail if there is a conflicting + # driver squatting on the alias.) + # + if [ "$NO_CMD" = "" -a $? -ne 0 ] + then + eval $cmd + else + # entry already in name_to_major, add alias, class, minorperm + # if necessary + if [ $ADD_ALIAS = 1 ] + then + for i in $alias + do + /usr/bin/egrep "^$drvname[ ]+$i" $BASEDIR/etc/driver_aliases>/dev/null 2>&1 + if [ $? -ne 0 ] + then + echo "$drvname $i" >> $BASEDIR/etc/driver_aliases + fi + done + fi + + if [ $ADD_CLASS = 1 ] + then + /usr/bin/egrep "^$drvname[ ]+$class( | |$)" $BASEDIR/etc/driver_classes > /dev/null 2>&1 + if [ $? -ne 0 ] + then + echo "$drvname\t$class" >> $BASEDIR/etc/driver_classes + fi + fi + + if [ $ADD_MINOR = 1 ] + then + /usr/bin/grep "^$drvname:" $BASEDIR/etc/minor_perm > /dev/null 2>&1 + if [ $? -ne 0 ] + then + minorentry="$drvname:$minor" + echo $minorentry >> $BASEDIR/etc/minor_perm + fi + fi + + fi + + +} + +check_add_drv -i '"pci11ad,c115" "pci10d9,531" "pci10d9,512" "pci11fc,9881"' -b "$BASEDIR" mxfe
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/postremove Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,38 @@ +#!/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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +BD=${BASEDIR:-/} +if grep -w mxfe $BD/etc/name_to_major > /dev/null 2>&1 +then + rem_drv -b ${BD} mxfe + if [ $? -ne 0 ] + then + exit 1 + fi +fi +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/prototype_com Fri Aug 31 17:00:55 2007 -0700 @@ -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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# 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 postremove
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/prototype_i386 Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# 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 +# +# + +# Macronix Fast Ethernet driver +d none kernel 0755 root sys +d none kernel/drv 0755 root sys +f none kernel/drv/mxfe 0755 root sys +d none kernel/drv/amd64 0755 root sys +f none kernel/drv/amd64/mxfe 0755 root sys
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmxfe/prototype_sparc Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,50 @@ +# +# 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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# 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 +# +# + +# Macronix Fast Ethernet driver +d none kernel 0755 root sys +d none kernel/drv 0755 root sys +d none kernel/drv/sparcv9 0755 root sys +f none kernel/drv/sparcv9/mxfe 0755 root sys
--- a/usr/src/uts/common/Makefile.files Fri Aug 31 16:49:49 2007 -0700 +++ b/usr/src/uts/common/Makefile.files Fri Aug 31 17:00:55 2007 -0700 @@ -1353,6 +1353,8 @@ RTW_OBJS += rtw.o smc93cx6.o rtwphy.o rtwphyio.o +MXFE_OBJS += mxfe.o + # # Build up defines and paths. #
--- a/usr/src/uts/common/Makefile.rules Fri Aug 31 16:49:49 2007 -0700 +++ b/usr/src/uts/common/Makefile.rules Fri Aug 31 17:00:55 2007 -0700 @@ -599,6 +599,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/mxfe/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/net80211/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1398,6 +1402,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mac/plugins/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mxfe/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/net80211/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mxfe/mxfe.c Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,3302 @@ +/* + * Solaris driver for ethernet cards based on the Macronix 98715 + * + * Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/varargs.h> +#include <sys/types.h> +#include <sys/modctl.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/cmn_err.h> +#include <sys/dlpi.h> +#include <sys/ethernet.h> +#include <sys/kmem.h> +#include <sys/time.h> +#include <sys/miiregs.h> +#include <sys/strsun.h> +#include <sys/priv.h> +#include <sys/policy.h> +#include <sys/cred.h> +#include <sys/mac.h> +#include <sys/mac_ether.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> + +#include "mxfe.h" +#include "mxfeimpl.h" + +/* + * Driver globals. + */ + +/* patchable debug flag ... must not be static! */ +#ifdef DEBUG +unsigned mxfe_debug = DWARN; +#endif + +/* table of supported devices */ +static mxfe_card_t mxfe_cards[] = { + + /* + * Lite-On products + */ + { 0x11ad, 0xc115, 0, 0, "Lite-On LC82C115", MXFE_PNICII }, + + /* + * Macronix chips + */ + { 0x10d9, 0x0531, 0x25, 0xff, "Macronix MX98715AEC", MXFE_98715AEC }, + { 0x10d9, 0x0531, 0x20, 0xff, "Macronix MX98715A", MXFE_98715A }, + { 0x10d9, 0x0531, 0x60, 0xff, "Macronix MX98715B", MXFE_98715B }, + { 0x10d9, 0x0531, 0x30, 0xff, "Macronix MX98725", MXFE_98725 }, + { 0x10d9, 0x0531, 0x00, 0xff, "Macronix MX98715", MXFE_98715 }, + { 0x10d9, 0x0512, 0, 0, "Macronix MX98713", MXFE_98713 }, + + /* + * Compex (relabeled Macronix products) + */ + { 0x11fc, 0x9881, 0x00, 0x00, "Compex 9881", MXFE_98713 }, + { 0x11fc, 0x9881, 0x10, 0xff, "Compex 9881A", MXFE_98713A }, + /* + * Models listed here + */ + { 0x11ad, 0xc001, 0, 0, "Linksys LNE100TX", MXFE_PNICII }, + { 0x2646, 0x000b, 0, 0, "Kingston KNE111TX", MXFE_PNICII }, + { 0x1154, 0x0308, 0, 0, "Buffalo LGY-PCI-TXL", MXFE_98715AEC }, +}; + +#define ETHERVLANMTU (ETHERMAX + 4) + +/* + * Function prototypes + */ +static int mxfe_attach(dev_info_t *, ddi_attach_cmd_t); +static int mxfe_detach(dev_info_t *, ddi_detach_cmd_t); +static int mxfe_resume(dev_info_t *); +static int mxfe_m_unicst(void *, const uint8_t *); +static int mxfe_m_multicst(void *, boolean_t, const uint8_t *); +static int mxfe_m_promisc(void *, boolean_t); +static mblk_t *mxfe_m_tx(void *, mblk_t *); +static int mxfe_m_stat(void *, uint_t, uint64_t *); +static int mxfe_m_start(void *); +static void mxfe_m_stop(void *); +static void mxfe_m_ioctl(void *, queue_t *, mblk_t *); +static unsigned mxfe_intr(caddr_t); +static void mxfe_startmac(mxfe_t *); +static void mxfe_stopmac(mxfe_t *); +static void mxfe_resetrings(mxfe_t *); +static boolean_t mxfe_initialize(mxfe_t *); +static void mxfe_startall(mxfe_t *); +static void mxfe_stopall(mxfe_t *); +static void mxfe_resetall(mxfe_t *); +static mxfe_txbuf_t *mxfe_alloctxbuf(mxfe_t *); +static void mxfe_destroytxbuf(mxfe_txbuf_t *); +static mxfe_rxbuf_t *mxfe_allocrxbuf(mxfe_t *); +static void mxfe_destroyrxbuf(mxfe_rxbuf_t *); +static void mxfe_send_setup(mxfe_t *); +static boolean_t mxfe_send(mxfe_t *, mblk_t *); +static int mxfe_allocrxring(mxfe_t *); +static void mxfe_freerxring(mxfe_t *); +static int mxfe_alloctxring(mxfe_t *); +static void mxfe_freetxring(mxfe_t *); +static void mxfe_error(dev_info_t *, char *, ...); +static uint8_t mxfe_sromwidth(mxfe_t *); +static uint16_t mxfe_readsromword(mxfe_t *, unsigned); +static void mxfe_readsrom(mxfe_t *, unsigned, unsigned, void *); +static void mxfe_getfactaddr(mxfe_t *, uchar_t *); +static int mxfe_miireadbit(mxfe_t *); +static void mxfe_miiwritebit(mxfe_t *, int); +static void mxfe_miitristate(mxfe_t *); +static unsigned mxfe_miiread(mxfe_t *, int, int); +static void mxfe_miiwrite(mxfe_t *, int, int, uint16_t); +static unsigned mxfe_miireadgeneral(mxfe_t *, int, int); +static void mxfe_miiwritegeneral(mxfe_t *, int, int, uint16_t); +static unsigned mxfe_miiread98713(mxfe_t *, int, int); +static void mxfe_miiwrite98713(mxfe_t *, int, int, uint16_t); +static void mxfe_startphy(mxfe_t *); +static void mxfe_stopphy(mxfe_t *); +static void mxfe_startphymii(mxfe_t *); +static void mxfe_startphynway(mxfe_t *); +static void mxfe_startnway(mxfe_t *); +static void mxfe_reportlink(mxfe_t *); +static void mxfe_checklink(mxfe_t *); +static void mxfe_checklinkmii(mxfe_t *); +static void mxfe_checklinknway(mxfe_t *); +static void mxfe_disableinterrupts(mxfe_t *); +static void mxfe_enableinterrupts(mxfe_t *); +static void mxfe_reclaim(mxfe_t *); +static mblk_t *mxfe_receive(mxfe_t *); +static int mxfe_ndaddbytes(mblk_t *, char *, int); +static int mxfe_ndaddstr(mblk_t *, char *, int); +static void mxfe_ndparsestring(mblk_t *, char *, int); +static int mxfe_ndparselen(mblk_t *); +static int mxfe_ndparseint(mblk_t *); +static void mxfe_ndget(mxfe_t *, queue_t *, mblk_t *); +static void mxfe_ndset(mxfe_t *, queue_t *, mblk_t *); +static void mxfe_ndfini(mxfe_t *); +static void mxfe_ndinit(mxfe_t *); +static int mxfe_ndquestion(mxfe_t *, mblk_t *, mxfe_nd_t *); +static int mxfe_ndgetint(mxfe_t *, mblk_t *, mxfe_nd_t *); +static int mxfe_ndgetbit(mxfe_t *, mblk_t *, mxfe_nd_t *); +static int mxfe_ndsetadv(mxfe_t *, mblk_t *, mxfe_nd_t *); +static mxfe_nd_t *mxfe_ndfind(mxfe_t *, char *); +static void mxfe_ndempty(mblk_t *); +static void mxfe_ndadd(mxfe_t *, char *, mxfe_nd_pf_t, mxfe_nd_pf_t, + intptr_t, intptr_t); + +#ifdef DEBUG +static void mxfe_dprintf(mxfe_t *, const char *, int, char *, ...); +#endif + +#define KIOIP KSTAT_INTR_PTR(mxfep->mxfe_intrstat) + +static mac_callbacks_t mxfe_m_callbacks = { + MC_IOCTL, + mxfe_m_stat, + mxfe_m_start, + mxfe_m_stop, + mxfe_m_promisc, + mxfe_m_multicst, + mxfe_m_unicst, + mxfe_m_tx, + NULL, + mxfe_m_ioctl, + NULL, /* m_getcapab */ +}; + +/* + * Stream information + */ +DDI_DEFINE_STREAM_OPS(mxfe_devops, nulldev, nulldev, mxfe_attach, mxfe_detach, + nodev, NULL, D_MP, NULL); + +/* + * Module linkage information. + */ + +static struct modldrv mxfe_modldrv = { + &mod_driverops, /* drv_modops */ + "Macronix Fast Ethernet", /* drv_linkinfo */ + &mxfe_devops /* drv_dev_ops */ +}; + +static struct modlinkage mxfe_modlinkage = { + MODREV_1, /* ml_rev */ + { &mxfe_modldrv, NULL } /* ml_linkage */ +}; + +/* + * Device attributes. + */ +static ddi_device_acc_attr_t mxfe_devattr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + +static ddi_device_acc_attr_t mxfe_bufattr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC +}; + +static ddi_dma_attr_t mxfe_dma_attr = { + DMA_ATTR_V0, /* dma_attr_version */ + 0, /* dma_attr_addr_lo */ + 0xFFFFFFFFU, /* dma_attr_addr_hi */ + 0x7FFFFFFFU, /* dma_attr_count_max */ + 4, /* dma_attr_align */ + 0x3F, /* dma_attr_burstsizes */ + 1, /* dma_attr_minxfer */ + 0xFFFFFFFFU, /* dma_attr_maxxfer */ + 0xFFFFFFFFU, /* dma_attr_seg */ + 1, /* dma_attr_sgllen */ + 1, /* dma_attr_granular */ + 0 /* dma_attr_flags */ +}; + +/* + * Tx buffers can be arbitrarily aligned. Additionally, they can + * cross a page boundary, so we use the two buffer addresses of the + * chip to provide a two-entry scatter-gather list. + */ +static ddi_dma_attr_t mxfe_dma_txattr = { + DMA_ATTR_V0, /* dma_attr_version */ + 0, /* dma_attr_addr_lo */ + 0xFFFFFFFFU, /* dma_attr_addr_hi */ + 0x7FFFFFFFU, /* dma_attr_count_max */ + 1, /* dma_attr_align */ + 0x3F, /* dma_attr_burstsizes */ + 1, /* dma_attr_minxfer */ + 0xFFFFFFFFU, /* dma_attr_maxxfer */ + 0xFFFFFFFFU, /* dma_attr_seg */ + 2, /* dma_attr_sgllen */ + 1, /* dma_attr_granular */ + 0 /* dma_attr_flags */ +}; + +/* + * Ethernet addresses. + */ +static uchar_t mxfe_broadcast[ETHERADDRL] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* + * DDI entry points. + */ +int +_init(void) +{ + int rv; + mac_init_ops(&mxfe_devops, "mxfe"); + if ((rv = mod_install(&mxfe_modlinkage)) != DDI_SUCCESS) { + mac_fini_ops(&mxfe_devops); + } + return (rv); +} + +int +_fini(void) +{ + int rv; + if ((rv = mod_remove(&mxfe_modlinkage)) == DDI_SUCCESS) { + mac_fini_ops(&mxfe_devops); + } + return (rv); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&mxfe_modlinkage, modinfop)); +} + +int +mxfe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + mxfe_t *mxfep; + mac_register_t *macp; + int inst = ddi_get_instance(dip); + ddi_acc_handle_t pci; + uint16_t venid; + uint16_t devid; + uint16_t revid; + uint16_t svid; + uint16_t ssid; + uint16_t cachesize; + mxfe_card_t *cardp; + int i; + + switch (cmd) { + case DDI_RESUME: + return (mxfe_resume(dip)); + + case DDI_ATTACH: + break; + + default: + return (DDI_FAILURE); + } + + /* this card is a bus master, reject any slave-only slot */ + if (ddi_slaveonly(dip) == DDI_SUCCESS) { + mxfe_error(dip, "slot does not support PCI bus-master"); + return (DDI_FAILURE); + } + /* PCI devices shouldn't generate hilevel interrupts */ + if (ddi_intr_hilevel(dip, 0) != 0) { + mxfe_error(dip, "hilevel interrupts not supported"); + return (DDI_FAILURE); + } + if (pci_config_setup(dip, &pci) != DDI_SUCCESS) { + mxfe_error(dip, "unable to setup PCI config handle"); + return (DDI_FAILURE); + } + + venid = pci_config_get16(pci, PCI_VID); + devid = pci_config_get16(pci, PCI_DID); + revid = pci_config_get16(pci, PCI_RID); + svid = pci_config_get16(pci, PCI_SVID); + ssid = pci_config_get16(pci, PCI_SSID); + + /* + * the last entry in the card table matches every possible + * card, so the for-loop always terminates properly. + */ + cardp = NULL; + for (i = 0; i < (sizeof (mxfe_cards) / sizeof (mxfe_card_t)); i++) { + if ((venid == mxfe_cards[i].card_venid) && + (devid == mxfe_cards[i].card_devid) && + ((revid & mxfe_cards[i].card_revmask) == + mxfe_cards[i].card_revid)) { + cardp = &mxfe_cards[i]; + } + if ((svid == mxfe_cards[i].card_venid) && + (ssid == mxfe_cards[i].card_devid) && + ((revid & mxfe_cards[i].card_revmask) == + mxfe_cards[i].card_revid)) { + cardp = &mxfe_cards[i]; + break; + } + } + + if (cardp == NULL) { + pci_config_teardown(&pci); + mxfe_error(dip, "Unable to identify PCI card"); + return (DDI_FAILURE); + } + + if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", + cardp->card_cardname) != DDI_PROP_SUCCESS) { + pci_config_teardown(&pci); + mxfe_error(dip, "Unable to create model property"); + return (DDI_FAILURE); + } + + /* + * Grab the PCI cachesize -- we use this to program the + * cache-optimization bus access bits. + */ + cachesize = pci_config_get8(pci, PCI_CLS); + + /* this cannot fail */ + mxfep = kmem_zalloc(sizeof (mxfe_t), KM_SLEEP); + ddi_set_driver_private(dip, mxfep); + + /* get the interrupt block cookie */ + if (ddi_get_iblock_cookie(dip, 0, &mxfep->mxfe_icookie) + != DDI_SUCCESS) { + mxfe_error(dip, "ddi_get_iblock_cookie failed"); + pci_config_teardown(&pci); + kmem_free(mxfep, sizeof (mxfe_t)); + return (DDI_FAILURE); + } + + mxfep->mxfe_dip = dip; + mxfep->mxfe_cardp = cardp; + mxfep->mxfe_phyaddr = -1; + mxfep->mxfe_cachesize = cachesize; + + /* default properties */ + mxfep->mxfe_adv_aneg = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_autoneg_cap", 1); + mxfep->mxfe_adv_100T4 = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_100T4_cap", 1); + mxfep->mxfe_adv_100fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_100fdx_cap", 1); + mxfep->mxfe_adv_100hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_100hdx_cap", 1); + mxfep->mxfe_adv_10fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_10fdx_cap", 1); + mxfep->mxfe_adv_10hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, + "adv_10hdx_cap", 1); + + DBG(DPCI, "PCI vendor id = %x", venid); + DBG(DPCI, "PCI device id = %x", devid); + DBG(DPCI, "PCI revision id = %x", revid); + DBG(DPCI, "PCI cachesize = %d", cachesize); + DBG(DPCI, "PCI COMM = %x", pci_config_get8(pci, PCI_CMD)); + DBG(DPCI, "PCI STAT = %x", pci_config_get8(pci, PCI_STAT)); + + mutex_init(&mxfep->mxfe_xmtlock, NULL, MUTEX_DRIVER, + mxfep->mxfe_icookie); + mutex_init(&mxfep->mxfe_intrlock, NULL, MUTEX_DRIVER, + mxfep->mxfe_icookie); + + mxfe_ndinit(mxfep); + + /* + * Enable bus master, IO space, and memory space accesses. + */ + pci_config_put16(pci, PCI_CMD, + pci_config_get16(pci, PCI_CMD) | + PCI_CMD_BME | PCI_CMD_MAE | PCI_CMD_MWIE); + + /* we're done with this now, drop it */ + pci_config_teardown(&pci); + + /* + * Initialize interrupt kstat. This should not normally fail, since + * we don't use a persistent stat. We do it this way to avoid having + * to test for it at run time on the hot path. + */ + mxfep->mxfe_intrstat = kstat_create("mxfe", inst, "intr", "controller", + KSTAT_TYPE_INTR, 1, 0); + if (mxfep->mxfe_intrstat == NULL) { + mxfe_error(dip, "kstat_create failed"); + goto failed; + } + kstat_install(mxfep->mxfe_intrstat); + + /* + * Map in the device registers. + */ + if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mxfep->mxfe_regs, + 0, 0, &mxfe_devattr, &mxfep->mxfe_regshandle)) { + mxfe_error(dip, "ddi_regs_map_setup failed"); + goto failed; + } + + /* + * Allocate DMA resources (descriptor rings and buffers). + */ + if ((mxfe_allocrxring(mxfep) != DDI_SUCCESS) || + (mxfe_alloctxring(mxfep) != DDI_SUCCESS)) { + mxfe_error(dip, "unable to allocate DMA resources"); + goto failed; + } + + /* Initialize the chip. */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + if (!mxfe_initialize(mxfep)) { + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + goto failed; + } + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + + /* Determine the number of address bits to our EEPROM. */ + mxfep->mxfe_sromwidth = mxfe_sromwidth(mxfep); + + /* + * Get the factory ethernet address. This becomes the current + * ethernet address (it can be overridden later via ifconfig). + */ + mxfe_getfactaddr(mxfep, mxfep->mxfe_curraddr); + mxfep->mxfe_promisc = B_FALSE; + + /* + * Establish interrupt handler. + */ + if (ddi_add_intr(dip, 0, NULL, NULL, mxfe_intr, (caddr_t)mxfep) != + DDI_SUCCESS) { + mxfe_error(dip, "unable to add interrupt"); + goto failed; + } + + /* TODO: do the power management stuff */ + + if ((macp = mac_alloc(MAC_VERSION)) == NULL) { + mxfe_error(dip, "mac_alloc failed"); + goto failed; + } + + macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; + macp->m_driver = mxfep; + macp->m_dip = dip; + macp->m_src_addr = mxfep->mxfe_curraddr; + macp->m_callbacks = &mxfe_m_callbacks; + macp->m_min_sdu = 0; + macp->m_max_sdu = ETHERMTU; + + if (mac_register(macp, &mxfep->mxfe_mh) == DDI_SUCCESS) { + mac_free(macp); + return (DDI_SUCCESS); + } + + /* failed to register with MAC */ + mac_free(macp); +failed: + if (mxfep->mxfe_icookie != NULL) { + ddi_remove_intr(dip, 0, mxfep->mxfe_icookie); + } + if (mxfep->mxfe_intrstat) { + kstat_delete(mxfep->mxfe_intrstat); + } + mxfe_ndfini(mxfep); + mutex_destroy(&mxfep->mxfe_intrlock); + mutex_destroy(&mxfep->mxfe_xmtlock); + + mxfe_freerxring(mxfep); + mxfe_freetxring(mxfep); + + if (mxfep->mxfe_regshandle != NULL) { + ddi_regs_map_free(&mxfep->mxfe_regshandle); + } + kmem_free(mxfep, sizeof (mxfe_t)); + return (DDI_FAILURE); +} + +int +mxfe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + mxfe_t *mxfep; + + mxfep = ddi_get_driver_private(dip); + if (mxfep == NULL) { + mxfe_error(dip, "no soft state in detach!"); + return (DDI_FAILURE); + } + + switch (cmd) { + case DDI_DETACH: + + if (mac_unregister(mxfep->mxfe_mh) != 0) { + return (DDI_FAILURE); + } + + /* make sure hardware is quiesced */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + mxfep->mxfe_flags &= ~MXFE_RUNNING; + mxfe_stopall(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + + /* clean up and shut down device */ + ddi_remove_intr(dip, 0, mxfep->mxfe_icookie); + + /* clean up kstats */ + kstat_delete(mxfep->mxfe_intrstat); + + ddi_prop_remove_all(dip); + + /* free up any left over buffers or DMA resources */ + mxfe_freerxring(mxfep); + mxfe_freetxring(mxfep); + + mxfe_ndfini(mxfep); + ddi_regs_map_free(&mxfep->mxfe_regshandle); + mutex_destroy(&mxfep->mxfe_intrlock); + mutex_destroy(&mxfep->mxfe_xmtlock); + + kmem_free(mxfep, sizeof (mxfe_t)); + return (DDI_SUCCESS); + + case DDI_SUSPEND: + /* quiesce the hardware */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + mxfep->mxfe_flags |= MXFE_SUSPENDED; + mxfe_stopall(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + return (DDI_SUCCESS); + default: + return (DDI_FAILURE); + } +} + +int +mxfe_resume(dev_info_t *dip) +{ + mxfe_t *mxfep; + + if ((mxfep = ddi_get_driver_private(dip)) == NULL) { + return (DDI_FAILURE); + } + + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + + mxfep->mxfe_flags &= ~MXFE_SUSPENDED; + + /* re-initialize chip */ + if (!mxfe_initialize(mxfep)) { + mxfe_error(mxfep->mxfe_dip, "unable to resume chip!"); + mxfep->mxfe_flags |= MXFE_SUSPENDED; + mutex_exit(&mxfep->mxfe_intrlock); + mutex_exit(&mxfep->mxfe_xmtlock); + return (DDI_SUCCESS); + } + + /* start the chip */ + if (mxfep->mxfe_flags & MXFE_RUNNING) { + mxfe_startall(mxfep); + } + + /* drop locks */ + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + + return (DDI_SUCCESS); +} + +void +mxfe_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) +{ + mxfe_t *mxfep = arg; + + switch (*(int *)(void *)(mp->b_rptr)) { + + case NDIOC_GET: + mxfe_ndget(mxfep, wq, mp); + break; + + case NDIOC_SET: + mxfe_ndset(mxfep, wq, mp); + break; + + default: + miocnak(wq, mp, 0, EINVAL); + break; + } +} + +/*ARGSUSED*/ +int +mxfe_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr) +{ + /* we already receive all multicast frames */ + return (0); +} + +int +mxfe_m_promisc(void *arg, boolean_t on) +{ + mxfe_t *mxfep = arg; + + /* exclusive access to the card while we reprogram it */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + /* save current promiscuous mode state for replay in resume */ + mxfep->mxfe_promisc = on; + + if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == + MXFE_RUNNING) { + if (on) + SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); + else + CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); + } + + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + + return (0); +} + +int +mxfe_m_unicst(void *arg, const uint8_t *macaddr) +{ + mxfe_t *mxfep = arg; + + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + bcopy(macaddr, mxfep->mxfe_curraddr, ETHERADDRL); + + mxfe_resetall(mxfep); + + mutex_exit(&mxfep->mxfe_intrlock); + mutex_exit(&mxfep->mxfe_xmtlock); + + return (0); +} + +mblk_t * +mxfe_m_tx(void *arg, mblk_t *mp) +{ + mxfe_t *mxfep = arg; + mblk_t *nmp; + + mutex_enter(&mxfep->mxfe_xmtlock); + + if (mxfep->mxfe_flags & MXFE_SUSPENDED) { + mutex_exit(&mxfep->mxfe_xmtlock); + return (mp); + } + + while (mp != NULL) { + nmp = mp->b_next; + mp->b_next = NULL; + + if (!mxfe_send(mxfep, mp)) { + mp->b_next = nmp; + break; + } + mp = nmp; + } + mutex_exit(&mxfep->mxfe_xmtlock); + + return (mp); +} + +/* + * Hardware management. + */ +boolean_t +mxfe_initialize(mxfe_t *mxfep) +{ + int i; + unsigned val; + uint32_t par, nar; + + ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + + DBG(DCHATTY, "resetting!"); + SETBIT(mxfep, CSR_PAR, PAR_RESET); + for (i = 1; i < 10; i++) { + drv_usecwait(5); + val = GETCSR(mxfep, CSR_PAR); + if (!(val & PAR_RESET)) { + break; + } + } + if (i == 10) { + mxfe_error(mxfep->mxfe_dip, "timed out waiting for reset!"); + return (B_FALSE); + } + + /* initialize busctl register */ + par = PAR_BAR | PAR_MRME | PAR_MRLE | PAR_MWIE; + + /* set the cache alignment if its supported */ + switch (mxfep->mxfe_cachesize) { + case 8: + par |= PAR_CALIGN_8; + break; + case 16: + par |= PAR_CALIGN_16; + break; + case 32: + par |= PAR_CALIGN_32; + break; + default: + par &= ~(PAR_MWIE | PAR_MRME | PAR_MRLE); + } + + /* leave the burst length at zero, indicating infinite burst */ + PUTCSR(mxfep, CSR_PAR, par); + + mxfe_resetrings(mxfep); + + /* clear the lost packet counter (cleared on read) */ + (void) GETCSR(mxfep, CSR_LPC); + + /* a few other NAR bits */ + nar = GETCSR(mxfep, CSR_NAR); + nar &= ~NAR_RX_HO; /* disable hash only filtering */ + nar |= NAR_RX_HP; /* hash perfect forwarding */ + nar |= NAR_RX_MULTI; /* receive all multicast */ + nar |= NAR_SF; /* store-and-forward */ + + if (mxfep->mxfe_promisc) { + nar |= NAR_RX_PROMISC; + } else { + nar &= ~NAR_RX_PROMISC; + } + PUTCSR(mxfep, CSR_NAR, nar); + + mxfe_send_setup(mxfep); + + return (B_TRUE); +} + +/* + * Serial EEPROM access - inspired by the FreeBSD implementation. + */ + +uint8_t +mxfe_sromwidth(mxfe_t *mxfep) +{ + int i; + int eeread; + uint8_t addrlen = 8; + + eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP; + + PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP); + drv_usecwait(1); + PUTCSR(mxfep, CSR_SPR, eeread); + + /* command bits first */ + for (i = 4; i != 0; i >>= 1) { + unsigned val = (SROM_READCMD & i) ? SPR_SROM_DIN : 0; + PUTCSR(mxfep, CSR_SPR, eeread | val); + drv_usecwait(1); + PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK); + drv_usecwait(1); + } + + PUTCSR(mxfep, CSR_SPR, eeread); + + for (addrlen = 1; addrlen <= 12; addrlen++) { + PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK); + drv_usecwait(1); + if (!(GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT)) { + PUTCSR(mxfep, CSR_SPR, eeread); + drv_usecwait(1); + break; + } + PUTCSR(mxfep, CSR_SPR, eeread); + drv_usecwait(1); + } + + /* turn off accesses to the EEPROM */ + PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP); + + DBG(DSROM, "detected srom width = %d bits", addrlen); + + return ((addrlen < 4 || addrlen > 12) ? 6 : addrlen); +} + +/* + * The words in EEPROM are stored in little endian order. We + * shift bits out in big endian order, though. This requires + * a byte swap on some platforms. + */ +uint16_t +mxfe_readsromword(mxfe_t *mxfep, unsigned romaddr) +{ + int i; + uint16_t word = 0; + uint16_t retval; + int eeread; + uint8_t addrlen; + int readcmd; + uchar_t *ptr; + + eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP; + addrlen = mxfep->mxfe_sromwidth; + readcmd = (SROM_READCMD << addrlen) | romaddr; + + if (romaddr >= (1 << addrlen)) { + /* too big to fit! */ + return (0); + } + + PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP); + PUTCSR(mxfep, CSR_SPR, eeread); + + /* command and address bits */ + for (i = 4 + addrlen; i >= 0; i--) { + short val = (readcmd & (1 << i)) ? SPR_SROM_DIN : 0; + PUTCSR(mxfep, CSR_SPR, eeread | val); + drv_usecwait(1); + PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK); + drv_usecwait(1); + } + + PUTCSR(mxfep, CSR_SPR, eeread); + + for (i = 0; i < 16; i++) { + PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK); + drv_usecwait(1); + word <<= 1; + if (GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT) { + word |= 1; + } + PUTCSR(mxfep, CSR_SPR, eeread); + drv_usecwait(1); + } + + /* turn off accesses to the EEPROM */ + PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP); + + /* + * Fix up the endianness thing. Note that the values + * are stored in little endian format on the SROM. + */ + DBG(DSROM, "got value %d from SROM (before swap)", word); + ptr = (uchar_t *)&word; + retval = (ptr[1] << 8) | ptr[0]; + return (retval); +} + +void +mxfe_readsrom(mxfe_t *mxfep, unsigned romaddr, unsigned len, void *dest) +{ + char *ptr = dest; + int i; + uint16_t word; + + for (i = 0; i < len; i++) { + word = mxfe_readsromword(mxfep, romaddr + i); + bcopy(&word, ptr, 2); + ptr += 2; + DBG(DSROM, "word at %d is 0x%x", romaddr + i, word); + } +} + +void +mxfe_getfactaddr(mxfe_t *mxfep, uchar_t *eaddr) +{ + uint16_t word; + uchar_t *ptr; + + /* first read to get the location of mac address in srom */ + word = mxfe_readsromword(mxfep, SROM_ENADDR / 2); + ptr = (uchar_t *)&word; + word = (ptr[1] << 8) | ptr[0]; + + /* then read the actual mac address */ + mxfe_readsrom(mxfep, word / 2, ETHERADDRL / 2, eaddr); + DBG(DMACID, + "factory ethernet address = %02x:%02x:%02x:%02x:%02x:%02x", + eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); +} + +void +mxfe_startphy(mxfe_t *mxfep) +{ + switch (MXFE_MODEL(mxfep)) { + case MXFE_98713A: + mxfe_startphymii(mxfep); + break; + default: + mxfe_startphynway(mxfep); + break; + } +} + +void +mxfe_stopphy(mxfe_t *mxfep) +{ + uint32_t nar; + int i; + + /* stop the phy timer */ + PUTCSR(mxfep, CSR_TIMER, 0); + + switch (MXFE_MODEL(mxfep)) { + case MXFE_98713A: + for (i = 0; i < 32; i++) { + mxfe_miiwrite(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL, + MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE); + } + break; + default: + DBG(DPHY, "resetting SIA"); + PUTCSR(mxfep, CSR_SIA, SIA_RESET); + drv_usecwait(500); + CLRBIT(mxfep, CSR_TCTL, TCTL_PWR | TCTL_ANE); + nar = GETCSR(mxfep, CSR_NAR); + nar &= ~(NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX); + nar |= NAR_SPEED; + PUTCSR(mxfep, CSR_NAR, nar); + break; + } + + /* + * mark the link state unknown + */ + if (!mxfep->mxfe_resetting) { + mxfep->mxfe_linkup = LINK_STATE_UNKNOWN; + mxfep->mxfe_ifspeed = 0; + mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; + if (mxfep->mxfe_flags & MXFE_RUNNING) + mxfe_reportlink(mxfep); + } +} + +/* + * NWay support. + */ +void +mxfe_startnway(mxfe_t *mxfep) +{ + unsigned nar; + unsigned tctl; + unsigned restart; + + /* this should not happen in a healthy system */ + if (mxfep->mxfe_linkstate != MXFE_NOLINK) { + DBG(DWARN, "link start called out of state (%x)", + mxfep->mxfe_linkstate); + return; + } + + if (mxfep->mxfe_adv_aneg == 0) { + /* not done for forced mode */ + return; + } + + nar = GETCSR(mxfep, CSR_NAR); + restart = nar & (NAR_TX_ENABLE | NAR_RX_ENABLE); + nar &= ~restart; + + if (restart != 0) + mxfe_stopmac(mxfep); + + nar |= NAR_SCR | NAR_PCS | NAR_HBD; + nar &= ~(NAR_FDX); + + tctl = GETCSR(mxfep, CSR_TCTL); + tctl &= ~(TCTL_100FDX | TCTL_100HDX | TCTL_HDX); + + if (mxfep->mxfe_adv_100fdx) { + tctl |= TCTL_100FDX; + } + if (mxfep->mxfe_adv_100hdx) { + tctl |= TCTL_100HDX; + } + if (mxfep->mxfe_adv_10fdx) { + nar |= NAR_FDX; + } + if (mxfep->mxfe_adv_10hdx) { + tctl |= TCTL_HDX; + } + tctl |= TCTL_PWR | TCTL_ANE | TCTL_LTE | TCTL_RSQ; + + /* possibly we should add in support for PAUSE frames */ + DBG(DPHY, "writing nar = 0x%x", nar); + PUTCSR(mxfep, CSR_NAR, nar); + + DBG(DPHY, "writing tctl = 0x%x", tctl); + PUTCSR(mxfep, CSR_TCTL, tctl); + + /* restart autonegotation */ + DBG(DPHY, "writing tstat = 0x%x", TSTAT_ANS_START); + PUTCSR(mxfep, CSR_TSTAT, TSTAT_ANS_START); + + /* restart tx/rx processes... */ + if (restart != 0) + mxfe_startmac(mxfep); + + /* Macronix initializations from Bolo Tsai */ + PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000); + PUTCSR(mxfep, CSR_ACOMP, 0x11000); + + mxfep->mxfe_linkstate = MXFE_NWAYCHECK; +} + +void +mxfe_checklinknway(mxfe_t *mxfep) +{ + unsigned tstat, lpar; + + DBG(DPHY, "NWay check, state %x", mxfep->mxfe_linkstate); + tstat = GETCSR(mxfep, CSR_TSTAT); + lpar = TSTAT_LPAR(tstat); + + mxfep->mxfe_anlpar = lpar; + if (tstat & TSTAT_LPN) { + mxfep->mxfe_aner |= MII_AN_EXP_LPCANAN; + } else { + mxfep->mxfe_aner &= ~(MII_AN_EXP_LPCANAN); + } + + DBG(DPHY, "tstat(CSR12) = 0x%x", tstat); + DBG(DPHY, "ANEG state = 0x%x", (tstat & TSTAT_ANS) >> 12); + + if ((tstat & TSTAT_ANS) != TSTAT_ANS_OK) { + /* autoneg did not complete */ + mxfep->mxfe_bmsr &= ~MII_STATUS_ANDONE; + } else { + mxfep->mxfe_bmsr |= ~MII_STATUS_ANDONE; + } + + if ((tstat & TSTAT_100F) && (tstat & TSTAT_10F)) { + mxfep->mxfe_linkup = LINK_STATE_DOWN; + mxfep->mxfe_ifspeed = 0; + mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; + mxfep->mxfe_linkstate = MXFE_NOLINK; + mxfe_reportlink(mxfep); + mxfe_startnway(mxfep); + return; + } + + /* + * if the link is newly up, then we might need to set various + * mode bits, or negotiate for parameters, etc. + */ + if (mxfep->mxfe_adv_aneg) { + + uint16_t anlpar; + + mxfep->mxfe_linkup = LINK_STATE_UP; + anlpar = mxfep->mxfe_anlpar; + + if (tstat & TSTAT_LPN) { + /* partner has NWay */ + + if ((anlpar & MII_ABILITY_100BASE_TX_FD) && + mxfep->mxfe_adv_100fdx) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if ((anlpar & MII_ABILITY_100BASE_TX) && + mxfep->mxfe_adv_100hdx) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else if ((anlpar & MII_ABILITY_10BASE_T_FD) && + mxfep->mxfe_adv_10fdx) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if ((anlpar & MII_ABILITY_10BASE_T) && + mxfep->mxfe_adv_10hdx) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else { + mxfep->mxfe_ifspeed = 0; + } + } else { + /* link partner does not have NWay */ + /* just assume half duplex, since we can't detect */ + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + if (!(tstat & TSTAT_100F)) { + DBG(DPHY, "Partner doesn't have NWAY"); + mxfep->mxfe_ifspeed = 100000000; + } else { + mxfep->mxfe_ifspeed = 10000000; + } + } + } else { + /* forced modes */ + mxfep->mxfe_linkup = LINK_STATE_UP; + if (mxfep->mxfe_adv_100fdx) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if (mxfep->mxfe_adv_100hdx) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else if (mxfep->mxfe_adv_10fdx) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if (mxfep->mxfe_adv_10hdx) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else { + mxfep->mxfe_ifspeed = 0; + } + } + mxfe_reportlink(mxfep); + mxfep->mxfe_linkstate = MXFE_GOODLINK; +} + +void +mxfe_startphynway(mxfe_t *mxfep) +{ + /* take NWay and PHY out of reset */ + PUTCSR(mxfep, CSR_SIA, SIA_NRESET); + drv_usecwait(500); + + mxfep->mxfe_linkstate = MXFE_NOLINK; + mxfep->mxfe_bmsr = MII_STATUS_CANAUTONEG | + MII_STATUS_100_BASEX_FD | MII_STATUS_100_BASEX | + MII_STATUS_10_FD | MII_STATUS_10; + + /* lie about the transceiver... its not really 802.3u compliant */ + mxfep->mxfe_phyaddr = 0; + mxfep->mxfe_phyinuse = XCVR_100X; + mxfep->mxfe_phyid = 0; + + /* 100-T4 not supported with NWay */ + mxfep->mxfe_adv_100T4 = 0; + + /* make sure at least one valid mode is selected */ + if ((!mxfep->mxfe_adv_100fdx) && + (!mxfep->mxfe_adv_100hdx) && + (!mxfep->mxfe_adv_10fdx) && + (!mxfep->mxfe_adv_10hdx)) { + mxfe_error(mxfep->mxfe_dip, "No valid link mode selected."); + mxfe_error(mxfep->mxfe_dip, "Powering down PHY."); + mxfe_stopphy(mxfep); + mxfep->mxfe_linkup = LINK_STATE_DOWN; + if (mxfep->mxfe_flags & MXFE_RUNNING) + mxfe_reportlink(mxfep); + return; + } + + if (mxfep->mxfe_adv_aneg == 0) { + /* forced mode */ + unsigned nar; + unsigned tctl; + + nar = GETCSR(mxfep, CSR_NAR); + tctl = GETCSR(mxfep, CSR_TCTL); + + ASSERT((nar & (NAR_TX_ENABLE | NAR_RX_ENABLE)) == 0); + + nar &= ~(NAR_FDX | NAR_PORTSEL | NAR_SCR | NAR_SPEED); + tctl &= ~TCTL_ANE; + if (mxfep->mxfe_adv_100fdx) { + nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX; + } else if (mxfep->mxfe_adv_100hdx) { + nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR; + } else if (mxfep->mxfe_adv_10fdx) { + nar |= NAR_FDX | NAR_SPEED; + } else { /* mxfep->mxfe_adv_10hdx */ + nar |= NAR_SPEED; + } + + PUTCSR(mxfep, CSR_NAR, nar); + PUTCSR(mxfep, CSR_TCTL, tctl); + + /* Macronix initializations from Bolo Tsai */ + PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000); + PUTCSR(mxfep, CSR_ACOMP, 0x11000); + } else { + mxfe_startnway(mxfep); + } + PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP | + (MXFE_LINKTIMER * 1000 / TIMER_USEC)); +} + +/* + * MII management. + */ +void +mxfe_startphymii(mxfe_t *mxfep) +{ + unsigned phyaddr; + unsigned bmcr; + unsigned bmsr; + unsigned anar; + unsigned phyidr1; + unsigned phyidr2; + int retries; + int cnt; + + mxfep->mxfe_phyaddr = -1; + + /* search for first PHY we can find */ + for (phyaddr = 0; phyaddr < 32; phyaddr++) { + bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS); + if ((bmsr != 0) && (bmsr != 0xffff)) { + mxfep->mxfe_phyaddr = phyaddr; + break; + } + } + + phyidr1 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDH); + phyidr2 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDL); + mxfep->mxfe_phyid = (phyidr1 << 16) | (phyidr2); + + /* + * Generally, all Macronix based devices use an internal + * 100BASE-TX internal transceiver. If we ever run into a + * variation on this, then the following logic will need to be + * enhanced. + * + * One could question the value of the XCVR_INUSE field in the + * MII statistics. + */ + if (bmsr & MII_STATUS_100_BASE_T4) { + mxfep->mxfe_phyinuse = XCVR_100T4; + } else { + mxfep->mxfe_phyinuse = XCVR_100X; + } + + DBG(DPHY, "phy at %d: %x,%x", phyaddr, phyidr1, phyidr2); + DBG(DPHY, "bmsr = %x", mxfe_miiread(mxfep, + mxfep->mxfe_phyaddr, MII_STATUS)); + DBG(DPHY, "anar = %x", mxfe_miiread(mxfep, + mxfep->mxfe_phyaddr, MII_AN_ADVERT)); + DBG(DPHY, "anlpar = %x", mxfe_miiread(mxfep, + mxfep->mxfe_phyaddr, MII_AN_LPABLE)); + DBG(DPHY, "aner = %x", mxfe_miiread(mxfep, + mxfep->mxfe_phyaddr, MII_AN_EXPANSION)); + + DBG(DPHY, "resetting phy"); + + /* we reset the phy block */ + mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, MII_CONTROL_RESET); + /* + * wait for it to complete -- 500usec is still to short to + * bother getting the system clock involved. + */ + drv_usecwait(500); + for (retries = 0; retries < 10; retries++) { + if (mxfe_miiread(mxfep, phyaddr, MII_CONTROL) & + MII_CONTROL_RESET) { + drv_usecwait(500); + continue; + } + break; + } + if (retries == 100) { + mxfe_error(mxfep->mxfe_dip, "timeout waiting on phy to reset"); + return; + } + + DBG(DPHY, "phy reset complete"); + + bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS); + bmcr = mxfe_miiread(mxfep, phyaddr, MII_CONTROL); + anar = mxfe_miiread(mxfep, phyaddr, MII_AN_ADVERT); + + anar &= ~(MII_ABILITY_100BASE_T4 | + MII_ABILITY_100BASE_TX_FD | MII_ABILITY_100BASE_TX | + MII_ABILITY_10BASE_T_FD | MII_ABILITY_10BASE_T); + + /* disable modes not supported in hardware */ + if (!(bmsr & MII_STATUS_100_BASE_T4)) { + mxfep->mxfe_adv_100T4 = 0; + } + if (!(bmsr & MII_STATUS_100_BASEX_FD)) { + mxfep->mxfe_adv_100fdx = 0; + } + if (!(bmsr & MII_STATUS_100_BASEX)) { + mxfep->mxfe_adv_100hdx = 0; + } + if (!(bmsr & MII_STATUS_10_FD)) { + mxfep->mxfe_adv_10fdx = 0; + } + if (!(bmsr & MII_STATUS_10)) { + mxfep->mxfe_adv_10hdx = 0; + } + if (!(bmsr & MII_STATUS_CANAUTONEG)) { + mxfep->mxfe_adv_aneg = 0; + } + + cnt = 0; + if (mxfep->mxfe_adv_100T4) { + anar |= MII_ABILITY_100BASE_T4; + cnt++; + } + if (mxfep->mxfe_adv_100fdx) { + anar |= MII_ABILITY_100BASE_TX_FD; + cnt++; + } + if (mxfep->mxfe_adv_100hdx) { + anar |= MII_ABILITY_100BASE_TX; + cnt++; + } + if (mxfep->mxfe_adv_10fdx) { + anar |= MII_ABILITY_10BASE_T_FD; + cnt++; + } + if (mxfep->mxfe_adv_10hdx) { + anar |= MII_ABILITY_10BASE_T; + cnt++; + } + + /* + * Make certain at least one valid link mode is selected. + */ + if (!cnt) { + mxfe_error(mxfep->mxfe_dip, "No valid link mode selected."); + mxfe_error(mxfep->mxfe_dip, "Powering down PHY."); + mxfe_stopphy(mxfep); + mxfep->mxfe_linkup = LINK_STATE_DOWN; + if (mxfep->mxfe_flags & MXFE_RUNNING) + mxfe_reportlink(mxfep); + return; + } + + if ((mxfep->mxfe_adv_aneg) && (bmsr & MII_STATUS_CANAUTONEG)) { + DBG(DPHY, "using autoneg mode"); + bmcr = (MII_CONTROL_ANE | MII_CONTROL_RSAN); + } else { + DBG(DPHY, "using forced mode"); + if (mxfep->mxfe_adv_100fdx) { + bmcr = (MII_CONTROL_100MB | MII_CONTROL_FDUPLEX); + } else if (mxfep->mxfe_adv_100hdx) { + bmcr = MII_CONTROL_100MB; + } else if (mxfep->mxfe_adv_10fdx) { + bmcr = MII_CONTROL_FDUPLEX; + } else { + /* 10HDX */ + bmcr = 0; + } + } + + mxfep->mxfe_forcephy = 0; + + DBG(DPHY, "programming anar to 0x%x", anar); + mxfe_miiwrite(mxfep, phyaddr, MII_AN_ADVERT, anar); + DBG(DPHY, "programming bmcr to 0x%x", bmcr); + mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, bmcr); + + /* + * schedule a query of the link status + */ + PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP | + (MXFE_LINKTIMER * 1000 / TIMER_USEC)); +} + +void +mxfe_reportlink(mxfe_t *mxfep) +{ + int changed = 0; + + if (mxfep->mxfe_ifspeed != mxfep->mxfe_lastifspeed) { + mxfep->mxfe_lastifspeed = mxfep->mxfe_ifspeed; + changed++; + } + if (mxfep->mxfe_duplex != mxfep->mxfe_lastduplex) { + mxfep->mxfe_lastduplex = mxfep->mxfe_duplex; + changed++; + } + if (mxfep->mxfe_linkup != mxfep->mxfe_lastlinkup) { + mxfep->mxfe_lastlinkup = mxfep->mxfe_linkup; + changed++; + } + if (changed) + mac_link_update(mxfep->mxfe_mh, mxfep->mxfe_linkup); +} + +void +mxfe_checklink(mxfe_t *mxfep) +{ + if ((mxfep->mxfe_flags & MXFE_RUNNING) == 0) + return; + + if ((mxfep->mxfe_txstall_time != 0) && + (gethrtime() > mxfep->mxfe_txstall_time) && + (mxfep->mxfe_txavail != MXFE_TXRING)) { + mxfep->mxfe_txstall_time = 0; + mxfe_error(mxfep->mxfe_dip, "TX stall detected!"); + mxfe_resetall(mxfep); + return; + } + + switch (MXFE_MODEL(mxfep)) { + case MXFE_98713A: + mxfe_checklinkmii(mxfep); + break; + default: + mxfe_checklinknway(mxfep); + } +} + +void +mxfe_checklinkmii(mxfe_t *mxfep) +{ + /* read MII state registers */ + uint16_t bmsr; + uint16_t bmcr; + uint16_t anar; + uint16_t anlpar; + uint16_t aner; + + /* read this twice, to clear latched link state */ + bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS); + bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS); + bmcr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL); + anar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_ADVERT); + anlpar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_LPABLE); + aner = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_EXPANSION); + + mxfep->mxfe_bmsr = bmsr; + mxfep->mxfe_anlpar = anlpar; + mxfep->mxfe_aner = aner; + + if (bmsr & MII_STATUS_REMFAULT) { + mxfe_error(mxfep->mxfe_dip, "Remote fault detected."); + } + if (bmsr & MII_STATUS_JABBERING) { + mxfe_error(mxfep->mxfe_dip, "Jabber condition detected."); + } + if ((bmsr & MII_STATUS_LINKUP) == 0) { + /* no link */ + mxfep->mxfe_ifspeed = 0; + mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; + mxfep->mxfe_linkup = LINK_STATE_DOWN; + mxfe_reportlink(mxfep); + return; + } + + DBG(DCHATTY, "link up!"); + mxfep->mxfe_linkup = LINK_STATE_UP; + + if (!(bmcr & MII_CONTROL_ANE)) { + /* forced mode */ + if (bmcr & MII_CONTROL_100MB) { + mxfep->mxfe_ifspeed = 100000000; + } else { + mxfep->mxfe_ifspeed = 10000000; + } + if (bmcr & MII_CONTROL_FDUPLEX) { + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else { + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } + } else if ((!(bmsr & MII_STATUS_CANAUTONEG)) || + (!(bmsr & MII_STATUS_ANDONE))) { + mxfep->mxfe_ifspeed = 0; + mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; + } else if (anar & anlpar & MII_ABILITY_100BASE_TX_FD) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if (anar & anlpar & MII_ABILITY_100BASE_T4) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else if (anar & anlpar & MII_ABILITY_100BASE_TX) { + mxfep->mxfe_ifspeed = 100000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else if (anar & anlpar & MII_ABILITY_10BASE_T_FD) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_FULL; + } else if (anar & anlpar & MII_ABILITY_10BASE_T) { + mxfep->mxfe_ifspeed = 10000000; + mxfep->mxfe_duplex = LINK_DUPLEX_HALF; + } else { + mxfep->mxfe_ifspeed = 0; + mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; + } + + mxfe_reportlink(mxfep); +} + +void +mxfe_miitristate(mxfe_t *mxfep) +{ + unsigned val = SPR_SROM_WRITE | SPR_MII_CTRL; + PUTCSR(mxfep, CSR_SPR, val); + drv_usecwait(1); + PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); + drv_usecwait(1); +} + +void +mxfe_miiwritebit(mxfe_t *mxfep, int bit) +{ + unsigned val = bit ? SPR_MII_DOUT : 0; + PUTCSR(mxfep, CSR_SPR, val); + drv_usecwait(1); + PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); + drv_usecwait(1); +} + +int +mxfe_miireadbit(mxfe_t *mxfep) +{ + unsigned val = SPR_MII_CTRL | SPR_SROM_READ; + int bit; + PUTCSR(mxfep, CSR_SPR, val); + drv_usecwait(1); + bit = (GETCSR(mxfep, CSR_SPR) & SPR_MII_DIN) ? 1 : 0; + PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); + drv_usecwait(1); + return (bit); +} + +unsigned +mxfe_miiread(mxfe_t *mxfep, int phy, int reg) +{ + switch (MXFE_MODEL(mxfep)) { + case MXFE_98713A: + return (mxfe_miiread98713(mxfep, phy, reg)); + default: + return (0xffff); + } +} + +unsigned +mxfe_miireadgeneral(mxfe_t *mxfep, int phy, int reg) +{ + unsigned value = 0; + int i; + + /* send the 32 bit preamble */ + for (i = 0; i < 32; i++) { + mxfe_miiwritebit(mxfep, 1); + } + + /* send the start code - 01b */ + mxfe_miiwritebit(mxfep, 0); + mxfe_miiwritebit(mxfep, 1); + + /* send the opcode for read, - 10b */ + mxfe_miiwritebit(mxfep, 1); + mxfe_miiwritebit(mxfep, 0); + + /* next we send the 5 bit phy address */ + for (i = 0x10; i > 0; i >>= 1) { + mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0); + } + + /* the 5 bit register address goes next */ + for (i = 0x10; i > 0; i >>= 1) { + mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0); + } + + /* turnaround - tristate followed by logic 0 */ + mxfe_miitristate(mxfep); + mxfe_miiwritebit(mxfep, 0); + + /* read the 16 bit register value */ + for (i = 0x8000; i > 0; i >>= 1) { + value <<= 1; + value |= mxfe_miireadbit(mxfep); + } + mxfe_miitristate(mxfep); + return (value); +} + +unsigned +mxfe_miiread98713(mxfe_t *mxfep, int phy, int reg) +{ + unsigned nar; + unsigned retval; + /* + * like an ordinary MII, but we have to turn off portsel while + * we read it. + */ + nar = GETCSR(mxfep, CSR_NAR); + PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL); + retval = mxfe_miireadgeneral(mxfep, phy, reg); + PUTCSR(mxfep, CSR_NAR, nar); + return (retval); +} + +void +mxfe_miiwrite(mxfe_t *mxfep, int phy, int reg, uint16_t val) +{ + switch (MXFE_MODEL(mxfep)) { + case MXFE_98713A: + mxfe_miiwrite98713(mxfep, phy, reg, val); + break; + default: + break; + } +} + +void +mxfe_miiwritegeneral(mxfe_t *mxfep, int phy, int reg, uint16_t val) +{ + int i; + + /* send the 32 bit preamble */ + for (i = 0; i < 32; i++) { + mxfe_miiwritebit(mxfep, 1); + } + + /* send the start code - 01b */ + mxfe_miiwritebit(mxfep, 0); + mxfe_miiwritebit(mxfep, 1); + + /* send the opcode for write, - 01b */ + mxfe_miiwritebit(mxfep, 0); + mxfe_miiwritebit(mxfep, 1); + + /* next we send the 5 bit phy address */ + for (i = 0x10; i > 0; i >>= 1) { + mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0); + } + + /* the 5 bit register address goes next */ + for (i = 0x10; i > 0; i >>= 1) { + mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0); + } + + /* turnaround - tristate followed by logic 0 */ + mxfe_miitristate(mxfep); + mxfe_miiwritebit(mxfep, 0); + + /* now write out our data (16 bits) */ + for (i = 0x8000; i > 0; i >>= 1) { + mxfe_miiwritebit(mxfep, (val & i) ? 1 : 0); + } + + /* idle mode */ + mxfe_miitristate(mxfep); +} + +void +mxfe_miiwrite98713(mxfe_t *mxfep, int phy, int reg, uint16_t val) +{ + unsigned nar; + /* + * like an ordinary MII, but we have to turn off portsel while + * we read it. + */ + nar = GETCSR(mxfep, CSR_NAR); + PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL); + mxfe_miiwritegeneral(mxfep, phy, reg, val); + PUTCSR(mxfep, CSR_NAR, nar); +} + +int +mxfe_m_start(void *arg) +{ + mxfe_t *mxfep = arg; + + /* grab exclusive access to the card */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + + mxfe_startall(mxfep); + mxfep->mxfe_flags |= MXFE_RUNNING; + + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + return (0); +} + +void +mxfe_m_stop(void *arg) +{ + mxfe_t *mxfep = arg; + + /* exclusive access to the hardware! */ + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + + mxfe_stopall(mxfep); + mxfep->mxfe_flags &= ~MXFE_RUNNING; + + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); +} + +void +mxfe_startmac(mxfe_t *mxfep) +{ + /* verify exclusive access to the card */ + ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + + /* start the card */ + SETBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE); + + if (mxfep->mxfe_txavail != MXFE_TXRING) + PUTCSR(mxfep, CSR_TDR, 0); + + /* tell the mac that we are ready to go! */ + if (mxfep->mxfe_flags & MXFE_RUNNING) + mac_tx_update(mxfep->mxfe_mh); +} + +void +mxfe_stopmac(mxfe_t *mxfep) +{ + int i; + + /* exclusive access to the hardware! */ + ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + + CLRBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE); + + /* + * A 1518 byte frame at 10Mbps takes about 1.2 msec to drain. + * We just add up to the nearest msec (2), which should be + * plenty to complete. + * + * Note that some chips never seem to indicate the transition to + * the stopped state properly. Experience shows that we can safely + * proceed anyway, after waiting the requisite timeout. + */ + for (i = 2000; i != 0; i -= 10) { + if ((GETCSR(mxfep, CSR_SR) & (SR_TX_STATE | SR_RX_STATE)) == 0) + break; + drv_usecwait(10); + } + + /* prevent an interrupt */ + PUTCSR(mxfep, CSR_SR, INT_RXSTOPPED | INT_TXSTOPPED); +} + +void +mxfe_resetrings(mxfe_t *mxfep) +{ + int i; + + /* now we need to reset the pointers... */ + PUTCSR(mxfep, CSR_RDB, 0); + PUTCSR(mxfep, CSR_TDB, 0); + + /* reset the descriptor ring pointers */ + mxfep->mxfe_rxhead = 0; + mxfep->mxfe_txreclaim = 0; + mxfep->mxfe_txsend = 0; + mxfep->mxfe_txavail = MXFE_TXRING; + + /* set up transmit descriptor ring */ + for (i = 0; i < MXFE_TXRING; i++) { + mxfe_desc_t *tmdp = &mxfep->mxfe_txdescp[i]; + unsigned control = 0; + if (i == (MXFE_TXRING - 1)) { + control |= TXCTL_ENDRING; + } + PUTTXDESC(mxfep, tmdp->desc_status, 0); + PUTTXDESC(mxfep, tmdp->desc_control, control); + PUTTXDESC(mxfep, tmdp->desc_buffer1, 0); + PUTTXDESC(mxfep, tmdp->desc_buffer2, 0); + SYNCTXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV); + } + PUTCSR(mxfep, CSR_TDB, mxfep->mxfe_txdesc_paddr); + + /* make the receive buffers available */ + for (i = 0; i < MXFE_RXRING; i++) { + mxfe_rxbuf_t *rxb = mxfep->mxfe_rxbufs[i]; + mxfe_desc_t *rmdp = &mxfep->mxfe_rxdescp[i]; + unsigned control; + + control = MXFE_BUFSZ & RXCTL_BUFLEN1; + if (i == (MXFE_RXRING - 1)) { + control |= RXCTL_ENDRING; + } + PUTRXDESC(mxfep, rmdp->desc_buffer1, rxb->rxb_paddr); + PUTRXDESC(mxfep, rmdp->desc_buffer2, 0); + PUTRXDESC(mxfep, rmdp->desc_control, control); + PUTRXDESC(mxfep, rmdp->desc_status, RXSTAT_OWN); + SYNCRXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV); + } + PUTCSR(mxfep, CSR_RDB, mxfep->mxfe_rxdesc_paddr); +} + +void +mxfe_stopall(mxfe_t *mxfep) +{ + mxfe_disableinterrupts(mxfep); + + mxfe_stopmac(mxfep); + + /* stop the phy */ + mxfe_stopphy(mxfep); +} + +void +mxfe_startall(mxfe_t *mxfep) +{ + ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + + /* make sure interrupts are disabled to begin */ + mxfe_disableinterrupts(mxfep); + + /* initialize the chip */ + (void) mxfe_initialize(mxfep); + + /* now we can enable interrupts */ + mxfe_enableinterrupts(mxfep); + + /* start up the phy */ + mxfe_startphy(mxfep); + + /* start up the mac */ + mxfe_startmac(mxfep); +} + +void +mxfe_resetall(mxfe_t *mxfep) +{ + mxfep->mxfe_resetting = B_TRUE; + mxfe_stopall(mxfep); + mxfep->mxfe_resetting = B_FALSE; + mxfe_startall(mxfep); +} + +mxfe_txbuf_t * +mxfe_alloctxbuf(mxfe_t *mxfep) +{ + ddi_dma_cookie_t dmac; + unsigned ncookies; + mxfe_txbuf_t *txb; + size_t len; + + txb = kmem_zalloc(sizeof (*txb), KM_SLEEP); + + if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_txattr, + DDI_DMA_SLEEP, NULL, &txb->txb_dmah) != DDI_SUCCESS) { + return (NULL); + } + + if (ddi_dma_mem_alloc(txb->txb_dmah, MXFE_BUFSZ, &mxfe_bufattr, + DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &txb->txb_buf, + &len, &txb->txb_acch) != DDI_SUCCESS) { + return (NULL); + } + if (ddi_dma_addr_bind_handle(txb->txb_dmah, NULL, txb->txb_buf, + len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, + &dmac, &ncookies) != DDI_DMA_MAPPED) { + return (NULL); + } + txb->txb_paddr = dmac.dmac_address; + + return (txb); +} + +void +mxfe_destroytxbuf(mxfe_txbuf_t *txb) +{ + if (txb != NULL) { + if (txb->txb_paddr) + (void) ddi_dma_unbind_handle(txb->txb_dmah); + if (txb->txb_acch) + ddi_dma_mem_free(&txb->txb_acch); + if (txb->txb_dmah) + ddi_dma_free_handle(&txb->txb_dmah); + kmem_free(txb, sizeof (*txb)); + } +} + +mxfe_rxbuf_t * +mxfe_allocrxbuf(mxfe_t *mxfep) +{ + mxfe_rxbuf_t *rxb; + size_t len; + unsigned ccnt; + ddi_dma_cookie_t dmac; + + rxb = kmem_zalloc(sizeof (*rxb), KM_SLEEP); + + if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, + DDI_DMA_SLEEP, NULL, &rxb->rxb_dmah) != DDI_SUCCESS) { + kmem_free(rxb, sizeof (*rxb)); + return (NULL); + } + if (ddi_dma_mem_alloc(rxb->rxb_dmah, MXFE_BUFSZ, &mxfe_bufattr, + DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, + &rxb->rxb_buf, &len, &rxb->rxb_acch) != DDI_SUCCESS) { + ddi_dma_free_handle(&rxb->rxb_dmah); + kmem_free(rxb, sizeof (*rxb)); + return (NULL); + } + if (ddi_dma_addr_bind_handle(rxb->rxb_dmah, NULL, rxb->rxb_buf, len, + DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac, + &ccnt) != DDI_DMA_MAPPED) { + ddi_dma_mem_free(&rxb->rxb_acch); + ddi_dma_free_handle(&rxb->rxb_dmah); + kmem_free(rxb, sizeof (*rxb)); + return (NULL); + } + rxb->rxb_paddr = dmac.dmac_address; + + return (rxb); +} + +void +mxfe_destroyrxbuf(mxfe_rxbuf_t *rxb) +{ + if (rxb != NULL) { + (void) ddi_dma_unbind_handle(rxb->rxb_dmah); + ddi_dma_mem_free(&rxb->rxb_acch); + ddi_dma_free_handle(&rxb->rxb_dmah); + kmem_free(rxb, sizeof (*rxb)); + } +} + +/* + * Allocate receive resources. + */ +int +mxfe_allocrxring(mxfe_t *mxfep) +{ + int rval; + int i; + size_t size; + size_t len; + ddi_dma_cookie_t dmac; + unsigned ncookies; + caddr_t kaddr; + + size = MXFE_RXRING * sizeof (mxfe_desc_t); + + rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, + DDI_DMA_SLEEP, NULL, &mxfep->mxfe_rxdesc_dmah); + if (rval != DDI_SUCCESS) { + mxfe_error(mxfep->mxfe_dip, + "unable to allocate DMA handle for rx descriptors"); + return (DDI_FAILURE); + } + + rval = ddi_dma_mem_alloc(mxfep->mxfe_rxdesc_dmah, size, &mxfe_devattr, + DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len, + &mxfep->mxfe_rxdesc_acch); + if (rval != DDI_SUCCESS) { + mxfe_error(mxfep->mxfe_dip, + "unable to allocate DMA memory for rx descriptors"); + return (DDI_FAILURE); + } + + rval = ddi_dma_addr_bind_handle(mxfep->mxfe_rxdesc_dmah, NULL, kaddr, + size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, + &dmac, &ncookies); + if (rval != DDI_DMA_MAPPED) { + mxfe_error(mxfep->mxfe_dip, + "unable to bind DMA for rx descriptors"); + return (DDI_FAILURE); + } + + /* because of mxfe_dma_attr */ + ASSERT(ncookies == 1); + + /* we take the 32-bit physical address out of the cookie */ + mxfep->mxfe_rxdesc_paddr = dmac.dmac_address; + mxfep->mxfe_rxdescp = (void *)kaddr; + + /* allocate buffer pointers (not the buffers themselves, yet) */ + mxfep->mxfe_rxbufs = kmem_zalloc(MXFE_RXRING * sizeof (mxfe_rxbuf_t *), + KM_SLEEP); + + /* now allocate rx buffers */ + for (i = 0; i < MXFE_RXRING; i++) { + mxfe_rxbuf_t *rxb = mxfe_allocrxbuf(mxfep); + if (rxb == NULL) + return (DDI_FAILURE); + mxfep->mxfe_rxbufs[i] = rxb; + } + + return (DDI_SUCCESS); +} + +/* + * Allocate transmit resources. + */ +int +mxfe_alloctxring(mxfe_t *mxfep) +{ + int rval; + int i; + size_t size; + size_t len; + ddi_dma_cookie_t dmac; + unsigned ncookies; + caddr_t kaddr; + + size = MXFE_TXRING * sizeof (mxfe_desc_t); + + rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, + DDI_DMA_SLEEP, NULL, &mxfep->mxfe_txdesc_dmah); + if (rval != DDI_SUCCESS) { + mxfe_error(mxfep->mxfe_dip, + "unable to allocate DMA handle for tx descriptors"); + return (DDI_FAILURE); + } + + rval = ddi_dma_mem_alloc(mxfep->mxfe_txdesc_dmah, size, &mxfe_devattr, + DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len, + &mxfep->mxfe_txdesc_acch); + if (rval != DDI_SUCCESS) { + mxfe_error(mxfep->mxfe_dip, + "unable to allocate DMA memory for tx descriptors"); + return (DDI_FAILURE); + } + + rval = ddi_dma_addr_bind_handle(mxfep->mxfe_txdesc_dmah, NULL, kaddr, + size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, + &dmac, &ncookies); + if (rval != DDI_DMA_MAPPED) { + mxfe_error(mxfep->mxfe_dip, + "unable to bind DMA for tx descriptors"); + return (DDI_FAILURE); + } + + /* because of mxfe_dma_attr */ + ASSERT(ncookies == 1); + + /* we take the 32-bit physical address out of the cookie */ + mxfep->mxfe_txdesc_paddr = dmac.dmac_address; + mxfep->mxfe_txdescp = (void *)kaddr; + + /* allocate buffer pointers (not the buffers themselves, yet) */ + mxfep->mxfe_txbufs = kmem_zalloc(MXFE_TXRING * sizeof (mxfe_txbuf_t *), + KM_SLEEP); + + /* now allocate tx buffers */ + for (i = 0; i < MXFE_TXRING; i++) { + mxfe_txbuf_t *txb = mxfe_alloctxbuf(mxfep); + if (txb == NULL) + return (DDI_FAILURE); + /* stick it in the stack */ + mxfep->mxfe_txbufs[i] = txb; + } + + return (DDI_SUCCESS); +} + +void +mxfe_freerxring(mxfe_t *mxfep) +{ + int i; + + for (i = 0; i < MXFE_RXRING; i++) { + mxfe_destroyrxbuf(mxfep->mxfe_rxbufs[i]); + } + + if (mxfep->mxfe_rxbufs) { + kmem_free(mxfep->mxfe_rxbufs, + MXFE_RXRING * sizeof (mxfe_rxbuf_t *)); + } + + if (mxfep->mxfe_rxdesc_paddr) + (void) ddi_dma_unbind_handle(mxfep->mxfe_rxdesc_dmah); + if (mxfep->mxfe_rxdesc_acch) + ddi_dma_mem_free(&mxfep->mxfe_rxdesc_acch); + if (mxfep->mxfe_rxdesc_dmah) + ddi_dma_free_handle(&mxfep->mxfe_rxdesc_dmah); +} + +void +mxfe_freetxring(mxfe_t *mxfep) +{ + int i; + + for (i = 0; i < MXFE_TXRING; i++) { + mxfe_destroytxbuf(mxfep->mxfe_txbufs[i]); + } + + if (mxfep->mxfe_txbufs) { + kmem_free(mxfep->mxfe_txbufs, + MXFE_TXRING * sizeof (mxfe_txbuf_t *)); + } + if (mxfep->mxfe_txdesc_paddr) + (void) ddi_dma_unbind_handle(mxfep->mxfe_txdesc_dmah); + if (mxfep->mxfe_txdesc_acch) + ddi_dma_mem_free(&mxfep->mxfe_txdesc_acch); + if (mxfep->mxfe_txdesc_dmah) + ddi_dma_free_handle(&mxfep->mxfe_txdesc_dmah); +} + +/* + * Interrupt service routine. + */ +unsigned +mxfe_intr(caddr_t arg) +{ + mxfe_t *mxfep = (void *)arg; + uint32_t status; + mblk_t *mp = NULL; + + mutex_enter(&mxfep->mxfe_intrlock); + + if (mxfep->mxfe_flags & MXFE_SUSPENDED) { + /* we cannot receive interrupts! */ + mutex_exit(&mxfep->mxfe_intrlock); + return (DDI_INTR_UNCLAIMED); + } + + /* check interrupt status bits, did we interrupt? */ + status = GETCSR(mxfep, CSR_SR) & INT_ALL; + + if (status == 0) { + KIOIP->intrs[KSTAT_INTR_SPURIOUS]++; + mutex_exit(&mxfep->mxfe_intrlock); + return (DDI_INTR_UNCLAIMED); + } + /* ack the interrupt */ + PUTCSR(mxfep, CSR_SR, status); + KIOIP->intrs[KSTAT_INTR_HARD]++; + + if (!(mxfep->mxfe_flags & MXFE_RUNNING)) { + /* not running, don't touch anything */ + mutex_exit(&mxfep->mxfe_intrlock); + return (DDI_INTR_CLAIMED); + } + + if (status & INT_RXOK) { + /* receive packets */ + mp = mxfe_receive(mxfep); + } + + if (status & INT_TXOK) { + /* transmit completed */ + mutex_enter(&mxfep->mxfe_xmtlock); + mxfe_reclaim(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + } + + if (((status & (INT_TIMER|INT_ANEG)) != 0) || + ((mxfep->mxfe_linkup == LINK_STATE_UP) && + ((status & (INT_10LINK|INT_100LINK)) != 0))) { + /* rescan the link */ + mutex_enter(&mxfep->mxfe_xmtlock); + mxfe_checklink(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + } + + if (status & (INT_RXSTOPPED|INT_TXSTOPPED|INT_RXNOBUF| + INT_RXJABBER|INT_TXJABBER|INT_TXUNDERFLOW)) { + + if (status & (INT_RXJABBER | INT_TXJABBER)) { + mxfep->mxfe_jabber++; + } + DBG(DWARN, "resetting mac, status %x", status); + mutex_enter(&mxfep->mxfe_xmtlock); + mxfe_resetall(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + } + + if (status & INT_BUSERR) { + switch (status & SR_BERR_TYPE) { + case SR_BERR_PARITY: + mxfe_error(mxfep->mxfe_dip, "PCI parity error"); + break; + case SR_BERR_TARGET_ABORT: + mxfe_error(mxfep->mxfe_dip, "PCI target abort"); + break; + case SR_BERR_MASTER_ABORT: + mxfe_error(mxfep->mxfe_dip, "PCI master abort"); + break; + default: + mxfe_error(mxfep->mxfe_dip, "Unknown PCI error"); + break; + } + + /* reset the chip in an attempt to fix things */ + mutex_enter(&mxfep->mxfe_xmtlock); + mxfe_resetall(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + } + + mutex_exit(&mxfep->mxfe_intrlock); + + /* + * Send up packets. We do this outside of the intrlock. + */ + if (mp) { + mac_rx(mxfep->mxfe_mh, NULL, mp); + } + + return (DDI_INTR_CLAIMED); +} + +void +mxfe_enableinterrupts(mxfe_t *mxfep) +{ + unsigned mask = INT_WANTED; + + if (mxfep->mxfe_wantw) + mask |= INT_TXOK; + + if (MXFE_MODEL(mxfep) != MXFE_98713A) + mask |= INT_LINKSTATUS; + + DBG(DINTR, "setting int mask to 0x%x", mask); + PUTCSR(mxfep, CSR_IER, mask); +} + +void +mxfe_disableinterrupts(mxfe_t *mxfep) +{ + /* disable further interrupts */ + PUTCSR(mxfep, CSR_IER, 0); + + /* clear any pending interrupts */ + PUTCSR(mxfep, CSR_SR, INT_ALL); +} + +void +mxfe_send_setup(mxfe_t *mxfep) +{ + mxfe_txbuf_t *txb; + mxfe_desc_t *tmdp; + + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + + /* setup frame -- must be at head of list -- guaranteed by caller! */ + ASSERT(mxfep->mxfe_txsend == 0); + + txb = mxfep->mxfe_txbufs[0]; + tmdp = &mxfep->mxfe_txdescp[0]; + + bzero(txb->txb_buf, MXFE_SETUP_LEN); + + /* program the unicast address */ + txb->txb_buf[156] = mxfep->mxfe_curraddr[0]; + txb->txb_buf[157] = mxfep->mxfe_curraddr[1]; + txb->txb_buf[160] = mxfep->mxfe_curraddr[2]; + txb->txb_buf[161] = mxfep->mxfe_curraddr[3]; + txb->txb_buf[164] = mxfep->mxfe_curraddr[4]; + txb->txb_buf[165] = mxfep->mxfe_curraddr[5]; + + /* make sure that the hardware can see it */ + SYNCTXBUF(txb, MXFE_SETUP_LEN, DDI_DMA_SYNC_FORDEV); + + PUTTXDESC(mxfep, tmdp->desc_control, + TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE | TXCTL_HASHPERF | + TXCTL_SETUP | MXFE_SETUP_LEN); + + PUTTXDESC(mxfep, tmdp->desc_buffer1, txb->txb_paddr); + PUTTXDESC(mxfep, tmdp->desc_buffer2, 0); + PUTTXDESC(mxfep, tmdp->desc_status, TXSTAT_OWN); + + /* sync the descriptor out to the device */ + SYNCTXDESC(mxfep, 0, DDI_DMA_SYNC_FORDEV); + + /* + * wake up the chip ... inside the lock to protect against DR suspend, + * etc. + */ + PUTCSR(mxfep, CSR_TDR, 0); + mxfep->mxfe_txsend++; + mxfep->mxfe_txavail--; + + /* + * Program promiscuous mode. + */ + if (mxfep->mxfe_promisc) { + SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); + } else { + CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); + } +} + +boolean_t +mxfe_send(mxfe_t *mxfep, mblk_t *mp) +{ + size_t len; + mxfe_txbuf_t *txb; + mxfe_desc_t *tmd; + uint32_t control; + int txsend; + + ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); + ASSERT(mp != NULL); + + len = msgsize(mp); + if (len > ETHERVLANMTU) { + DBG(DXMIT, "frame too long: %d", len); + mxfep->mxfe_macxmt_errors++; + freemsg(mp); + return (B_TRUE); + } + + if (mxfep->mxfe_txavail < MXFE_TXRECLAIM) + mxfe_reclaim(mxfep); + + if (mxfep->mxfe_txavail == 0) { + /* no more tmds */ + mxfep->mxfe_wantw = B_TRUE; + /* enable TX interrupt */ + mxfe_enableinterrupts(mxfep); + return (B_FALSE); + } + + txsend = mxfep->mxfe_txsend; + + /* + * For simplicity, we just do a copy into a preallocated + * DMA buffer. + */ + + txb = mxfep->mxfe_txbufs[txsend]; + mcopymsg(mp, txb->txb_buf); /* frees mp! */ + + /* + * Statistics. + */ + mxfep->mxfe_opackets++; + mxfep->mxfe_obytes += len; + if (txb->txb_buf[0] & 0x1) { + if (bcmp(txb->txb_buf, mxfe_broadcast, ETHERADDRL) != 0) + mxfep->mxfe_multixmt++; + else + mxfep->mxfe_brdcstxmt++; + } + + /* note len is already known to be a small unsigned */ + control = len | TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE; + + if (txsend == (MXFE_TXRING - 1)) + control |= TXCTL_ENDRING; + + tmd = &mxfep->mxfe_txdescp[txsend]; + + SYNCTXBUF(txb, len, DDI_DMA_SYNC_FORDEV); + PUTTXDESC(mxfep, tmd->desc_control, control); + PUTTXDESC(mxfep, tmd->desc_buffer1, txb->txb_paddr); + PUTTXDESC(mxfep, tmd->desc_buffer2, 0); + PUTTXDESC(mxfep, tmd->desc_status, TXSTAT_OWN); + /* sync the descriptor out to the device */ + SYNCTXDESC(mxfep, txsend, DDI_DMA_SYNC_FORDEV); + + /* + * Note the new values of txavail and txsend. + */ + mxfep->mxfe_txavail--; + mxfep->mxfe_txsend = (txsend + 1) % MXFE_TXRING; + + /* + * It should never, ever take more than 5 seconds to drain + * the ring. If it happens, then we are stuck! + */ + mxfep->mxfe_txstall_time = gethrtime() + (5 * 1000000000ULL); + + /* + * wake up the chip ... inside the lock to protect against DR suspend, + * etc. + */ + PUTCSR(mxfep, CSR_TDR, 0); + + return (B_TRUE); +} + +/* + * Reclaim buffers that have completed transmission. + */ +void +mxfe_reclaim(mxfe_t *mxfep) +{ + mxfe_desc_t *tmdp; + + while (mxfep->mxfe_txavail != MXFE_TXRING) { + uint32_t status; + uint32_t control; + int index = mxfep->mxfe_txreclaim; + + tmdp = &mxfep->mxfe_txdescp[index]; + + /* sync it before we read it */ + SYNCTXDESC(mxfep, index, DDI_DMA_SYNC_FORKERNEL); + + control = GETTXDESC(mxfep, tmdp->desc_control); + status = GETTXDESC(mxfep, tmdp->desc_status); + + if (status & TXSTAT_OWN) { + /* chip is still working on it, we're done */ + break; + } + + mxfep->mxfe_txavail++; + mxfep->mxfe_txreclaim = (index + 1) % MXFE_TXRING; + + /* in the most common successful case, all bits are clear */ + if (status == 0) + continue; + + if (((control & TXCTL_SETUP) != 0) || + ((control & TXCTL_LAST) == 0)) { + /* no interesting statistics here */ + continue; + } + + if (status & TXSTAT_TXERR) { + mxfep->mxfe_errxmt++; + + if (status & TXSTAT_JABBER) { + /* transmit jabber timeout */ + mxfep->mxfe_macxmt_errors++; + } + if (status & (TXSTAT_CARRLOST | TXSTAT_NOCARR)) { + mxfep->mxfe_carrier_errors++; + } + if (status & TXSTAT_UFLOW) { + mxfep->mxfe_underflow++; + } + if (status & TXSTAT_LATECOL) { + mxfep->mxfe_tx_late_collisions++; + } + if (status & TXSTAT_EXCOLL) { + mxfep->mxfe_ex_collisions++; + mxfep->mxfe_collisions += 16; + } + } + + if (status & TXSTAT_DEFER) { + mxfep->mxfe_defer_xmts++; + } + + /* collision counting */ + if (TXCOLLCNT(status) == 1) { + mxfep->mxfe_collisions++; + mxfep->mxfe_first_collisions++; + } else if (TXCOLLCNT(status)) { + mxfep->mxfe_collisions += TXCOLLCNT(status); + mxfep->mxfe_multi_collisions += TXCOLLCNT(status); + } + } + + if (mxfep->mxfe_txavail >= MXFE_TXRESCHED) { + if (mxfep->mxfe_wantw) { + /* + * we were able to reclaim some packets, so + * disable tx interrupts + */ + mxfep->mxfe_wantw = B_FALSE; + mxfe_enableinterrupts(mxfep); + mac_tx_update(mxfep->mxfe_mh); + } + } +} + +mblk_t * +mxfe_receive(mxfe_t *mxfep) +{ + unsigned len; + mxfe_rxbuf_t *rxb; + mxfe_desc_t *rmd; + uint32_t status; + mblk_t *mpchain, **mpp, *mp; + int head, cnt; + + mpchain = NULL; + mpp = &mpchain; + head = mxfep->mxfe_rxhead; + + /* limit the number of packets we process to a ring size */ + for (cnt = 0; cnt < MXFE_RXRING; cnt++) { + + DBG(DRECV, "receive at index %d", head); + + rmd = &mxfep->mxfe_rxdescp[head]; + rxb = mxfep->mxfe_rxbufs[head]; + + SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORKERNEL); + status = GETRXDESC(mxfep, rmd->desc_status); + if (status & RXSTAT_OWN) { + /* chip is still chewing on it */ + break; + } + + /* discard the ethernet frame checksum */ + len = RXLENGTH(status) - ETHERFCSL; + + DBG(DRECV, "recv length %d, status %x", len, status); + + if ((status & (RXSTAT_ERRS | RXSTAT_FIRST | RXSTAT_LAST)) != + (RXSTAT_FIRST | RXSTAT_LAST)) { + + mxfep->mxfe_errrcv++; + + /* + * Abnormal status bits detected, analyze further. + */ + if ((status & (RXSTAT_LAST|RXSTAT_FIRST)) != + (RXSTAT_LAST|RXSTAT_FIRST)) { + DBG(DRECV, "rx packet overspill"); + if (status & RXSTAT_FIRST) { + mxfep->mxfe_toolong_errors++; + } + } else if (status & RXSTAT_DESCERR) { + mxfep->mxfe_macrcv_errors++; + + } else if (status & RXSTAT_RUNT) { + mxfep->mxfe_runt++; + + } else if (status & RXSTAT_COLLSEEN) { + /* this should really be rx_late_collisions */ + mxfep->mxfe_macrcv_errors++; + + } else if (status & RXSTAT_DRIBBLE) { + mxfep->mxfe_align_errors++; + + } else if (status & RXSTAT_CRCERR) { + mxfep->mxfe_fcs_errors++; + + } else if (status & RXSTAT_OFLOW) { + mxfep->mxfe_overflow++; + } + } + + else if (len > ETHERVLANMTU) { + mxfep->mxfe_errrcv++; + mxfep->mxfe_toolong_errors++; + } + + /* + * At this point, the chip thinks the packet is OK. + */ + else { + mp = allocb(len + MXFE_HEADROOM, 0); + if (mp == NULL) { + mxfep->mxfe_errrcv++; + mxfep->mxfe_norcvbuf++; + goto skip; + } + + /* sync the buffer before we look at it */ + SYNCRXBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL); + mp->b_rptr += MXFE_HEADROOM; + mp->b_wptr = mp->b_rptr + len; + bcopy((char *)rxb->rxb_buf, mp->b_rptr, len); + + mxfep->mxfe_ipackets++; + mxfep->mxfe_rbytes += len; + if (status & RXSTAT_GROUP) { + if (bcmp(mp->b_rptr, mxfe_broadcast, + ETHERADDRL) == 0) + mxfep->mxfe_brdcstrcv++; + else + mxfep->mxfe_multircv++; + } + *mpp = mp; + mpp = &mp->b_next; + } + +skip: + /* return ring entry to the hardware */ + PUTRXDESC(mxfep, rmd->desc_status, RXSTAT_OWN); + SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORDEV); + + /* advance to next RMD */ + head = (head + 1) % MXFE_RXRING; + } + + mxfep->mxfe_rxhead = head; + + return (mpchain); +} + +int +mxfe_m_stat(void *arg, uint_t stat, uint64_t *val) +{ + mxfe_t *mxfep = arg; + + mutex_enter(&mxfep->mxfe_xmtlock); + if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == MXFE_RUNNING) + mxfe_reclaim(mxfep); + mutex_exit(&mxfep->mxfe_xmtlock); + + switch (stat) { + case MAC_STAT_IFSPEED: + *val = mxfep->mxfe_ifspeed; + break; + + case MAC_STAT_MULTIRCV: + *val = mxfep->mxfe_multircv; + break; + + case MAC_STAT_BRDCSTRCV: + *val = mxfep->mxfe_brdcstrcv; + break; + + case MAC_STAT_MULTIXMT: + *val = mxfep->mxfe_multixmt; + break; + + case MAC_STAT_BRDCSTXMT: + *val = mxfep->mxfe_brdcstxmt; + break; + + case MAC_STAT_IPACKETS: + *val = mxfep->mxfe_ipackets; + break; + + case MAC_STAT_RBYTES: + *val = mxfep->mxfe_rbytes; + break; + + case MAC_STAT_OPACKETS: + *val = mxfep->mxfe_opackets; + break; + + case MAC_STAT_OBYTES: + *val = mxfep->mxfe_obytes; + break; + + case MAC_STAT_NORCVBUF: + *val = mxfep->mxfe_norcvbuf; + break; + + case MAC_STAT_NOXMTBUF: + *val = mxfep->mxfe_noxmtbuf; + break; + + case MAC_STAT_COLLISIONS: + *val = mxfep->mxfe_collisions; + break; + + case MAC_STAT_IERRORS: + *val = mxfep->mxfe_errrcv; + break; + + case MAC_STAT_OERRORS: + *val = mxfep->mxfe_errxmt; + break; + + case ETHER_STAT_LINK_DUPLEX: + *val = mxfep->mxfe_duplex; + break; + + case ETHER_STAT_ALIGN_ERRORS: + *val = mxfep->mxfe_align_errors; + break; + + case ETHER_STAT_FCS_ERRORS: + *val = mxfep->mxfe_fcs_errors; + break; + + case ETHER_STAT_SQE_ERRORS: + *val = mxfep->mxfe_sqe_errors; + break; + + case ETHER_STAT_DEFER_XMTS: + *val = mxfep->mxfe_defer_xmts; + break; + + case ETHER_STAT_FIRST_COLLISIONS: + *val = mxfep->mxfe_first_collisions; + break; + + case ETHER_STAT_MULTI_COLLISIONS: + *val = mxfep->mxfe_multi_collisions; + break; + + case ETHER_STAT_TX_LATE_COLLISIONS: + *val = mxfep->mxfe_tx_late_collisions; + break; + + case ETHER_STAT_EX_COLLISIONS: + *val = mxfep->mxfe_ex_collisions; + break; + + case ETHER_STAT_MACXMT_ERRORS: + *val = mxfep->mxfe_macxmt_errors; + break; + + case ETHER_STAT_CARRIER_ERRORS: + *val = mxfep->mxfe_carrier_errors; + break; + + case ETHER_STAT_TOOLONG_ERRORS: + *val = mxfep->mxfe_toolong_errors; + break; + + case ETHER_STAT_MACRCV_ERRORS: + *val = mxfep->mxfe_macrcv_errors; + break; + + case MAC_STAT_OVERFLOWS: + *val = mxfep->mxfe_overflow; + break; + + case MAC_STAT_UNDERFLOWS: + *val = mxfep->mxfe_underflow; + break; + + case ETHER_STAT_TOOSHORT_ERRORS: + *val = mxfep->mxfe_runt; + break; + + case ETHER_STAT_JABBER_ERRORS: + *val = mxfep->mxfe_jabber; + break; + + case ETHER_STAT_CAP_100T4: + *val = mxfep->mxfe_bmsr & MII_STATUS_100_BASE_T4 ? 1 : 0; + break; + + case ETHER_STAT_ADV_CAP_100T4: + *val = mxfep->mxfe_adv_100T4; + break; + + case ETHER_STAT_LP_CAP_100T4: + *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_T4) ? 1 : 0; + break; + + case ETHER_STAT_CAP_100FDX: + *val = mxfep->mxfe_bmsr & MII_STATUS_100_BASEX_FD ? 1 : 0; + break; + + case ETHER_STAT_CAP_100HDX: + *val = mxfep->mxfe_bmsr & MII_STATUS_100_BASEX ? 1 : 0; + break; + + case ETHER_STAT_CAP_10FDX: + *val = mxfep->mxfe_bmsr & MII_STATUS_10_FD ? 1 : 0; + break; + + case ETHER_STAT_CAP_10HDX: + *val = mxfep->mxfe_bmsr & MII_STATUS_10 ? 1 : 0; + break; + + case ETHER_STAT_CAP_AUTONEG: + *val = mxfep->mxfe_bmsr & MII_STATUS_CANAUTONEG ? 1 : 0; + break; + + case ETHER_STAT_LINK_AUTONEG: + *val = ((mxfep->mxfe_adv_aneg != 0) && + ((mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) != 0)); + break; + + case ETHER_STAT_ADV_CAP_100FDX: + *val = mxfep->mxfe_adv_100fdx; + break; + + case ETHER_STAT_ADV_CAP_100HDX: + *val = mxfep->mxfe_adv_100hdx; + break; + + case ETHER_STAT_ADV_CAP_10FDX: + *val = mxfep->mxfe_adv_10fdx; + break; + + case ETHER_STAT_ADV_CAP_10HDX: + *val = mxfep->mxfe_adv_10hdx; + break; + + case ETHER_STAT_ADV_CAP_AUTONEG: + *val = mxfep->mxfe_adv_aneg; + break; + + case ETHER_STAT_LP_CAP_100FDX: + *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX_FD) ? 1 : 0; + break; + + case ETHER_STAT_LP_CAP_100HDX: + *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX) ? 1 : 0; + break; + + case ETHER_STAT_LP_CAP_10FDX: + *val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T_FD) ? 1 : 0; + break; + + case ETHER_STAT_LP_CAP_10HDX: + *val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T) ? 1 : 0; + break; + + case ETHER_STAT_LP_CAP_AUTONEG: + *val = (mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) ? 1 : 0; + break; + + case ETHER_STAT_XCVR_ADDR: + *val = mxfep->mxfe_phyaddr; + break; + + case ETHER_STAT_XCVR_ID: + *val = mxfep->mxfe_phyid; + break; + + case ETHER_STAT_XCVR_INUSE: + *val = mxfep->mxfe_phyinuse; + break; + + default: + return (ENOTSUP); + } + return (0); +} + +/* + * NDD support. + */ +mxfe_nd_t * +mxfe_ndfind(mxfe_t *mxfep, char *name) +{ + mxfe_nd_t *ndp; + + for (ndp = mxfep->mxfe_ndp; ndp != NULL; ndp = ndp->nd_next) { + if (strcmp(name, ndp->nd_name) == 0) { + break; + } + } + return (ndp); +} + +void +mxfe_ndadd(mxfe_t *mxfep, char *name, mxfe_nd_pf_t get, mxfe_nd_pf_t set, + intptr_t arg1, intptr_t arg2) +{ + mxfe_nd_t *newndp; + mxfe_nd_t **ndpp; + + newndp = (mxfe_nd_t *)kmem_alloc(sizeof (mxfe_nd_t), KM_SLEEP); + newndp->nd_next = NULL; + newndp->nd_name = name; + newndp->nd_get = get; + newndp->nd_set = set; + newndp->nd_arg1 = arg1; + newndp->nd_arg2 = arg2; + + /* seek to the end of the list */ + for (ndpp = &mxfep->mxfe_ndp; *ndpp; ndpp = &(*ndpp)->nd_next) { + } + + *ndpp = newndp; +} + +void +mxfe_ndempty(mblk_t *mp) +{ + while (mp != NULL) { + mp->b_rptr = mp->b_datap->db_base; + mp->b_wptr = mp->b_rptr; + mp = mp->b_cont; + } +} + +void +mxfe_ndget(mxfe_t *mxfep, queue_t *wq, mblk_t *mp) +{ + mblk_t *nmp = mp->b_cont; + mxfe_nd_t *ndp; + int rv; + char name[128]; + + /* assumption, name will fit in first mblk of chain */ + if ((nmp == NULL) || (nmp->b_wptr <= nmp->b_rptr)) { + miocnak(wq, mp, 0, EINVAL); + return; + } + + if (mxfe_ndparselen(nmp) >= sizeof (name)) { + miocnak(wq, mp, 0, EINVAL); + return; + } + mxfe_ndparsestring(nmp, name, sizeof (name)); + + /* locate variable */ + if ((ndp = mxfe_ndfind(mxfep, name)) == NULL) { + miocnak(wq, mp, 0, EINVAL); + return; + } + + /* locate get callback */ + if (ndp->nd_get == NULL) { + miocnak(wq, mp, 0, EACCES); + return; + } + + /* clear the result buffer */ + mxfe_ndempty(nmp); + + rv = (*ndp->nd_get)(mxfep, nmp, ndp); + if (rv == 0) { + /* add final null bytes */ + rv = mxfe_ndaddbytes(nmp, "\0", 1); + } + + if (rv == 0) { + miocack(wq, mp, msgsize(nmp), 0); + } else { + miocnak(wq, mp, 0, rv); + } +} + +void +mxfe_ndset(mxfe_t *mxfep, queue_t *wq, mblk_t *mp) +{ + struct iocblk *iocp = (void *)mp->b_rptr; + mblk_t *nmp = mp->b_cont; + mxfe_nd_t *ndp; + int rv; + char name[128]; + + /* enforce policy */ + if ((rv = priv_getbyname(PRIV_SYS_NET_CONFIG, 0)) < 0) { + /* priv_getbyname returns a negative errno */ + miocnak(wq, mp, 0, -rv); + return; + } + if ((rv = priv_policy(iocp->ioc_cr, rv, B_FALSE, EPERM, NULL)) != 0) { + miocnak(wq, mp, 0, rv); + return; + } + + /* assumption, name will fit in first mblk of chain */ + if ((nmp == NULL) || (nmp->b_wptr <= nmp->b_rptr)) { + miocnak(wq, mp, 0, EINVAL); + return; + } + + if (mxfe_ndparselen(nmp) >= sizeof (name)) { + miocnak(wq, mp, 0, EINVAL); + return; + } + mxfe_ndparsestring(nmp, name, sizeof (name)); + + /* locate variable */ + if ((ndp = mxfe_ndfind(mxfep, name)) == NULL) { + miocnak(wq, mp, 0, EINVAL); + return; + } + + /* locate set callback */ + if (ndp->nd_set == NULL) { + miocnak(wq, mp, 0, EACCES); + return; + } + + rv = (*ndp->nd_set)(mxfep, nmp, ndp); + + if (rv == 0) { + miocack(wq, mp, 0, 0); + } else { + miocnak(wq, mp, 0, rv); + } +} + +int +mxfe_ndaddbytes(mblk_t *mp, char *bytes, int cnt) +{ + int index; + + for (index = 0; index < cnt; index++) { + while (mp && (mp->b_wptr >= DB_LIM(mp))) { + mp = mp->b_cont; + } + if (mp == NULL) { + return (ENOSPC); + } + *(mp->b_wptr) = *bytes; + mp->b_wptr++; + bytes++; + } + return (0); +} + +int +mxfe_ndaddstr(mblk_t *mp, char *str, int addnull) +{ + /* store the string, plus the terminating null */ + return (mxfe_ndaddbytes(mp, str, strlen(str) + (addnull ? 1 : 0))); +} + +int +mxfe_ndparselen(mblk_t *mp) +{ + int len = 0; + int done = 0; + uchar_t *ptr; + + while (mp && !done) { + for (ptr = mp->b_rptr; ptr < mp->b_wptr; ptr++) { + if (!(*ptr)) { + done = 1; + break; + } + len++; + } + mp = mp->b_cont; + } + return (len); +} + +int +mxfe_ndparseint(mblk_t *mp) +{ + int done = 0; + int val = 0; + while (mp && !done) { + while (mp->b_rptr < mp->b_wptr) { + uchar_t ch = *(mp->b_rptr); + mp->b_rptr++; + if ((ch >= '0') && (ch <= '9')) { + val *= 10; + val += ch - '0'; + } else if (ch == 0) { + return (val); + } else { + /* parse error, put back rptr */ + mp->b_rptr--; + return (val); + } + } + mp = mp->b_cont; + } + return (val); +} + +void +mxfe_ndparsestring(mblk_t *mp, char *buf, int maxlen) +{ + int done = 0; + int len = 0; + + /* ensure null termination */ + buf[maxlen - 1] = 0; + while (mp && !done) { + while (mp->b_rptr < mp->b_wptr) { + char ch = *((char *)mp->b_rptr); + mp->b_rptr++; + buf[len++] = ch; + if ((ch == 0) || (len == maxlen)) { + return; + } + } + mp = mp->b_cont; + } +} + +int +mxfe_ndquestion(mxfe_t *mxfep, mblk_t *mp, mxfe_nd_t *ndp) +{ + for (ndp = mxfep->mxfe_ndp; ndp; ndp = ndp->nd_next) { + int rv; + char *s; + if ((rv = mxfe_ndaddstr(mp, ndp->nd_name, 0)) != 0) { + return (rv); + } + if (ndp->nd_get && ndp->nd_set) { + s = " (read and write)"; + } else if (ndp->nd_get) { + s = " (read only)"; + } else if (ndp->nd_set) { + s = " (write only)"; + } else { + s = " (no read or write)"; + } + if ((rv = mxfe_ndaddstr(mp, s, 1)) != 0) { + return (rv); + } + } + return (0); +} + +/*ARGSUSED*/ +int +mxfe_ndgetint(mxfe_t *mxfep, mblk_t *mp, mxfe_nd_t *ndp) +{ + int val; + char buf[16]; + + val = *(int *)ndp->nd_arg1; + + (void) snprintf(buf, sizeof (buf), "%d", val); + return (mxfe_ndaddstr(mp, buf, 1)); +} + +/*ARGSUSED*/ +int +mxfe_ndgetbit(mxfe_t *mxfep, mblk_t *mp, mxfe_nd_t *ndp) +{ + unsigned val; + unsigned mask; + + val = *(unsigned *)ndp->nd_arg1; + mask = (unsigned)ndp->nd_arg2; + + return (mxfe_ndaddstr(mp, val & mask ? "1" : "0", 1)); +} + +int +mxfe_ndsetadv(mxfe_t *mxfep, mblk_t *mp, mxfe_nd_t *ndp) +{ + unsigned *ptr = (unsigned *)ndp->nd_arg1; + unsigned oldval, newval; + + newval = mxfe_ndparseint(mp) ? 1 : 0; + + mutex_enter(&mxfep->mxfe_intrlock); + mutex_enter(&mxfep->mxfe_xmtlock); + + oldval = *ptr; + if (oldval != newval) { + *ptr = newval; + if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == + MXFE_RUNNING) { + /* + * This re-initializes the phy, but it also + * restarts transmit and receive rings. + * Needless to say, changing the link + * parameters is destructive to traffic in + * progress. + */ + mxfe_resetall(mxfep); + } + } + mutex_exit(&mxfep->mxfe_xmtlock); + mutex_exit(&mxfep->mxfe_intrlock); + + return (0); +} + +void +mxfe_ndfini(mxfe_t *mxfep) +{ + mxfe_nd_t *ndp; + + while ((ndp = mxfep->mxfe_ndp) != NULL) { + mxfep->mxfe_ndp = ndp->nd_next; + kmem_free(ndp, sizeof (mxfe_nd_t)); + } +} + +void +mxfe_ndinit(mxfe_t *mxfep) +{ + mxfe_ndadd(mxfep, "?", mxfe_ndquestion, NULL, 0, 0); + mxfe_ndadd(mxfep, "link_status", mxfe_ndgetint, NULL, + (intptr_t)&mxfep->mxfe_linkup, 0); + mxfe_ndadd(mxfep, "link_speed", mxfe_ndgetint, NULL, + (intptr_t)&mxfep->mxfe_ifspeed, 0); + mxfe_ndadd(mxfep, "link_duplex", mxfe_ndgetint, NULL, + (intptr_t)&mxfep->mxfe_duplex, 0); + mxfe_ndadd(mxfep, "adv_autoneg_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_aneg, 0); + mxfe_ndadd(mxfep, "adv_100T4_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_100T4, 0); + mxfe_ndadd(mxfep, "adv_100fdx_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_100fdx, 0); + mxfe_ndadd(mxfep, "adv_100hdx_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_100hdx, 0); + mxfe_ndadd(mxfep, "adv_10fdx_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_10fdx, 0); + mxfe_ndadd(mxfep, "adv_10hdx_cap", mxfe_ndgetint, mxfe_ndsetadv, + (intptr_t)&mxfep->mxfe_adv_10hdx, 0); + mxfe_ndadd(mxfep, "autoneg_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_CANAUTONEG); + mxfe_ndadd(mxfep, "100T4_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_100_BASE_T4); + mxfe_ndadd(mxfep, "100fdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_100_BASEX_FD); + mxfe_ndadd(mxfep, "100hdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_100_BASEX); + mxfe_ndadd(mxfep, "10fdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_10_FD); + mxfe_ndadd(mxfep, "10hdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_bmsr, MII_STATUS_10); + mxfe_ndadd(mxfep, "lp_autoneg_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_aner, MII_AN_EXP_LPCANAN); + mxfe_ndadd(mxfep, "lp_100T4_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_anlpar, MII_ABILITY_100BASE_T4); + mxfe_ndadd(mxfep, "lp_100fdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_anlpar, MII_ABILITY_100BASE_TX_FD); + mxfe_ndadd(mxfep, "lp_100hdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_anlpar, MII_ABILITY_100BASE_TX); + mxfe_ndadd(mxfep, "lp_10fdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_anlpar, MII_ABILITY_10BASE_T_FD); + mxfe_ndadd(mxfep, "lp_10hdx_cap", mxfe_ndgetbit, NULL, + (intptr_t)&mxfep->mxfe_anlpar, MII_ABILITY_10BASE_T); +} + +/* + * Debugging and error reporting. + */ +void +mxfe_error(dev_info_t *dip, char *fmt, ...) +{ + va_list ap; + char buf[256]; + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + if (dip) { + cmn_err(CE_WARN, "%s%d: %s", + ddi_driver_name(dip), ddi_get_instance(dip), buf); + } else { + cmn_err(CE_WARN, "mxfe: %s", buf); + } +} + +#ifdef DEBUG + +void +mxfe_dprintf(mxfe_t *mxfep, const char *func, int level, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (mxfe_debug & level) { + char tag[64]; + char buf[256]; + + if (mxfep && mxfep->mxfe_dip) { + (void) snprintf(tag, sizeof (tag), + "%s%d", ddi_driver_name(mxfep->mxfe_dip), + ddi_get_instance(mxfep->mxfe_dip)); + } else { + (void) snprintf(tag, sizeof (tag), "mxfe"); + } + + (void) snprintf(buf, sizeof (buf), "%s: %s: %s\n", tag, + func, fmt); + + vcmn_err(CE_CONT, buf, ap); + } + va_end(ap); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mxfe/mxfe.h Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,270 @@ +/* + * Solaris driver for ethernet cards based on the Macronix 98715 + * + * Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MXFE_H +#define _MXFE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * These are conveniently defined to have the same values + * as are used by the NDD utility, which is an undocumented + * interface. YMMV. + */ +#define NDIOC ('N' << 8) +#define NDIOC_GET (NDIOC|0) +#define NDIOC_SET (NDIOC|1) + +/* + * Registers and values are here, becuase they can be exported to userland + * via the MXFEIOC_GETCSR and friends ioctls. These are private to this + * driver and the bundled diagnostic utility, and should not be used by + * end user application programs. + */ + +/* + * MXFE register definitions. + */ +/* PCI configuration registers */ +#define PCI_VID 0x00 /* Loaded vendor ID */ +#define PCI_DID 0x02 /* Loaded device ID */ +#define PCI_CMD 0x04 /* Configuration command register */ +#define PCI_STAT 0x06 /* Configuration status register */ +#define PCI_RID 0x08 /* Revision ID */ +#define PCI_CLS 0x0c /* Cache line size */ +#define PCI_SVID 0x2c /* Subsystem vendor ID */ +#define PCI_SSID 0x2e /* Subsystem ID */ +#define PCI_MINGNT 0x3e /* Minimum Grant */ +#define PCI_MAXLAT 0x3f /* Maximum latency */ + +/* + * Bits for PCI command register. + */ +#define PCI_CMD_MWIE 0x0010 /* memory write-invalidate enable */ +#define PCI_CMD_BME 0x0004 /* bus master enable */ +#define PCI_CMD_MAE 0x0002 /* memory access enable */ +#define PCI_CMD_IOE 0x0001 /* I/O access enable */ + +/* Ordinary control/status registers */ +#define CSR_PAR 0x00 /* PCI access register */ +#define CSR_TDR 0x08 /* Transmit demand register */ +#define CSR_RDR 0x10 /* Receive demand register */ +#define CSR_RDB 0x18 /* Receive descriptor base address */ +#define CSR_TDB 0x20 /* Transmit descriptor base address */ +#define CSR_SR 0x28 /* Status register */ +#define CSR_NAR 0x30 /* Network access register */ +#define CSR_IER 0x38 /* Interrupt enable register */ +#define CSR_LPC 0x40 /* Lost packet counter */ +#define CSR_SPR 0x48 /* Serial port register */ +#define CSR_TIMER 0x58 /* Timer */ +#define CSR_TSTAT 0x60 /* 10Base-T status */ +#define CSR_SIA 0x68 /* SIA reset register */ +#define CSR_TCTL 0x70 /* 10Base-T control */ +#define CSR_WTMR 0x78 /* Watchdog timer */ +#define CSR_MXMAGIC 0x80 /* MXIC magic register */ +#define CSR_PMCSR 0x90 /* Power Management Command and Status */ +#define CSR_TXBR 0x9c /* Transmit burst counter/time-out register */ +#define CSR_FROM 0xa0 /* Flash(boot) ROM port */ +#define CSR_ACOMP 0xa0 /* Autocompensation */ +#define CSR_FLOW 0xa8 /* Flow control (newer parts only) */ + +/* + * Bits for PCI access register. + */ +#define PAR_RESET 0x00000001U /* Reset the entire chip */ +#define PAR_MWIE 0x01000000U /* PCI memory-write-invalidate */ +#define PAR_MRLE 0x00800000U /* PCI memory-read-line */ +#define PAR_MRME 0x00200000U /* PCI memory-read-multiple */ +#define PAR_BAR 0x00000002U /* Bus arbitration */ +#define PAR_DESCSKIP 0x0000007cU /* Descriptor skip length in DW */ +#define PAR_BIGENDIAN 0x00000080U /* Use big endian data buffers */ +#define PAR_TXAUTOPOLL 0x00060000U /* Programmable TX autopoll interval */ +#define PAR_CALIGN_NONE 0x00000000U /* No cache alignment */ +#define PAR_CALIGN_8 0x00004000U /* 8 DW cache alignment */ +#define PAR_CALIGN_16 0x00008000U /* 16 DW cache alignment */ +#define PAR_CALIGN_32 0x0000c000U /* 32 DW cache alignment */ +#define PAR_BURSTLEN 0x00003F00U /* Programmable burst length */ +#define PAR_BURSTUNL 0x00000000U /* Unlimited burst length */ +#define PAR_BURST_1 0x00000100U /* 1 DW burst length */ +#define PAR_BURST_2 0x00000200U /* 2 DW burst length */ +#define PAR_BURST_4 0x00000400U /* 4 DW burst length */ +#define PAR_BURST_8 0x00000800U /* 8 DW burst length */ +#define PAR_BURST_16 0x00001000U /* 16 DW burst length */ +#define PAR_BURST_32 0x00002000U /* 32 DW burst length */ + +/* + * Bits for status register. Interrupt bits are also used by + * the interrupt enable register. + */ +#define SR_BERR_TYPE 0x03800000U /* bus error type */ +#define SR_BERR_PARITY 0x00000000U /* parity error */ +#define SR_BERR_TARGET_ABORT 0x01000000U /* target abort */ +#define SR_BERR_MASTER_ABORT 0x00800000U /* master abort */ +#define SR_TX_STATE 0x00700000U /* transmit state */ +#define SR_RX_STATE 0x000E0000U /* transmit state */ +#define INT_100LINK 0x08000000U /* 100 Base-T link */ +#define INT_NORMAL 0x00010000U /* normal interrupt */ +#define INT_ABNORMAL 0x00008000U /* abnormal interrupt */ +#define INT_EARLYRX 0x00004000U /* early receive interrupt */ +#define INT_BUSERR 0x00002000U /* fatal bus error interrupt */ +#define INT_10LINK 0x00001000U /* 10 Base-T link */ +#define INT_TIMER 0x00000800U /* onboard timer interrupt */ +#define INT_EARLYTX 0x00000400U /* early transmit interrupt */ +#define INT_RXJABBER 0x00000200U /* receive watchdog timeout */ +#define INT_RXSTOPPED 0x00000100U /* receive stopped */ +#define INT_RXNOBUF 0x00000080U /* no rcv descriptor */ +#define INT_RXOK 0x00000040U /* rcv complete interrupt */ +#define INT_TXUNDERFLOW 0x00000020U /* transmit underflow */ +#define INT_ANEG 0x00000010U /* autonegotiation */ +#define INT_TXJABBER 0x00000008U /* transmit jabber timeout */ +#define INT_TXNOBUF 0x00000004U /* no xmt descriptor */ +#define INT_TXSTOPPED 0x00000002U /* transmit stopped */ +#define INT_TXOK 0x00000001U /* transmit ok interrupt */ + +#define INT_NONE 0x00000000U /* no interrupts */ +#define INT_WANTED (INT_BUSERR | INT_RXJABBER | \ + INT_RXOK | INT_TXUNDERFLOW | \ + INT_RXNOBUF | INT_TXJABBER | \ + INT_RXSTOPPED | INT_TXSTOPPED | \ + INT_TIMER | \ + INT_ABNORMAL | INT_NORMAL) + +#define INT_LINKSTATUS (INT_ANEG | INT_100LINK | INT_10LINK) +#define INT_ALL (INT_WANTED | INT_TXOK | \ + INT_TXNOBUF | INT_LINKSTATUS) + +/* + * Bits for network access register. + */ +#define NAR_TX_ENABLE 0x00002000U /* Enable transmit */ +#define NAR_RX_MULTI 0x00000080U /* Receive all multicast packets */ +#define NAR_RX_PROMISC 0x00000040U /* Receive any good packet */ +#define NAR_RX_BAD 0x00000008U /* Pass bad packets */ +#define NAR_RX_HO 0x00000004U /* Hash only receive */ +#define NAR_RX_ENABLE 0x00000002U /* Enable receive */ +#define NAR_RX_HP 0x00000001U /* Hash perfect receive */ +#define NAR_TR 0x0000c000U /* Transmit threshold mask */ +#define NAR_TR_72 0x00000000U /* 72 B (128 @ 100Mbps) tx thresh */ +#define NAR_TR_96 0x00004000U /* 96 B (256 @ 100Mbps) tx thresh */ +#define NAR_TR_128 0x00008000U /* 128 B (512 @ 100Mbps) tx thresh */ +#define NAR_TR_160 0x0000c000U /* 160 B (1K @ 100Mbsp) tx thresh */ +#define NAR_SCR 0x01000000U /* scrambler mode */ +#define NAR_PCS 0x00800000U /* set for forced 100 mbit */ +#define NAR_SPEED 0x00400000U /* transmit threshold, set for 10bt */ +#define NAR_SF 0x00200000U /* store and forward */ +#define NAR_HBD 0x00080000U /* Disable SQE heartbeat */ +#define NAR_COE 0x00020000U /* collision offset enable */ +#define NAR_PORTSEL 0x00040000U /* 1 = 100 mbit */ +#define NAR_FDX 0x00000200U /* 1 = full duplex */ + +/* + * Bits for lost packet counter. + */ +#define LPC_COUNT 0x0000FFFFU /* Count of missed frames */ +#define LPC_OFLOW 0x00010000U /* Counter overflow bit */ + +/* + * Bits for CSR_SPR (MII and SROM access) + */ +#define SPR_MII_DIN 0x00080000U /* MII data input */ +#define SPR_MII_CTRL 0x00040000U /* MII management control, 1=read */ +#define SPR_MII_DOUT 0x00020000U /* MII data output */ +#define SPR_MII_CLOCK 0x00010000U /* MII data clock */ +#define SPR_SROM_READ 0x00004000U /* Serial EEPROM read control */ +#define SPR_SROM_WRITE 0x00002000U /* Serial EEPROM write control */ +#define SPR_SROM_SEL 0x00000800U /* Serial EEPROM select */ +#define SPR_SROM_DOUT 0x00000008U /* Serial EEPROM data out */ +#define SPR_SROM_DIN 0x00000004U /* Serial EEPROM data in */ +#define SPR_SROM_CLOCK 0x00000002U /* Serial EEPROM clock */ +#define SPR_SROM_CHIP 0x00000001U /* Serial EEPROM chip select */ +#define SROM_ENADDR 0x70 /* Ethernet address pointer! */ +#define SROM_READCMD 0x6 /* command to read SROM */ + +/* + * Bits for CSR_TIMER + */ +#define TIMER_LOOP 0x00010000U /* continuous operating mode */ +#define TIMER_USEC 204 /* usecs per timer count */ + +/* + * Bits for TSTAT + */ +#define TSTAT_LPC 0xFFFF0000U /* link partner's code word */ +#define TSTAT_LPN 0x00008000U /* link partner supports nway */ +#define TSTAT_ANS 0x00007000U /* autonegotiation state mask */ +#define TSTAT_TRF 0x00000800U /* transmit remote fault */ +#define TSTAT_APS 0x00000008U /* autopolarity state */ +#define TSTAT_10F 0x00000004U /* 10Base-T link failure */ +#define TSTAT_100F 0x00000002U /* 100Base-T link failure */ +#define TSTAT_ANS_DIS 0x00000000U /* autonegotiation disabled */ +#define TSTAT_ANS_OK 0x00005000U /* autonegotiation complete */ +#define TSTAT_ANS_START 0x00001000U /* restart autonegotiation */ + +/* macro to convert TSTAT link partner's code word to MII equivalents */ +#define TSTAT_LPAR(x) ((x & TSTAT_LPC) >> 16) + +/* + * Bits for SIA reset + */ +#define SIA_RESET 0x00000001U /* reset 100 PHY */ +#define SIA_NRESET 0x00000002U /* reset NWay */ + +/* + * Bits for TCTL + */ +#define TCTL_PAUSE 0x00080000U /* Pause enable */ +#define TCTL_100BT4 0x00040000U /* 100 BaseT4 enable */ +#define TCTL_100FDX 0x00020000U /* 100 BaseT fdx enable */ +#define TCTL_100HDX 0x00010000U /* 100 BaseT hdx enable */ +#define TCTL_LTE 0x00001000U /* link test enable */ +#define TCTL_RSQ 0x00000100U /* receive squelch enable */ +#define TCTL_ANE 0x00000080U /* autoneg. enable */ +#define TCTL_HDX 0x00000040U /* half-duplex enable */ +#define TCTL_PWR 0x00000004U /* supply power to 10BaseT */ + +/* + * Bits for flow control + */ +#define FLOW_TMVAL 0xffff0000U /* flow timer value */ +#define FLOW_TEST 0x00008000U /* test flow control timer */ +#define FLOW_RESTART 0x00004000U /* re-start mode */ +#define FLOW_RESTOP 0x00002000U /* re-stop mode */ +#define FLOW_TXFCEN 0x00001000U /* tx flow control enable */ +#define FLOW_RXFCEN 0x00000800U /* rx flow control enable */ +#define FLOW_RUFCEN 0x00000400U /* send pause when rxnobuf */ +#define FLOW_STOPTX 0x00000200U /* tx flow status */ +#define FLOW_REJECTFC 0x00000100U /* abort rx flow when set */ +#define FLOW_RXFCTH1 0x00000080U /* rx flow threshold 1 */ +#define FLOW_RXFCTH0 0x00000040U /* rx flow threshold 0 */ +#define FLOW_NFCEN 0x00000020U /* accept nway flow control */ + + +#endif /* _MXFE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mxfe/mxfeimpl.h Fri Aug 31 17:00:55 2007 -0700 @@ -0,0 +1,391 @@ +/* + * Solaris driver for ethernet cards based on the Macronix 98715 + * + * Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MXFEIMPL_H +#define _MXFEIMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This entire file is private to the MXFE driver. + */ + +#ifdef _KERNEL + +/* + * Compile time tunables. + */ +#define MXFE_TXRING 128 /* number of xmt buffers */ +#define MXFE_RXRING 256 /* number of rcv buffers */ +#define MXFE_TXRECLAIM 32 /* when to reclaim tx buffers (txavail) */ +#define MXFE_TXRESCHED 120 /* when to resched (txavail) */ +#define MXFE_LINKTIMER 5000 /* how often we check link state (msec) */ +#define MXFE_HEADROOM 34 /* headroom in packet (should be 2 modulo 4) */ + +/* + * Constants, do not change. The bufsize is setup to make sure it comes + * in at a whole number of cache lines, even for 32-long-word aligned + * caches. + */ +#define MXFE_BUFSZ (1664) /* big enough for a vlan frame */ +#define MXFE_SETUP_LEN 192 /* size of a setup frame */ + +typedef struct mxfe mxfe_t; +typedef struct mxfe_card mxfe_card_t; +typedef struct mxfe_nd mxfe_nd_t; +typedef struct mxfe_rxbuf mxfe_rxbuf_t; +typedef struct mxfe_txbuf mxfe_txbuf_t; +typedef struct mxfe_desc mxfe_desc_t; +typedef int (*mxfe_nd_pf_t)(mxfe_t *, mblk_t *, mxfe_nd_t *); + +struct mxfe_card { + uint16_t card_venid; /* PCI vendor id */ + uint16_t card_devid; /* PCI device id */ + uint16_t card_revid; /* PCI revision id */ + uint16_t card_revmask; + char *card_cardname; /* Description of the card */ + unsigned card_model; /* Card specific flags */ +}; + +struct mxfe_nd { + mxfe_nd_t *nd_next; + char *nd_name; + mxfe_nd_pf_t nd_get; + mxfe_nd_pf_t nd_set; + intptr_t nd_arg1; + intptr_t nd_arg2; +}; + +/* + * Device instance structure, one per PCI card. + */ +struct mxfe { + dev_info_t *mxfe_dip; + mac_handle_t mxfe_mh; + mxfe_card_t *mxfe_cardp; + ushort_t mxfe_cachesize; + ushort_t mxfe_sromwidth; + int mxfe_flags; + kmutex_t mxfe_xmtlock; + kmutex_t mxfe_intrlock; + ddi_iblock_cookie_t mxfe_icookie; + + /* + * Register access. + */ + uint32_t *mxfe_regs; + ddi_acc_handle_t mxfe_regshandle; + + /* + * Receive descriptors. + */ + int mxfe_rxhead; + struct mxfe_desc *mxfe_rxdescp; + ddi_dma_handle_t mxfe_rxdesc_dmah; + ddi_acc_handle_t mxfe_rxdesc_acch; + uint32_t mxfe_rxdesc_paddr; + struct mxfe_rxbuf **mxfe_rxbufs; + + /* + * Transmit descriptors. + */ + int mxfe_txreclaim; + int mxfe_txsend; + int mxfe_txavail; + struct mxfe_desc *mxfe_txdescp; + ddi_dma_handle_t mxfe_txdesc_dmah; + ddi_acc_handle_t mxfe_txdesc_acch; + uint32_t mxfe_txdesc_paddr; + struct mxfe_txbuf **mxfe_txbufs; + hrtime_t mxfe_txstall_time; + boolean_t mxfe_wantw; + + /* + * Address management. + */ + uchar_t mxfe_curraddr[ETHERADDRL]; + boolean_t mxfe_promisc; + + /* + * Link state. + */ + int mxfe_linkstate; + int mxfe_lastifspeed; + int mxfe_lastduplex; + int mxfe_lastlinkup; + int mxfe_linkup; + int mxfe_duplex; + int mxfe_ifspeed; + boolean_t mxfe_resetting; /* no link warning */ + + /* + * NDD related support. + */ + mxfe_nd_t *mxfe_ndp; + + /* + * Transceiver stuff. + */ + int mxfe_phyaddr; + int mxfe_phyid; + int mxfe_phyinuse; + int mxfe_adv_aneg; + int mxfe_adv_100T4; + int mxfe_adv_100fdx; + int mxfe_adv_100hdx; + int mxfe_adv_10fdx; + int mxfe_adv_10hdx; + int mxfe_forcephy; + int mxfe_bmsr; + int mxfe_anlpar; + int mxfe_aner; + + /* + * Kstats. + */ + kstat_t *mxfe_intrstat; + uint64_t mxfe_ipackets; + uint64_t mxfe_opackets; + uint64_t mxfe_rbytes; + uint64_t mxfe_obytes; + uint64_t mxfe_brdcstrcv; + uint64_t mxfe_multircv; + uint64_t mxfe_brdcstxmt; + uint64_t mxfe_multixmt; + + unsigned mxfe_norcvbuf; + unsigned mxfe_noxmtbuf; + unsigned mxfe_errrcv; + unsigned mxfe_errxmt; + unsigned mxfe_missed; + unsigned mxfe_underflow; + unsigned mxfe_overflow; + unsigned mxfe_align_errors; + unsigned mxfe_fcs_errors; + unsigned mxfe_carrier_errors; + unsigned mxfe_collisions; + unsigned mxfe_ex_collisions; + unsigned mxfe_tx_late_collisions; + unsigned mxfe_defer_xmts; + unsigned mxfe_first_collisions; + unsigned mxfe_multi_collisions; + unsigned mxfe_sqe_errors; + unsigned mxfe_macxmt_errors; + unsigned mxfe_macrcv_errors; + unsigned mxfe_toolong_errors; + unsigned mxfe_runt; + unsigned mxfe_jabber; +}; + +struct mxfe_rxbuf { + caddr_t rxb_buf; + ddi_dma_handle_t rxb_dmah; + ddi_acc_handle_t rxb_acch; + uint32_t rxb_paddr; +}; + +struct mxfe_txbuf { + /* bcopy version of tx */ + caddr_t txb_buf; + uint32_t txb_paddr; + ddi_dma_handle_t txb_dmah; + ddi_acc_handle_t txb_acch; +}; + +/* + * Descriptor. We use rings rather than chains. + */ +struct mxfe_desc { + unsigned desc_status; + unsigned desc_control; + unsigned desc_buffer1; + unsigned desc_buffer2; +}; + +#define PUTTXDESC(mxfep, member, val) \ + ddi_put32(mxfep->mxfe_txdesc_acch, &member, val) + +#define PUTRXDESC(mxfep, member, val) \ + ddi_put32(mxfep->mxfe_rxdesc_acch, &member, val) + +#define GETTXDESC(mxfep, member) \ + ddi_get32(mxfep->mxfe_txdesc_acch, &member) + +#define GETRXDESC(mxfep, member) \ + ddi_get32(mxfep->mxfe_rxdesc_acch, &member) + +/* + * Receive descriptor fields. + */ +#define RXSTAT_OWN 0x80000000U /* ownership */ +#define RXSTAT_RXLEN 0x3FFF0000U /* frame length, incl. crc */ +#define RXSTAT_RXERR 0x00008000U /* error summary */ +#define RXSTAT_DESCERR 0x00004000U /* descriptor error */ +#define RXSTAT_RXTYPE 0x00003000U /* data type */ +#define RXSTAT_RUNT 0x00000800U /* runt frame */ +#define RXSTAT_GROUP 0x00000400U /* multicast/brdcast frame */ +#define RXSTAT_FIRST 0x00000200U /* first descriptor */ +#define RXSTAT_LAST 0x00000100U /* last descriptor */ +#define RXSTAT_TOOLONG 0x00000080U /* frame too long */ +#define RXSTAT_COLLSEEN 0x00000040U /* late collision seen */ +#define RXSTAT_FRTYPE 0x00000020U /* frame type */ +#define RXSTAT_WATCHDOG 0x00000010U /* receive watchdog */ +#define RXSTAT_DRIBBLE 0x00000004U /* dribbling bit */ +#define RXSTAT_CRCERR 0x00000002U /* crc error */ +#define RXSTAT_OFLOW 0x00000001U /* fifo overflow */ +#define RXSTAT_ERRS (RXSTAT_DESCERR | RXSTAT_RUNT | \ + RXSTAT_COLLSEEN | RXSTAT_DRIBBLE | \ + RXSTAT_CRCERR | RXSTAT_OFLOW) +#define RXLENGTH(x) ((x & RXSTAT_RXLEN) >> 16) + +#define RXCTL_ENDRING 0x02000000U /* end of ring */ +#define RXCTL_CHAIN 0x01000000U /* chained descriptors */ +#define RXCTL_BUFLEN2 0x003FF800U /* buffer 2 length */ +#define RXCTL_BUFLEN1 0x000007FFU /* buffer 1 length */ + +/* + * Transmit descriptor fields. + */ +#define TXSTAT_OWN 0x80000000U /* ownership */ +#define TXSTAT_URCNT 0x00C00000U /* underrun count */ +#define TXSTAT_TXERR 0x00008000U /* error summary */ +#define TXSTAT_JABBER 0x00004000U /* jabber timeout */ +#define TXSTAT_CARRLOST 0x00000800U /* lost carrier */ +#define TXSTAT_NOCARR 0x00000400U /* no carrier */ +#define TXSTAT_LATECOL 0x00000200U /* late collision */ +#define TXSTAT_EXCOLL 0x00000100U /* excessive collisions */ +#define TXSTAT_SQE 0x00000080U /* heartbeat failure */ +#define TXSTAT_COLLCNT 0x00000078U /* collision count */ +#define TXSTAT_UFLOW 0x00000002U /* underflow */ +#define TXSTAT_DEFER 0x00000001U /* deferred */ +#define TXCOLLCNT(x) ((x & TXSTAT_COLLCNT) >> 3) +#define TXUFLOWCNT(x) ((x & TXSTAT_URCNT) >> 22) + +#define TXCTL_INTCMPLTE 0x80000000U /* interrupt completed */ +#define TXCTL_LAST 0x40000000U /* last descriptor */ +#define TXCTL_FIRST 0x20000000U /* first descriptor */ +#define TXCTL_NOCRC 0x04000000U /* disable crc */ +#define TXCTL_SETUP 0x08000000U /* setup frame */ +#define TXCTL_ENDRING 0x02000000U /* end of ring */ +#define TXCTL_CHAIN 0x01000000U /* chained descriptors */ +#define TXCTL_NOPAD 0x00800000U /* disable padding */ +#define TXCTL_HASHPERF 0x00400000U /* hash perfect mode */ +#define TXCTL_BUFLEN2 0x003FF800U /* buffer length 2 */ +#define TXCTL_BUFLEN1 0x000007FFU /* buffer length 1 */ + +/* + * Interface flags. + */ +#define MXFE_RUNNING 0x1 /* chip is initialized */ +#define MXFE_SUSPENDED 0x2 /* interface is suspended */ +#define MXFE_SYMBOL 0x8 /* use symbol mode */ + +/* + * Link flags... + */ +#define MXFE_NOLINK 0x0 /* initial link state, no timer */ +#define MXFE_NWAYCHECK 0x2 /* checking for NWay support */ +#define MXFE_NWAYRENEG 0x3 /* renegotiating NWay mode */ +#define MXFE_GOODLINK 0x4 /* detected link is good */ + +/* + * Card models. + */ +#define MXFE_MODEL(mxfep) ((mxfep)->mxfe_cardp->card_model) +#define MXFE_98715 0x1 +#define MXFE_98715A 0x2 +#define MXFE_98715AEC 0x3 +#define MXFE_98715B 0x4 +#define MXFE_98725 0x5 +#define MXFE_98713 0x6 +#define MXFE_98713A 0x7 +#define MXFE_PNICII 0x8 + +/* + * Register definitions located in mxfe.h exported header file. + */ + +/* + * Macros to simplify hardware access. Note that the reg/4 is used to + * help with pointer arithmetic. + */ +#define GETCSR(mxfep, reg) \ + ddi_get32(mxfep->mxfe_regshandle, mxfep->mxfe_regs + (reg/4)) + +#define PUTCSR(mxfep, reg, val) \ + ddi_put32(mxfep->mxfe_regshandle, mxfep->mxfe_regs + (reg/4), val) + +#define SETBIT(mxfep, reg, val) \ + PUTCSR(mxfep, reg, GETCSR(mxfep, reg) | (val)) + +#define CLRBIT(mxfep, reg, val) \ + PUTCSR(mxfep, reg, GETCSR(mxfep, reg) & ~(val)) + +#define SYNCTXDESC(mxfep, index, who) \ + (void) ddi_dma_sync(mxfep->mxfe_txdesc_dmah, \ + (index * sizeof (mxfe_desc_t)), sizeof (mxfe_desc_t), who) + +#define SYNCTXBUF(txb, len, who) \ + (void) (ddi_dma_sync(txb->txb_dmah, 0, len, who)) + +#define SYNCRXDESC(mxfep, index, who) \ + (void) ddi_dma_sync(mxfep->mxfe_rxdesc_dmah, \ + (index * sizeof (mxfe_desc_t)), sizeof (mxfe_desc_t), who) + +#define SYNCRXBUF(rxb, len, who) \ + (void) (ddi_dma_sync(rxb->rxb_dmah, 0, len, who)) + +/* + * Debugging flags. + */ +#define DWARN 0x0001 +#define DINTR 0x0002 +#define DWSRV 0x0004 +#define DMACID 0x0008 +#define DDLPI 0x0010 +#define DPHY 0x0020 +#define DPCI 0x0040 +#define DCHATTY 0x0080 +#define DDMA 0x0100 +#define DLINK 0x0200 +#define DSROM 0x0400 +#define DRECV 0x0800 +#define DXMIT 0x1000 + +#ifdef DEBUG +#define DBG(lvl, ...) mxfe_dprintf(mxfep, __func__, lvl, __VA_ARGS__); +#else +#define DBG(lvl, ...) +#endif + +#endif /* _KERNEL */ + +#endif /* _MXFEIMPL_H */
--- a/usr/src/uts/intel/Makefile.intel.shared Fri Aug 31 16:49:49 2007 -0700 +++ b/usr/src/uts/intel/Makefile.intel.shared Fri Aug 31 17:00:55 2007 -0700 @@ -346,6 +346,7 @@ # DRV_KMODS += e1000g +DRV_KMODS += mxfe DRV_KMODS += rge $(CLOSED_BUILD)CLOSED_DRV_KMODS += ixgb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/mxfe/Makefile Fri Aug 31 17:00:55 2007 -0700 @@ -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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the Macronix +# Fast Ethernet (MXFE) driver module in intel systems +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = mxfe +OBJECTS = $(MXFE_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(MXFE_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides +# + +# +# Driver depends on GLDv3 +# +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)/intel/Makefile.targ
--- a/usr/src/uts/sparc/Makefile.sparc.shared Fri Aug 31 16:49:49 2007 -0700 +++ b/usr/src/uts/sparc/Makefile.sparc.shared Fri Aug 31 17:00:55 2007 -0700 @@ -259,6 +259,7 @@ # DRV_KMODS += e1000g +DRV_KMODS += mxfe DRV_KMODS += pcan DRV_KMODS += pcwl DRV_KMODS += rge
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sparc/mxfe/Makefile Fri Aug 31 17:00:55 2007 -0700 @@ -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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the Macronix +# Fast Ethernet (MXFE) 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 = mxfe +OBJECTS = $(MXFE_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(MXFE_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 GLDv3 +# +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