summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/pkg/manifests/driver-network-efe.mf32
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/io/efe/THIRDPARTYLICENSE27
-rw-r--r--usr/src/uts/common/io/efe/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/efe/efe.c1671
-rw-r--r--usr/src/uts/common/io/efe/efe.h458
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/efe/Makefile67
-rw-r--r--usr/src/uts/intel/os/master1
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared1
-rw-r--r--usr/src/uts/sparc/efe/Makefile67
12 files changed, 2334 insertions, 1 deletions
diff --git a/usr/src/pkg/manifests/driver-network-efe.mf b/usr/src/pkg/manifests/driver-network-efe.mf
new file mode 100644
index 0000000000..23fba6c940
--- /dev/null
+++ b/usr/src/pkg/manifests/driver-network-efe.mf
@@ -0,0 +1,32 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# The default for payload-bearing actions in this package is to appear in the
+# global zone only. See the include file for greater detail, as well as
+# information about overriding the defaults.
+#
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/driver/network/efe@$(PKGVERS)
+set name=pkg.description value="EPIC/100 Fast Ethernet Network Adapter Driver"
+set name=pkg.summary value="EPIC/100 Fast Ethernet Driver"
+set name=info.classification \
+ value=org.opensolaris.category.2008:Drivers/Networking
+set name=variant.arch value=$(ARCH)
+dir path=kernel group=sys
+dir path=kernel/drv group=sys
+dir path=kernel/drv/$(ARCH64) group=sys
+driver name=efe alias=pci10b8,5 clone_perms="efe 0666 root sys" \
+ perms="* 0666 root sys"
+file path=kernel/drv/$(ARCH64)/efe group=sys
+$(i386_ONLY)file path=kernel/drv/efe group=sys
+license usr/src/uts/common/io/efe/THIRDPARTYLICENSE \
+ license=usr/src/uts/common/io/efe/THIRDPARTYLICENSE
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 102c449aa9..95d3b54cf2 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1763,6 +1763,8 @@ BGE_OBJS += bge_main2.o bge_chip2.o bge_kstats.o bge_log.o bge_ndd.o \
DMFE_OBJS += dmfe_log.o dmfe_main.o dmfe_mii.o
+EFE_OBJS += efe.o
+
ELXL_OBJS += elxl.o
HME_OBJS += hme.o
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index c05da5b2ec..d1c9ad4030 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -712,6 +712,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/drm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/efe/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/elxl/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -2034,6 +2038,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/dmfe/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/drm/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/efe/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/elxl/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/io/efe/THIRDPARTYLICENSE b/usr/src/uts/common/io/efe/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..3586841c65
--- /dev/null
+++ b/usr/src/uts/common/io/efe/THIRDPARTYLICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2010 Steven Stallion. 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 copyright owner nor the names of any
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 OWNER 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.
diff --git a/usr/src/uts/common/io/efe/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/efe/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..24c53ca16f
--- /dev/null
+++ b/usr/src/uts/common/io/efe/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+EPIC/100 FAST ETHERNET DRIVER
diff --git a/usr/src/uts/common/io/efe/efe.c b/usr/src/uts/common/io/efe/efe.c
new file mode 100644
index 0000000000..0c5473ce56
--- /dev/null
+++ b/usr/src/uts/common/io/efe/efe.c
@@ -0,0 +1,1671 @@
+/*
+ * Copyright (c) 2010 Steven Stallion. 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 copyright owner nor the names of any
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/byteorder.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/varargs.h>
+#include <sys/cmn_err.h>
+#include <sys/note.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/ddi_intr.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/pci.h>
+#include <sys/ethernet.h>
+#include <sys/vlan.h>
+#include <sys/crc32.h>
+#include <sys/mii.h>
+#include <sys/mac.h>
+#include <sys/mac_ether.h>
+#include <sys/mac_provider.h>
+
+#include "efe.h"
+
+/* Autoconfiguration entry points */
+static int efe_attach(dev_info_t *, ddi_attach_cmd_t);
+static int efe_detach(dev_info_t *, ddi_detach_cmd_t);
+static int efe_quiesce(dev_info_t *);
+
+/* MII entry points */
+static uint16_t efe_mii_read(void *, uint8_t, uint8_t);
+static void efe_mii_write(void *, uint8_t, uint8_t, uint16_t);
+static void efe_mii_notify(void *, link_state_t);
+
+/* MAC entry points */
+static int efe_m_getstat(void *, uint_t, uint64_t *);
+static int efe_m_start(void *);
+static void efe_m_stop(void *);
+static int efe_m_setpromisc(void *, boolean_t);
+static int efe_m_multicst(void *, boolean_t, const uint8_t *);
+static int efe_m_unicst(void *, const uint8_t *);
+static mblk_t *efe_m_tx(void *, mblk_t *);
+static int efe_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
+ const void *);
+static int efe_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
+ void *);
+static void efe_m_propinfo(void *, const char *, mac_prop_id_t,
+ mac_prop_info_handle_t);
+
+/* ISR/periodic callbacks */
+static uint_t efe_intr(caddr_t, caddr_t);
+
+/* Support functions */
+static void efe_init(efe_t *);
+static void efe_init_rx_ring(efe_t *);
+static void efe_init_tx_ring(efe_t *);
+static void efe_reset(efe_t *);
+static void efe_start(efe_t *);
+static void efe_stop(efe_t *);
+static void efe_stop_dma(efe_t *);
+static inline void efe_restart(efe_t *);
+static int efe_suspend(efe_t *);
+static int efe_resume(efe_t *);
+
+static efe_ring_t *efe_ring_alloc(dev_info_t *, size_t);
+static void efe_ring_free(efe_ring_t **);
+static efe_buf_t *efe_buf_alloc(dev_info_t *, size_t);
+static void efe_buf_free(efe_buf_t **);
+
+static void efe_intr_enable(efe_t *);
+static void efe_intr_disable(efe_t *);
+
+static mblk_t *efe_recv(efe_t *);
+static mblk_t *efe_recv_pkt(efe_t *, efe_desc_t *);
+
+static int efe_send(efe_t *, mblk_t *);
+static void efe_send_done(efe_t *);
+
+static void efe_getaddr(efe_t *, uint8_t *);
+static void efe_setaddr(efe_t *, uint8_t *);
+static void efe_setmchash(efe_t *, uint16_t *);
+
+static void efe_eeprom_read(efe_t *, uint8_t *, size_t, uint8_t);
+static uint16_t efe_eeprom_readw(efe_t *, int, uint8_t);
+static inline int efe_eeprom_readbit(efe_t *);
+static inline void efe_eeprom_writebit(efe_t *, int);
+
+static void efe_dprintf(dev_info_t *, int, const char *, ...);
+
+#ifdef DEBUG
+#define efe_debug(dip, ...) \
+ efe_dprintf((dip), CE_CONT, __VA_ARGS__)
+#else
+#define efe_debug(dip, ...) /*EMPTY*/
+#endif
+
+#define efe_error(dip, ...) \
+ efe_dprintf((dip), CE_WARN, __VA_ARGS__)
+
+extern struct mod_ops mod_driverops;
+
+DDI_DEFINE_STREAM_OPS(efe_dev_ops, nulldev, nulldev, efe_attach, efe_detach,
+ nodev, NULL, D_MP, NULL, efe_quiesce);
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* drv_modops */
+ "EPIC/100 Fast Ethernet", /* drv_linkinfo */
+ &efe_dev_ops /* drv_dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, /* ml_rev */
+ { &modldrv, NULL } /* ml_linkage */
+};
+
+static ddi_device_acc_attr_t efe_regs_acc_attr = {
+ DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
+ DDI_STRUCTURE_LE_ACC, /* devacc_attr_endian_flags */
+ DDI_STRICTORDER_ACC /* devacc_attr_dataorder */
+};
+
+static ddi_device_acc_attr_t efe_buf_acc_attr = {
+ DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
+ DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */
+ DDI_STRICTORDER_ACC /* devacc_attr_dataorder */
+};
+
+static ddi_dma_attr_t efe_dma_attr = {
+ DMA_ATTR_V0, /* dma_attr_version */
+ 0, /* dma_attr_addr_lo */
+ 0xFFFFFFFFUL, /* dma_attr_addr_hi */
+ 0x7FFFFFFFUL, /* dma_attr_count_max */
+ 4, /* dma_attr_align */
+ 0x7F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ 0xFFFFFFFFUL, /* dma_attr_maxxfer */
+ 0xFFFFFFFFUL, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0 /* dma_attr_flags */
+};
+
+static mii_ops_t efe_mii_ops = {
+ MII_OPS_VERSION, /* mii_version */
+ efe_mii_read, /* mii_read */
+ efe_mii_write, /* mii_write */
+ efe_mii_notify /* mii_notify */
+};
+
+static mac_callbacks_t efe_m_callbacks = {
+ MC_SETPROP | MC_GETPROP, /* mc_callbacks */
+ efe_m_getstat, /* mc_getstat */
+ efe_m_start, /* mc_start */
+ efe_m_stop, /* mc_stop */
+ efe_m_setpromisc, /* mc_setpromisc */
+ efe_m_multicst, /* mc_multicst */
+ efe_m_unicst, /* mc_unicst */
+ efe_m_tx, /* mc_tx */
+ NULL, /* mc_reserved */
+ NULL, /* mc_ioctl */
+ NULL, /* mc_getcapab */
+ NULL, /* mc_open */
+ NULL, /* mc_close */
+ efe_m_setprop, /* mc_setprop */
+ efe_m_getprop, /* mc_getprop */
+ efe_m_propinfo /* mc_propinfo */
+};
+
+static uint8_t efe_broadcast[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static uint16_t efe_mchash_promisc[] = {
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+};
+
+/*
+ * Loadable module entry points.
+ */
+int
+_init(void)
+{
+ int error;
+
+ mac_init_ops(&efe_dev_ops, "efe");
+ if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
+ mac_fini_ops(&efe_dev_ops);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
+ mac_fini_ops(&efe_dev_ops);
+ }
+
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Autoconfiguration entry points.
+ */
+int
+efe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ ddi_acc_handle_t pci;
+ int types;
+ int count;
+ int actual;
+ uint_t pri;
+ efe_t *efep;
+ mac_register_t *macp;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+
+ case DDI_RESUME:
+ efep = ddi_get_driver_private(dip);
+ return (efe_resume(efep));
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * PCI configuration.
+ */
+ if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
+ efe_error(dip, "unable to setup PCI configuration!");
+ return (DDI_FAILURE);
+ }
+
+ pci_config_put16(pci, PCI_CONF_COMM,
+ pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_MAE | PCI_COMM_ME);
+
+ pci_config_teardown(&pci);
+
+ if (ddi_intr_get_supported_types(dip, &types)
+ != DDI_SUCCESS || !(types & DDI_INTR_TYPE_FIXED)) {
+ efe_error(dip, "fixed interrupts not supported!");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &count)
+ != DDI_SUCCESS || count != 1) {
+ efe_error(dip, "no fixed interrupts available!");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Initialize soft state.
+ */
+ efep = kmem_zalloc(sizeof (efe_t), KM_SLEEP);
+ ddi_set_driver_private(dip, efep);
+
+ efep->efe_dip = dip;
+
+ if (ddi_regs_map_setup(dip, 1, (caddr_t *)&efep->efe_regs, 0, 0,
+ &efe_regs_acc_attr, &efep->efe_regs_acch) != DDI_SUCCESS) {
+ efe_error(dip, "unable to setup register mapping!");
+ goto failure;
+ }
+
+ efep->efe_rx_ring = efe_ring_alloc(efep->efe_dip, RXDESCL);
+ if (efep->efe_rx_ring == NULL) {
+ efe_error(efep->efe_dip, "unable to allocate rx ring!");
+ goto failure;
+ }
+
+ efep->efe_tx_ring = efe_ring_alloc(efep->efe_dip, TXDESCL);
+ if (efep->efe_tx_ring == NULL) {
+ efe_error(efep->efe_dip, "unable to allocate tx ring!");
+ goto failure;
+ }
+
+ if (ddi_intr_alloc(dip, &efep->efe_intrh, DDI_INTR_TYPE_FIXED, 0,
+ count, &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS ||
+ actual != count) {
+ efe_error(dip, "unable to allocate fixed interrupt!");
+ goto failure;
+ }
+
+ if (ddi_intr_get_pri(efep->efe_intrh, &pri) != DDI_SUCCESS ||
+ pri >= ddi_intr_get_hilevel_pri()) {
+ efe_error(dip, "unable to get valid interrupt priority!");
+ goto failure;
+ }
+
+ mutex_init(&efep->efe_intrlock, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(pri));
+
+ mutex_init(&efep->efe_txlock, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(pri));
+
+ /*
+ * Initialize device.
+ */
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ efe_reset(efep);
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ /* Use factory address as default */
+ efe_getaddr(efep, efep->efe_macaddr);
+
+ /*
+ * Enable the ISR.
+ */
+ if (ddi_intr_add_handler(efep->efe_intrh, efe_intr, efep, NULL)
+ != DDI_SUCCESS) {
+ efe_error(dip, "unable to add interrupt handler!");
+ goto failure;
+ }
+
+ if (ddi_intr_enable(efep->efe_intrh) != DDI_SUCCESS) {
+ efe_error(dip, "unable to enable interrupt!");
+ goto failure;
+ }
+
+ /*
+ * Allocate MII resources.
+ */
+ if ((efep->efe_miih = mii_alloc(efep, dip, &efe_mii_ops)) == NULL) {
+ efe_error(dip, "unable to allocate mii resources!");
+ goto failure;
+ }
+
+ /*
+ * Allocate MAC resources.
+ */
+ if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+ efe_error(dip, "unable to allocate mac resources!");
+ goto failure;
+ }
+
+ macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ macp->m_driver = efep;
+ macp->m_dip = dip;
+ macp->m_src_addr = efep->efe_macaddr;
+ macp->m_callbacks = &efe_m_callbacks;
+ macp->m_min_sdu = 0;
+ macp->m_max_sdu = ETHERMTU;
+ macp->m_margin = VLAN_TAGSZ;
+
+ if (mac_register(macp, &efep->efe_mh) != 0) {
+ efe_error(dip, "unable to register with mac!");
+ goto failure;
+ }
+ mac_free(macp);
+
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+
+failure:
+ if (macp != NULL) {
+ mac_free(macp);
+ }
+
+ if (efep->efe_miih != NULL) {
+ mii_free(efep->efe_miih);
+ }
+
+ if (efep->efe_intrh != NULL) {
+ (void) ddi_intr_disable(efep->efe_intrh);
+ (void) ddi_intr_remove_handler(efep->efe_intrh);
+ (void) ddi_intr_free(efep->efe_intrh);
+ }
+
+ mutex_destroy(&efep->efe_txlock);
+ mutex_destroy(&efep->efe_intrlock);
+
+ if (efep->efe_tx_ring != NULL) {
+ efe_ring_free(&efep->efe_tx_ring);
+ }
+ if (efep->efe_rx_ring != NULL) {
+ efe_ring_free(&efep->efe_rx_ring);
+ }
+
+ if (efep->efe_regs_acch != NULL) {
+ ddi_regs_map_free(&efep->efe_regs_acch);
+ }
+
+ kmem_free(efep, sizeof (efe_t));
+
+ return (DDI_FAILURE);
+}
+
+int
+efe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ efe_t *efep = ddi_get_driver_private(dip);
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+
+ case DDI_SUSPEND:
+ return (efe_suspend(efep));
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (mac_unregister(efep->efe_mh) != 0) {
+ efe_error(dip, "unable to unregister from mac!");
+ return (DDI_FAILURE);
+ }
+
+ mii_free(efep->efe_miih);
+
+ (void) ddi_intr_disable(efep->efe_intrh);
+ (void) ddi_intr_remove_handler(efep->efe_intrh);
+ (void) ddi_intr_free(efep->efe_intrh);
+
+ mutex_destroy(&efep->efe_txlock);
+ mutex_destroy(&efep->efe_intrlock);
+
+ if (efep->efe_tx_ring != NULL) {
+ efe_ring_free(&efep->efe_tx_ring);
+ }
+ if (efep->efe_rx_ring != NULL) {
+ efe_ring_free(&efep->efe_rx_ring);
+ }
+
+ ddi_regs_map_free(&efep->efe_regs_acch);
+
+ kmem_free(efep, sizeof (efe_t));
+
+ return (DDI_SUCCESS);
+}
+
+int
+efe_quiesce(dev_info_t *dip)
+{
+ efe_t *efep = ddi_get_driver_private(dip);
+
+ PUTCSR(efep, CSR_GENCTL, GENCTL_RESET);
+ drv_usecwait(RESET_DELAY);
+
+ PUTCSR(efep, CSR_GENCTL, GENCTL_PWRDWN);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * MII entry points.
+ */
+uint16_t
+efe_mii_read(void *arg, uint8_t phy, uint8_t reg)
+{
+ efe_t *efep = arg;
+
+ PUTCSR(efep, CSR_MMCTL, MMCTL_READ |
+ reg << MMCTL_PHYREG | phy << MMCTL_PHYADDR);
+
+ for (int i = 0; i < MII_DELAY_CYCLES; ++i) {
+ if (!(GETCSR(efep, CSR_MMCTL) & MMCTL_READ)) {
+ return ((uint16_t)GETCSR(efep, CSR_MMDATA));
+ }
+ drv_usecwait(MII_DELAY);
+ }
+ efe_error(efep->efe_dip, "timed out reading from MII!");
+
+ return (0);
+}
+
+void
+efe_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data)
+{
+ efe_t *efep = arg;
+
+ PUTCSR(efep, CSR_MMDATA, data);
+
+ PUTCSR(efep, CSR_MMCTL, MMCTL_WRITE |
+ reg << MMCTL_PHYREG | phy << MMCTL_PHYADDR);
+
+ for (int i = 0; i < MII_DELAY_CYCLES; ++i) {
+ if (!(GETCSR(efep, CSR_MMCTL) & MMCTL_WRITE)) {
+ return;
+ }
+ drv_usecwait(MII_DELAY);
+ }
+ efe_error(efep->efe_dip, "timed out writing to MII!");
+}
+
+void
+efe_mii_notify(void *arg, link_state_t link)
+{
+ efe_t *efep = arg;
+
+ mac_link_update(efep->efe_mh, link);
+}
+
+/*
+ * MAC entry points.
+ */
+int
+efe_m_getstat(void *arg, uint_t stat, uint64_t *val)
+{
+ efe_t *efep = arg;
+
+ if (mii_m_getstat(efep->efe_miih, stat, val) == 0) {
+ return (0);
+ }
+
+ switch (stat) {
+ case MAC_STAT_MULTIRCV:
+ *val = efep->efe_multircv;
+ break;
+
+ case MAC_STAT_BRDCSTRCV:
+ *val = efep->efe_brdcstrcv;
+ break;
+
+ case MAC_STAT_MULTIXMT:
+ *val = efep->efe_multixmt;
+ break;
+
+ case MAC_STAT_BRDCSTXMT:
+ *val = efep->efe_brdcstxmt;
+ break;
+
+ case MAC_STAT_NORCVBUF:
+ *val = efep->efe_norcvbuf;
+ break;
+
+ case MAC_STAT_IERRORS:
+ *val = efep->efe_ierrors;
+ break;
+
+ case MAC_STAT_NOXMTBUF:
+ *val = efep->efe_noxmtbuf;
+ break;
+
+ case MAC_STAT_OERRORS:
+ *val = efep->efe_oerrors;
+ break;
+
+ case MAC_STAT_COLLISIONS:
+ *val = efep->efe_collisions;
+ break;
+
+ case MAC_STAT_RBYTES:
+ *val = efep->efe_rbytes;
+ break;
+
+ case MAC_STAT_IPACKETS:
+ *val = efep->efe_ipackets;
+ break;
+
+ case MAC_STAT_OBYTES:
+ *val = efep->efe_obytes;
+ break;
+
+ case MAC_STAT_OPACKETS:
+ *val = efep->efe_opackets;
+ break;
+
+ case MAC_STAT_UNDERFLOWS:
+ *val = efep->efe_uflo;
+ break;
+
+ case MAC_STAT_OVERFLOWS:
+ *val = efep->efe_oflo;
+ break;
+
+ case ETHER_STAT_ALIGN_ERRORS:
+ *val = efep->efe_align_errors;
+ break;
+
+ case ETHER_STAT_FCS_ERRORS:
+ *val = efep->efe_fcs_errors;
+ break;
+
+ case ETHER_STAT_FIRST_COLLISIONS:
+ *val = efep->efe_first_collisions;
+ break;
+
+ case ETHER_STAT_TX_LATE_COLLISIONS:
+ *val = efep->efe_tx_late_collisions;
+ break;
+
+ case ETHER_STAT_DEFER_XMTS:
+ *val = efep->efe_defer_xmts;
+ break;
+
+ case ETHER_STAT_EX_COLLISIONS:
+ *val = efep->efe_ex_collisions;
+ break;
+
+ case ETHER_STAT_MACXMT_ERRORS:
+ *val = efep->efe_macxmt_errors;
+ break;
+
+ case ETHER_STAT_CARRIER_ERRORS:
+ *val = efep->efe_carrier_errors;
+ break;
+
+ case ETHER_STAT_TOOLONG_ERRORS:
+ *val = efep->efe_toolong_errors;
+ break;
+
+ case ETHER_STAT_MACRCV_ERRORS:
+ *val = efep->efe_macrcv_errors;
+ break;
+
+ case ETHER_STAT_TOOSHORT_ERRORS:
+ *val = efep->efe_runt_errors;
+ break;
+
+ case ETHER_STAT_JABBER_ERRORS:
+ *val = efep->efe_jabber_errors;
+ break;
+
+ default:
+ return (ENOTSUP);
+ }
+
+ return (0);
+}
+
+int
+efe_m_start(void *arg)
+{
+ efe_t *efep = arg;
+
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ efe_start(efep);
+ efep->efe_flags |= FLAG_RUNNING;
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ mii_start(efep->efe_miih);
+
+ return (0);
+}
+
+void
+efe_m_stop(void *arg)
+{
+ efe_t *efep = arg;
+
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ efe_stop(efep);
+ efep->efe_flags &= ~FLAG_RUNNING;
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ mii_stop(efep->efe_miih);
+}
+
+int
+efe_m_setpromisc(void *arg, boolean_t on)
+{
+ efe_t *efep = arg;
+
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_SUSPENDED) {
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+ return (0);
+ }
+
+ efep->efe_promisc = on;
+
+ if (efep->efe_flags & FLAG_RUNNING) {
+ efe_restart(efep);
+ }
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ return (0);
+}
+
+int
+efe_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
+{
+ efe_t *efep = arg;
+ uint32_t val;
+ int index;
+ int bit;
+ boolean_t restart = B_FALSE;
+
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_SUSPENDED) {
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+ return (0);
+ }
+
+ CRC32(val, macaddr, ETHERADDRL, -1U, crc32_table);
+ val %= MCHASHL;
+
+ index = val / MCHASHSZ;
+ bit = 1U << (val % MCHASHSZ);
+
+ if (add) {
+ efep->efe_mccount[val]++;
+ if (efep->efe_mccount[val] == 1) {
+ efep->efe_mchash[index] |= bit;
+ restart = B_TRUE;
+ }
+
+ } else {
+ efep->efe_mccount[val]--;
+ if (efep->efe_mccount[val] == 0) {
+ efep->efe_mchash[index] &= ~bit;
+ restart = B_TRUE;
+ }
+ }
+
+ if (restart && efep->efe_flags & FLAG_RUNNING) {
+ efe_restart(efep);
+ }
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ return (0);
+}
+
+int
+efe_m_unicst(void *arg, const uint8_t *macaddr)
+{
+ efe_t *efep = arg;
+
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_SUSPENDED) {
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+ return (0);
+ }
+
+ bcopy(macaddr, efep->efe_macaddr, ETHERADDRL);
+
+ if (efep->efe_flags & FLAG_RUNNING) {
+ efe_restart(efep);
+ }
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ return (0);
+}
+
+mblk_t *
+efe_m_tx(void *arg, mblk_t *mp)
+{
+ efe_t *efep = arg;
+
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_SUSPENDED) {
+ mutex_exit(&efep->efe_txlock);
+ return (mp);
+ }
+
+ while (mp != NULL) {
+ mblk_t *tmp = mp->b_next;
+ mp->b_next = NULL;
+
+ if (efe_send(efep, mp) != DDI_SUCCESS) {
+ mp->b_next = tmp;
+ break;
+ }
+ mp = tmp;
+ }
+
+ /* Kick the transmitter */
+ PUTCSR(efep, CSR_COMMAND, COMMAND_TXQUEUED);
+
+ mutex_exit(&efep->efe_txlock);
+
+ return (mp);
+}
+
+int
+efe_m_setprop(void *arg, const char *name, mac_prop_id_t id,
+ uint_t valsize, const void *val)
+{
+ efe_t *efep = arg;
+
+ return (mii_m_setprop(efep->efe_miih, name, id, valsize, val));
+}
+
+int
+efe_m_getprop(void *arg, const char *name, mac_prop_id_t id,
+ uint_t valsize, void *val)
+{
+ efe_t *efep = arg;
+
+ return (mii_m_getprop(efep->efe_miih, name, id, valsize, val));
+}
+
+void
+efe_m_propinfo(void *arg, const char *name, mac_prop_id_t id,
+ mac_prop_info_handle_t state)
+{
+ efe_t *efep = arg;
+
+ mii_m_propinfo(efep->efe_miih, name, id, state);
+}
+
+/*
+ * ISR/periodic callbacks.
+ */
+uint_t
+efe_intr(caddr_t arg1, caddr_t arg2)
+{
+ efe_t *efep = (void *)arg1;
+ uint32_t status;
+ mblk_t *mp = NULL;
+
+ _NOTE(ARGUNUSED(arg2));
+
+ mutex_enter(&efep->efe_intrlock);
+
+ if (efep->efe_flags & FLAG_SUSPENDED) {
+ mutex_exit(&efep->efe_intrlock);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ status = GETCSR(efep, CSR_INTSTAT);
+ if (!(status & INTSTAT_ACTV)) {
+ mutex_exit(&efep->efe_intrlock);
+ return (DDI_INTR_UNCLAIMED);
+ }
+ PUTCSR(efep, CSR_INTSTAT, status);
+
+ if (status & INTSTAT_RCC) {
+ mp = efe_recv(efep);
+ }
+
+ if (status & INTSTAT_RQE) {
+ efep->efe_ierrors++;
+ efep->efe_macrcv_errors++;
+
+ /* Kick the receiver */
+ PUTCSR(efep, CSR_COMMAND, COMMAND_RXQUEUED);
+ }
+
+ if (status & INTSTAT_TXC) {
+ mutex_enter(&efep->efe_txlock);
+
+ efe_send_done(efep);
+
+ mutex_exit(&efep->efe_txlock);
+ }
+
+ if (status & INTSTAT_FATAL) {
+ mutex_enter(&efep->efe_txlock);
+
+ efe_error(efep->efe_dip, "bus error; resetting!");
+ efe_restart(efep);
+
+ mutex_exit(&efep->efe_txlock);
+ }
+
+ mutex_exit(&efep->efe_intrlock);
+
+ if (mp != NULL) {
+ mac_rx(efep->efe_mh, NULL, mp);
+ }
+
+ if (status & INTSTAT_TXC) {
+ mac_tx_update(efep->efe_mh);
+ }
+
+ if (status & INTSTAT_FATAL) {
+ mii_reset(efep->efe_miih);
+ }
+
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Support functions.
+ */
+void
+efe_init(efe_t *efep)
+{
+ uint32_t val;
+
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ efe_reset(efep);
+
+ val = GENCTL_ONECOPY | GENCTL_RFT_128 | GENCTL_MRM;
+#ifdef _BIG_ENDIAN
+ val |= GENCTL_BE;
+#endif /* _BIG_ENDIAN */
+
+ PUTCSR(efep, CSR_GENCTL, val);
+ PUTCSR(efep, CSR_PBLCNT, BURSTLEN);
+
+ efe_init_rx_ring(efep);
+ efe_init_tx_ring(efep);
+
+ efe_setaddr(efep, efep->efe_macaddr);
+
+ if (efep->efe_promisc) {
+ efe_setmchash(efep, efe_mchash_promisc);
+ } else {
+ efe_setmchash(efep, efep->efe_mchash);
+ }
+}
+
+void
+efe_init_rx_ring(efe_t *efep)
+{
+ efe_ring_t *rp;
+
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+
+ rp = efep->efe_rx_ring;
+
+ for (int i = 0; i < DESCLEN(rp); ++i) {
+ efe_desc_t *dp = GETDESC(rp, i);
+ efe_buf_t *bp = GETBUF(rp, i);
+
+ PUTDESC16(rp, &dp->d_status, RXSTAT_OWNER);
+ PUTDESC16(rp, &dp->d_len, 0);
+ PUTDESC32(rp, &dp->d_bufaddr, BUFADDR(bp));
+ PUTDESC16(rp, &dp->d_buflen, BUFLEN(bp));
+ PUTDESC16(rp, &dp->d_control, 0);
+ PUTDESC32(rp, &dp->d_next, NEXTDESCADDR(rp, i));
+
+ SYNCDESC(rp, i, DDI_DMA_SYNC_FORDEV);
+ }
+
+ efep->efe_rx_desc = 0;
+
+ PUTCSR(efep, CSR_PRCDAR, DESCADDR(rp, 0));
+}
+
+void
+efe_init_tx_ring(efe_t *efep)
+{
+ efe_ring_t *rp;
+
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ rp = efep->efe_tx_ring;
+
+ for (int i = 0; i < DESCLEN(rp); ++i) {
+ efe_desc_t *dp = GETDESC(rp, i);
+ efe_buf_t *bp = GETBUF(rp, i);
+
+ PUTDESC16(rp, &dp->d_status, 0);
+ PUTDESC16(rp, &dp->d_len, 0);
+ PUTDESC32(rp, &dp->d_bufaddr, BUFADDR(bp));
+ PUTDESC16(rp, &dp->d_buflen, BUFLEN(bp));
+ PUTDESC16(rp, &dp->d_control, 0);
+ PUTDESC32(rp, &dp->d_next, NEXTDESCADDR(rp, i));
+
+ SYNCDESC(rp, i, DDI_DMA_SYNC_FORDEV);
+ }
+
+ efep->efe_tx_desc = 0;
+ efep->efe_tx_sent = 0;
+
+ PUTCSR(efep, CSR_PTCDAR, DESCADDR(rp, 0));
+}
+
+void
+efe_reset(efe_t *efep)
+{
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ PUTCSR(efep, CSR_GENCTL, GENCTL_RESET);
+ drv_usecwait(RESET_DELAY);
+
+ /* Assert internal clock source (AN 7.15) */
+ for (int i = 0; i < RESET_TEST_CYCLES; ++i) {
+ PUTCSR(efep, CSR_TEST, TEST_CLOCK);
+ }
+}
+
+void
+efe_start(efe_t *efep)
+{
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ efe_init(efep);
+
+ PUTCSR(efep, CSR_RXCON,
+ RXCON_SEP | RXCON_RRF | RXCON_RBF | RXCON_RMF |
+ (efep->efe_promisc ? RXCON_PROMISC : 0));
+
+ PUTCSR(efep, CSR_TXCON, TXCON_LB_3);
+
+ efe_intr_enable(efep);
+
+ SETBIT(efep, CSR_COMMAND,
+ COMMAND_START_RX | COMMAND_RXQUEUED);
+}
+
+void
+efe_stop(efe_t *efep)
+{
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ efe_intr_disable(efep);
+
+ PUTCSR(efep, CSR_COMMAND, COMMAND_STOP_RX);
+
+ efe_stop_dma(efep);
+
+ PUTCSR(efep, CSR_GENCTL, GENCTL_RESET);
+ drv_usecwait(RESET_DELAY);
+
+ PUTCSR(efep, CSR_GENCTL, GENCTL_PWRDWN);
+}
+
+void
+efe_stop_dma(efe_t *efep)
+{
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ PUTCSR(efep, CSR_COMMAND,
+ COMMAND_STOP_RDMA | COMMAND_STOP_TDMA);
+
+ for (int i = 0; i < STOP_DELAY_CYCLES; ++i) {
+ uint32_t status = GETCSR(efep, CSR_INTSTAT);
+ if (status & INTSTAT_RXIDLE &&
+ status & INTSTAT_TXIDLE) {
+ return;
+ }
+ drv_usecwait(STOP_DELAY);
+ }
+ efe_error(efep->efe_dip, "timed out stopping DMA engine!");
+}
+
+inline void
+efe_restart(efe_t *efep)
+{
+ efe_stop(efep);
+ efe_start(efep);
+}
+
+int
+efe_suspend(efe_t *efep)
+{
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_RUNNING) {
+ efe_stop(efep);
+ }
+ efep->efe_flags |= FLAG_SUSPENDED;
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ mii_suspend(efep->efe_miih);
+
+ return (DDI_SUCCESS);
+}
+
+int
+efe_resume(efe_t *efep)
+{
+ mutex_enter(&efep->efe_intrlock);
+ mutex_enter(&efep->efe_txlock);
+
+ if (efep->efe_flags & FLAG_RUNNING) {
+ efe_start(efep);
+ }
+ efep->efe_flags &= ~FLAG_SUSPENDED;
+
+ mutex_exit(&efep->efe_txlock);
+ mutex_exit(&efep->efe_intrlock);
+
+ mii_resume(efep->efe_miih);
+
+ return (DDI_SUCCESS);
+}
+
+efe_ring_t *
+efe_ring_alloc(dev_info_t *dip, size_t len)
+{
+ efe_ring_t *rp;
+ size_t rlen;
+ uint_t ccount;
+
+ ASSERT(len > 1);
+
+ rp = kmem_zalloc(sizeof (efe_ring_t), KM_SLEEP);
+ rp->r_len = len;
+
+ if (ddi_dma_alloc_handle(dip, &efe_dma_attr, DDI_DMA_SLEEP, NULL,
+ &rp->r_dmah) != DDI_SUCCESS) {
+ efe_error(dip, "unable to allocate DMA handle!");
+ goto failure;
+ }
+
+ if (ddi_dma_mem_alloc(rp->r_dmah, DESCSZ(len), &efe_buf_acc_attr,
+ DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, (caddr_t *)&rp->r_descp,
+ &rlen, &rp->r_acch) != DDI_SUCCESS) {
+ efe_error(dip, "unable to allocate descriptors!");
+ goto failure;
+ }
+
+ if (ddi_dma_addr_bind_handle(rp->r_dmah, NULL, (caddr_t)rp->r_descp,
+ DESCSZ(len), DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
+ NULL, &rp->r_dmac, &ccount) != DDI_DMA_MAPPED) {
+ efe_error(dip, "unable to bind DMA handle to descriptors!");
+ goto failure;
+ }
+
+ rp->r_bufpp = kmem_zalloc(BUFPSZ(len), KM_SLEEP);
+
+ for (int i = 0; i < len; ++i) {
+ efe_buf_t *bp = efe_buf_alloc(dip, BUFSZ);
+ if (bp == NULL) {
+ goto failure;
+ }
+ rp->r_bufpp[i] = bp;
+ }
+
+ return (rp);
+
+failure:
+ efe_ring_free(&rp);
+
+ return (NULL);
+}
+
+void
+efe_ring_free(efe_ring_t **rpp)
+{
+ efe_ring_t *rp = *rpp;
+
+ ASSERT(rp != NULL);
+
+ for (int i = 0; i < DESCLEN(rp); ++i) {
+ efe_buf_t *bp = GETBUF(rp, i);
+ if (bp != NULL) {
+ efe_buf_free(&bp);
+ }
+ }
+ kmem_free(rp->r_bufpp, BUFPSZ(DESCLEN(rp)));
+
+ if (rp->r_descp != NULL) {
+ (void) ddi_dma_unbind_handle(rp->r_dmah);
+ }
+ if (rp->r_acch != NULL) {
+ ddi_dma_mem_free(&rp->r_acch);
+ }
+ if (rp->r_dmah != NULL) {
+ ddi_dma_free_handle(&rp->r_dmah);
+ }
+ kmem_free(rp, sizeof (efe_ring_t));
+
+ *rpp = NULL;
+}
+
+efe_buf_t *
+efe_buf_alloc(dev_info_t *dip, size_t len)
+{
+ efe_buf_t *bp;
+ size_t rlen;
+ uint_t ccount;
+
+ bp = kmem_zalloc(sizeof (efe_buf_t), KM_SLEEP);
+ bp->b_len = len;
+
+ if (ddi_dma_alloc_handle(dip, &efe_dma_attr, DDI_DMA_SLEEP, NULL,
+ &bp->b_dmah) != DDI_SUCCESS) {
+ efe_error(dip, "unable to allocate DMA handle!");
+ goto failure;
+ }
+
+ if (ddi_dma_mem_alloc(bp->b_dmah, len, &efe_buf_acc_attr,
+ DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &bp->b_kaddr, &rlen,
+ &bp->b_acch) != DDI_SUCCESS) {
+ efe_error(dip, "unable to allocate buffer!");
+ goto failure;
+ }
+
+ if (ddi_dma_addr_bind_handle(bp->b_dmah, NULL, bp->b_kaddr,
+ len, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
+ &bp->b_dmac, &ccount) != DDI_DMA_MAPPED) {
+ efe_error(dip, "unable to bind DMA handle to buffer!");
+ goto failure;
+ }
+
+ return (bp);
+
+failure:
+ efe_buf_free(&bp);
+
+ return (NULL);
+}
+
+void
+efe_buf_free(efe_buf_t **bpp)
+{
+ efe_buf_t *bp = *bpp;
+
+ ASSERT(bp != NULL);
+
+ if (bp->b_kaddr != NULL) {
+ (void) ddi_dma_unbind_handle(bp->b_dmah);
+ }
+ if (bp->b_acch != NULL) {
+ ddi_dma_mem_free(&bp->b_acch);
+ }
+ if (bp->b_dmah != NULL) {
+ ddi_dma_free_handle(&bp->b_dmah);
+ }
+ kmem_free(bp, sizeof (efe_buf_t));
+
+ *bpp = NULL;
+}
+
+void
+efe_intr_enable(efe_t *efep)
+{
+ PUTCSR(efep, CSR_INTMASK,
+ INTMASK_RCC | INTMASK_RQE | INTMASK_TXC | INTMASK_FATAL);
+
+ SETBIT(efep, CSR_GENCTL, GENCTL_INT);
+}
+
+void
+efe_intr_disable(efe_t *efep)
+{
+ PUTCSR(efep, CSR_INTMASK, 0);
+
+ CLRBIT(efep, CSR_GENCTL, GENCTL_INT);
+}
+
+mblk_t *
+efe_recv(efe_t *efep)
+{
+ efe_ring_t *rp;
+ mblk_t *mp = NULL;
+ mblk_t **mpp = &mp;
+
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+
+ rp = efep->efe_rx_ring;
+
+ for (;;) {
+ efe_desc_t *dp;
+ uint16_t status;
+
+ dp = GETDESC(rp, efep->efe_rx_desc);
+ SYNCDESC(rp, efep->efe_rx_desc, DDI_DMA_SYNC_FORKERNEL);
+
+ status = GETDESC16(rp, &dp->d_status);
+
+ /* Stop if device owns descriptor */
+ if (status & RXSTAT_OWNER) {
+ break;
+ }
+
+ if (status & RXSTAT_PRI) {
+ mblk_t *tmp = efe_recv_pkt(efep, dp);
+ if (tmp != NULL) {
+ *mpp = tmp;
+ mpp = &tmp->b_next;
+ }
+
+ } else {
+ efep->efe_ierrors++;
+
+ if (status & RXSTAT_FAE) {
+ efep->efe_align_errors++;
+ }
+ if (status & RXSTAT_CRC) {
+ efep->efe_fcs_errors++;
+ }
+ if (status & RXSTAT_MP) {
+ efep->efe_oflo++;
+ }
+ }
+
+ /* Release ownership to device */
+ PUTDESC16(rp, &dp->d_status, RXSTAT_OWNER);
+
+ SYNCDESC(rp, efep->efe_rx_desc, DDI_DMA_SYNC_FORDEV);
+
+ efep->efe_rx_desc = NEXTDESC(rp, efep->efe_rx_desc);
+ }
+
+ return (mp);
+}
+
+mblk_t *
+efe_recv_pkt(efe_t *efep, efe_desc_t *dp)
+{
+ efe_ring_t *rp;
+ efe_buf_t *bp;
+ uint16_t len;
+ mblk_t *mp;
+ uint16_t status;
+
+ ASSERT(mutex_owned(&efep->efe_intrlock));
+
+ rp = efep->efe_rx_ring;
+
+ len = GETDESC16(rp, &dp->d_len) - ETHERFCSL;
+
+ if (len < ETHERMIN) {
+ efep->efe_ierrors++;
+ efep->efe_runt_errors++;
+ return (NULL);
+ }
+
+ if (len > ETHERMAX + VLAN_TAGSZ) {
+ efep->efe_ierrors++;
+ efep->efe_toolong_errors++;
+ return (NULL);
+ }
+
+ mp = allocb(len, 0);
+ if (mp == NULL) {
+ efep->efe_ierrors++;
+ efep->efe_norcvbuf++;
+ return (NULL);
+ }
+ mp->b_wptr = mp->b_rptr + len;
+
+ bp = GETBUF(rp, efep->efe_rx_desc);
+ SYNCBUF(bp, DDI_DMA_SYNC_FORKERNEL);
+
+ bcopy(bp->b_kaddr, mp->b_rptr, len);
+
+ efep->efe_ipackets++;
+ efep->efe_rbytes += len;
+
+ status = GETDESC16(rp, &dp->d_status);
+
+ if (status & RXSTAT_BAR) {
+ efep->efe_brdcstrcv++;
+
+ } else if (status & RXSTAT_MAR) {
+ efep->efe_multircv++;
+ }
+
+ return (mp);
+}
+
+int
+efe_send(efe_t *efep, mblk_t *mp)
+{
+ efe_ring_t *rp;
+ uint16_t len;
+ efe_desc_t *dp;
+ uint16_t status;
+ efe_buf_t *bp;
+
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ rp = efep->efe_tx_ring;
+
+ len = msgsize(mp);
+
+ if (len > ETHERMAX + VLAN_TAGSZ) {
+ efep->efe_oerrors++;
+ efep->efe_macxmt_errors++;
+ freemsg(mp);
+ return (DDI_SUCCESS);
+ }
+
+ dp = GETDESC(rp, efep->efe_tx_desc);
+ SYNCDESC(rp, efep->efe_tx_desc, DDI_DMA_SYNC_FORKERNEL);
+
+ status = GETDESC16(efep->efe_tx_ring, &dp->d_status);
+
+ /* Stop if device owns descriptor */
+ if (status & TXSTAT_OWNER) {
+ return (DDI_FAILURE);
+ }
+
+ bp = GETBUF(rp, efep->efe_tx_desc);
+
+ mcopymsg(mp, bp->b_kaddr);
+
+ /*
+ * Packets must contain at least ETHERMIN octets.
+ * Padded octets are zeroed out prior to sending.
+ */
+ if (len < ETHERMIN) {
+ bzero(bp->b_kaddr + len, ETHERMIN - len);
+ len = ETHERMIN;
+ }
+
+ SYNCBUF(bp, DDI_DMA_SYNC_FORDEV);
+
+ PUTDESC16(rp, &dp->d_status, TXSTAT_OWNER);
+ PUTDESC16(rp, &dp->d_len, len);
+ PUTDESC16(rp, &dp->d_control, TXCTL_LASTDESCR);
+
+ SYNCDESC(rp, efep->efe_tx_desc, DDI_DMA_SYNC_FORDEV);
+
+ efep->efe_opackets++;
+ efep->efe_obytes += len;
+
+ if (*bp->b_kaddr & 0x01) {
+ if (bcmp(bp->b_kaddr, efe_broadcast, ETHERADDRL) == 0) {
+ efep->efe_brdcstxmt++;
+ } else {
+ efep->efe_multixmt++;
+ }
+ }
+
+ efep->efe_tx_desc = NEXTDESC(rp, efep->efe_tx_desc);
+
+ return (DDI_SUCCESS);
+}
+
+void
+efe_send_done(efe_t *efep)
+{
+ efe_ring_t *rp;
+
+ ASSERT(mutex_owned(&efep->efe_txlock));
+
+ rp = efep->efe_tx_ring;
+
+ for (;;) {
+ efe_desc_t *dp;
+ uint16_t status;
+
+ dp = GETDESC(rp, efep->efe_tx_sent);
+ SYNCDESC(rp, efep->efe_tx_sent, DDI_DMA_SYNC_FORKERNEL);
+
+ status = GETDESC16(rp, &dp->d_status);
+
+ /* Stop if device owns descriptor */
+ if (status & TXSTAT_OWNER) {
+ break;
+ }
+
+ if (status & TXSTAT_PTX) {
+ if (!(status & TXSTAT_ND)) {
+ efep->efe_defer_xmts++;
+ }
+ if (status & TXSTAT_COLL) {
+ efep->efe_first_collisions++;
+ }
+
+ } else {
+ efep->efe_oerrors++;
+
+ if (status & TXSTAT_CSL) {
+ efep->efe_carrier_errors++;
+ }
+ if (status & TXSTAT_UFLO) {
+ efep->efe_uflo++;
+ }
+ if (status & TXSTAT_OWC) {
+ efep->efe_tx_late_collisions++;
+ }
+ if (status & TXSTAT_DEFER) {
+ efep->efe_jabber_errors++;
+ }
+ if (status & TXSTAT_EXCOLL) {
+ efep->efe_ex_collisions++;
+ }
+ }
+
+ efep->efe_collisions +=
+ (status >> TXSTAT_CCNT) & TXSTAT_CCNTMASK;
+
+ efep->efe_tx_sent = NEXTDESC(rp, efep->efe_tx_sent);
+ }
+}
+
+void
+efe_getaddr(efe_t *efep, uint8_t *macaddr)
+{
+ efe_eeprom_read(efep, macaddr, ETHERADDRL, 0x0);
+
+ efe_debug(efep->efe_dip,
+ "factory address is %02x:%02x:%02x:%02x:%02x:%02x\n",
+ macaddr[0], macaddr[1], macaddr[2], macaddr[3],
+ macaddr[4], macaddr[5]);
+}
+
+void
+efe_setaddr(efe_t *efep, uint8_t *macaddr)
+{
+ uint16_t val;
+
+ bcopy(macaddr, &val, sizeof (uint16_t));
+ PUTCSR(efep, CSR_LAN0, val);
+ macaddr += sizeof (uint16_t);
+
+ bcopy(macaddr, &val, sizeof (uint16_t));
+ PUTCSR(efep, CSR_LAN1, val);
+ macaddr += sizeof (uint16_t);
+
+ bcopy(macaddr, &val, sizeof (uint16_t));
+ PUTCSR(efep, CSR_LAN2, val);
+}
+
+void
+efe_setmchash(efe_t *efep, uint16_t *mchash)
+{
+ PUTCSR(efep, CSR_MC0, mchash[0]);
+ PUTCSR(efep, CSR_MC1, mchash[1]);
+ PUTCSR(efep, CSR_MC2, mchash[2]);
+ PUTCSR(efep, CSR_MC3, mchash[3]);
+}
+
+void
+efe_eeprom_read(efe_t *efep, uint8_t *buf, size_t len, uint8_t addr)
+{
+ int addrlen;
+
+ ASSERT(len & ~0x1); /* non-zero; word-aligned */
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS);
+ drv_usecwait(EEPROM_DELAY);
+
+ addrlen = (GETCSR(efep, CSR_EECTL) & EECTL_SIZE ?
+ AT93C46_ADDRLEN : AT93C56_ADDRLEN);
+
+ for (int i = 0; i < len / sizeof (uint16_t); ++i) {
+ uint16_t val = efe_eeprom_readw(efep, addrlen, addr + i);
+ bcopy(&val, buf, sizeof (uint16_t));
+ buf += sizeof (uint16_t);
+ }
+}
+
+uint16_t
+efe_eeprom_readw(efe_t *efep, int addrlen, uint8_t addr)
+{
+ uint16_t val = 0;
+
+ ASSERT(addrlen > 0);
+
+ /* Write Start Bit (SB) */
+ efe_eeprom_writebit(efep, 1);
+
+ /* Write READ instruction */
+ efe_eeprom_writebit(efep, 1);
+ efe_eeprom_writebit(efep, 0);
+
+ /* Write EEPROM address */
+ for (int i = addrlen - 1; i >= 0; --i) {
+ efe_eeprom_writebit(efep, addr & 1U << i);
+ }
+
+ /* Read EEPROM word */
+ for (int i = EEPROM_WORDSZ - 1; i >= 0; --i) {
+ val |= efe_eeprom_readbit(efep) << i;
+ }
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE);
+ drv_usecwait(EEPROM_DELAY);
+
+ return (val);
+}
+
+inline int
+efe_eeprom_readbit(efe_t *efep)
+{
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS);
+ drv_usecwait(EEPROM_DELAY);
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS |
+ EECTL_EESK);
+ drv_usecwait(EEPROM_DELAY);
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS);
+ drv_usecwait(EEPROM_DELAY);
+
+ return (!!(GETCSR(efep, CSR_EECTL) & EECTL_EEDO));
+}
+
+inline void
+efe_eeprom_writebit(efe_t *efep, int bit)
+{
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS);
+ drv_usecwait(EEPROM_DELAY);
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS |
+ EECTL_EESK | (bit ? EECTL_EEDI : 0));
+ drv_usecwait(EEPROM_DELAY);
+
+ PUTCSR(efep, CSR_EECTL, EECTL_ENABLE | EECTL_EECS);
+ drv_usecwait(EEPROM_DELAY);
+}
+
+void
+efe_dprintf(dev_info_t *dip, int level, const char *format, ...)
+{
+ va_list ap;
+ char buf[255];
+
+ va_start(ap, format);
+
+ (void) vsnprintf(buf, sizeof (buf), format, ap);
+
+ cmn_err(level, "?%s%d %s", ddi_driver_name(dip),
+ ddi_get_instance(dip), buf);
+
+ va_end(ap);
+}
diff --git a/usr/src/uts/common/io/efe/efe.h b/usr/src/uts/common/io/efe/efe.h
new file mode 100644
index 0000000000..87864ec4b6
--- /dev/null
+++ b/usr/src/uts/common/io/efe/efe.h
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2010 Steven Stallion. 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 copyright owner nor the names of any
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 OWNER 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 _EFE_H
+#define _EFE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VENDOR_ID 0x10B8
+#define DEVICE_ID 0x0005
+
+#define RESET_DELAY 1
+#define RESET_TEST_CYCLES 16
+
+#define STOP_DELAY 10
+#define STOP_DELAY_CYCLES 160
+
+#define MII_DELAY 1
+#define MII_DELAY_CYCLES 16
+
+#define EEPROM_DELAY 3
+#define EEPROM_WORDSZ 16
+
+#define AT93C46_ADDRLEN 6
+#define AT93C56_ADDRLEN 8
+
+#define FLAG_RUNNING (1UL << 0)
+#define FLAG_SUSPENDED (1UL << 1)
+
+#define MCHASHL 64
+#define MCHASHSZ 16
+
+#define BURSTLEN 0x3F
+
+#define RXDESCL 128
+#define TXDESCL 128
+
+#define BUFSZ 1536
+
+/*
+ * Control/Status registers.
+ */
+#define CSR_COMMAND 0x00 /* Control Register */
+#define CSR_INTSTAT 0x04 /* Interrupt Status Register */
+#define CSR_INTMASK 0x08 /* Interrupt Mask Register */
+#define CSR_GENCTL 0x0C /* General Control Register */
+#define CSR_NVCTL 0x10 /* Non-volatile Control Register */
+#define CSR_EECTL 0x14 /* EEPROM Control Register */
+#define CSR_PBLCNT 0x18 /* Programmable Burst Length Counter */
+#define CSR_TEST 0x1C /* Test Register */
+#define CSR_CRCCNT 0x20 /* CRC Error Counter */
+#define CSR_ALICNT 0x24 /* Frame Alignment Error Counter */
+#define CSR_MPCNT 0x28 /* Missed Packet Counter */
+#define CSR_RXFIFO 0x2C /* Receive FIFO Contents */
+#define CSR_MMCTL 0x30 /* MII Control Register */
+#define CSR_MMDATA 0x34 /* MII Interface Register */
+#define CSR_MMCFG 0x38 /* MII Configuration Register */
+#define CSR_IPG 0x3C /* Interpacket Gap Register */
+#define CSR_LAN0 0x40 /* LAN Address Register 0 */
+#define CSR_LAN1 0x44 /* LAN Address Register 1 */
+#define CSR_LAN2 0x48 /* LAN Address Register 2 */
+#define CSR_IDCHK 0x4C /* Board ID/Checksum Register */
+#define CSR_MC0 0x50 /* Multicast Address Register 0 */
+#define CSR_MC1 0x54 /* Multicast Address Register 1 */
+#define CSR_MC2 0x58 /* Multicast Address Register 2 */
+#define CSR_MC3 0x5C /* Multicast Address Register 3 */
+#define CSR_RXCON 0x60 /* Receive Control Register */
+#define CSR_RXSTAT 0x64 /* Receive Status Register */
+#define CSR_RXCNT 0x68 /* Receive Byte Count */
+#define CSR_RXTEST 0x6C /* Receive Test */
+#define CSR_TXCON 0x70 /* Transmit Control Register */
+#define CSR_TXSTAT 0x74 /* Transmit Status Register */
+#define CSR_TDPAR 0x78 /* Transmit Packet Address */
+#define CSR_TXTEST 0x7C /* Transmit Test */
+#define CSR_PRFDAR 0x80 /* PCI Receive First Descriptor Address */
+#define CSR_PRCDAR 0x84 /* PCI Receive Current Descriptor Address */
+#define CSR_PRHDAR 0x88 /* PCI Receive Host Data Address */
+#define CSR_PRFLAR 0x8C /* PCI Receive Fragment List Address */
+#define CSR_PRDLGTH 0x90 /* PCI Receive DMA Length/Control */
+#define CSR_PRFCNT 0x94 /* PCI Receive Fragment Count */
+#define CSR_PRLCAR 0x98 /* PCI Receive RAM Current Address */
+#define CSR_PRLPAR 0x9C /* PCI Receive RAM Packet Address */
+#define CSR_PREFAR 0xA0 /* PCI Receive End of Frame Address */
+#define CSR_PRSTAT 0xA4 /* PCI Receive DMA Status Register */
+#define CSR_PRBUF 0xA8 /* Receive RAM Buffer */
+#define CSR_RDNCAR 0xAC /* Receive MTU Current Address */
+#define CSR_PRCPTHR 0xB0 /* PCI Receive Copy Threshold Register */
+#define CSR_ROMDATA 0xB4 /* ROMDATA */
+#define CSR_PREEMPR 0xBC /* Preemptive Interrupt */
+#define CSR_PTFDAR 0xC0 /* PCI Transmit First Descriptor Address */
+#define CSR_PTCDAR 0xC4 /* PCI Transmit Current Descriptor Address */
+#define CSR_PTHDAR 0xC8 /* PCI Transmit Host Data Address */
+#define CSR_PTFLAR 0xCC /* PCI Transmit Fragment List Address */
+#define CSR_PTDLGTH 0xD0 /* PCI Transmit DMA Length/Control */
+#define CSR_PTFCNT 0xD4 /* PCI Transmit Fragment Count */
+#define CSR_PTLCAR 0xD8 /* PCI Transmit RAM Current Address */
+#define CSR_ETXTHR 0xDC /* PCI Early Transmit Threshold Register */
+#define CSR_PTETXC 0xE0 /* PCI Early Transmit Count */
+#define CSR_PTSTAT 0xE4 /* PCI Transmit DMA Status */
+#define CSR_PTBUF 0xE8 /* Transmit RAM Buffer */
+#define CSR_PTFDAR2 0xEC /* PCI Transmit 2 First Descriptor Address */
+#define CSR_FEVTR 0xF0 /* CardBus (UNUSED) */
+#define CSR_FEVTRMSKR 0xF4 /* CardBus (UNUSED) */
+#define CSR_FPRSTSTR 0xF8 /* CardBus (UNUSED) */
+#define CSR_FFRCEVTR 0xFF /* CardBus (UNUSED) */
+
+/*
+ * Register fields.
+ */
+#define COMMAND_STOP_RX (1UL << 0) /* Stop Receiver */
+#define COMMAND_START_RX (1UL << 1) /* Start Receiver */
+#define COMMAND_TXQUEUED (1UL << 2) /* Queue TX Descriptor */
+#define COMMAND_RXQUEUED (1UL << 3) /* Queue RX Descriptor */
+#define COMMAND_NEXTFRAME (1UL << 4) /* Release RX Frame */
+#define COMMAND_STOP_TDMA (1UL << 5) /* Stop TX DMA */
+#define COMMAND_STOP_RDMA (1UL << 6) /* Stop RX DMA */
+#define COMMAND_TXUGO (1UL << 7) /* Restart Transmission */
+
+#define INTSTAT_RCC (1UL << 0) /* Receive Copy Complete */
+#define INTSTAT_HCC (1UL << 1) /* Header Copy Complete */
+#define INTSTAT_RQE (1UL << 2) /* Receive Queue Empty */
+#define INTSTAT_OVW (1UL << 3) /* Receive Overflow */
+#define INTSTAT_RXE (1UL << 4) /* Receive Error */
+#define INTSTAT_TXC (1UL << 5) /* Transmit Complete */
+#define INTSTAT_TCC (1UL << 6) /* Transmit Chain Complete */
+#define INTSTAT_TQE (1UL << 7) /* Transmit Queue Empty */
+#define INTSTAT_TXU (1UL << 8) /* Transmit Underrun */
+#define INTSTAT_CNT (1UL << 9) /* Counter Overflow */
+#define INTSTAT_PREI (1UL << 10) /* Preemptive Interrupt */
+#define INTSTAT_RCT (1UL << 11) /* Receive Copy Threshold */
+#define INTSTAT_FATAL (1UL << 12) /* Fatal Error */
+#define INTSTAT_PME (1UL << 14) /* Power Management Event */
+#define INTSTAT_GP2 (1UL << 15) /* GPIO Event */
+#define INTSTAT_ACTV (1UL << 16) /* Interrupt Active */
+#define INTSTAT_RXIDLE (1UL << 17) /* Receive Idle */
+#define INTSTAT_TXIDLE (1UL << 18) /* Transmit Idle */
+#define INTSTAT_RCIP (1UL << 19) /* Receive Copy in Progress */
+#define INTSTAT_TCIP (1UL << 20) /* Transmit Copy in Progress */
+#define INTSTAT_RBE (1UL << 21) /* Receive Buffers Empty */
+#define INTSTAT_RCTS (1UL << 22) /* Receive Copy Threshold Status */
+#define INTSTAT_RSV (1UL << 23) /* Receive Status Valid */
+#define INTSTAT_DPE (1UL << 24) /* PCI Data Parity Error */
+#define INTSTAT_APE (1UL << 25) /* PCI Address Parity Error */
+#define INTSTAT_PMA (1UL << 26) /* PCI Master Abort */
+#define INTSTAT_PTA (1UL << 27) /* PCI Target Abort */
+
+#define INTMASK_RCC (1UL << 0) /* Receive Copy Complete */
+#define INTMASK_HCC (1UL << 1) /* Header Copy Complete */
+#define INTMASK_RQE (1UL << 2) /* Receive Queue Empty */
+#define INTMASK_OVW (1UL << 3) /* Receive Overflow */
+#define INTMASK_RXE (1UL << 4) /* Receive Error */
+#define INTMASK_TXC (1UL << 5) /* Transmit Complete */
+#define INTMASK_TCC (1UL << 6) /* Transmit Chain Complete */
+#define INTMASK_TQE (1UL << 7) /* Transmit Queue Empty */
+#define INTMASK_TXU (1UL << 8) /* Transmit Underrun */
+#define INTMASK_CNT (1UL << 9) /* Counter Overflow */
+#define INTMASK_PREI (1UL << 10) /* Preemptive Interrupt */
+#define INTMASK_RCT (1UL << 11) /* Receive Copy Threshold */
+#define INTMASK_FATAL (1UL << 12) /* Fatal Error */
+#define INTMASK_PME (1UL << 14) /* Power Management Event */
+#define INTMASK_GP2 (1UL << 15) /* GPIO Event */
+
+#define GENCTL_RESET (1UL << 0) /* Soft Reset */
+#define GENCTL_INT (1UL << 1) /* Interrupt Enable */
+#define GENCTL_SWINT (1UL << 2) /* Software Interrupt */
+#define GENCTL_PWRDWN (1UL << 3) /* Power Down */
+#define GENCTL_ONECOPY (1UL << 4) /* One Copy per Receive Frame */
+#define GENCTL_BE (1UL << 5) /* Big Endian */
+#define GENCTL_RDP (1UL << 6) /* Receive DMA Priority */
+#define GENCTL_TDP (1UL << 7) /* Transmit DMA Priority */
+#define GENCTL_RFT_32 (0UL << 8) /* Receive FIFO Threshold (1/4) */
+#define GENCTL_RFT_64 (1UL << 8) /* Receive FIFO Threshold (1/2) */
+#define GENCTL_RFT_96 (2UL << 8) /* Receive FIFO Threshold (3/4) */
+#define GENCTL_RFT_128 (3UL << 8) /* Receive FIFO Threshold (FULL) */
+#define GENCTL_MRM (1UL << 10) /* Memory Read Multiple */
+#define GENCTL_MRL (1UL << 11) /* Memory Read Line */
+#define GENCTL_SOFT0 (1UL << 12) /* Software Bit 0 */
+#define GENCTL_SOFT1 (1UL << 13) /* Software Bit 1 */
+#define GENCTL_RSTPHY (1UL << 14) /* PHY Reset */
+#define GENCTL_SCLK (1UL << 16) /* System Clock */
+#define GENCTL_RD (1UL << 17) /* Reset Disable */
+#define GENCTL_MPE (1UL << 18) /* Magic Packet Enable */
+#define GENCTL_PME (1UL << 19) /* PME Interrupt Enable */
+#define GENCTL_PS_00 (0UL << 20) /* Power State "00" */
+#define GENCTL_PS_01 (1UL << 20) /* Power State "01" */
+#define GENCTL_PS_10 (2UL << 20) /* Power State "10" */
+#define GENCTL_PS_11 (3UL << 20) /* Power State "11" */
+#define GENCTL_OPLE (1UL << 22) /* On Power Loss Enable */
+
+#define NVCTL_EMM (1UL << 0) /* Enable Memory Map */
+#define NVCTL_CRS (1UL << 1) /* Clock Run Supported */
+#define NVCTL_GPOE1 (1UL << 2) /* General Purpose Output Enable 1 */
+#define NVCTL_GPOE2 (1UL << 3) /* General Purpose Output Enable 2 */
+#define NVCTL_GPIO1 (1UL << 4) /* General Purpose I/O 1 */
+#define NVCTL_GPIO2 (1UL << 5) /* General Purpose I/O 2 */
+#define NVCTL_CB_MODE (1UL << 6) /* CardBus (UNUSED) */
+#define NVCTL_IPG_DLY 7 /* Inter-packet Gap Timer Delay */
+
+#define EECTL_ENABLE (1UL << 0) /* EEPROM Enable */
+#define EECTL_EECS (1UL << 1) /* EEPROM Chip Select */
+#define EECTL_EESK (1UL << 2) /* EEPROM Clock */
+#define EECTL_EEDI (1UL << 3) /* EEPROM Data Input */
+#define EECTL_EEDO (1UL << 4) /* EEPROM Data Output */
+#define EECTL_EERDY (1UL << 5) /* EEPROM Ready */
+#define EECTL_SIZE (1UL << 6) /* EEPROM Size */
+
+#define TEST_CLOCK (1UL << 3) /* Clock Test */
+
+#define MMCTL_READ (1UL << 0) /* MII Read */
+#define MMCTL_WRITE (1UL << 1) /* MII Write */
+#define MMCTL_RESPONDER (1UL << 3) /* MII Responder */
+#define MMCTL_PHYREG 4 /* PHY Address */
+#define MMCTL_PHYADDR 9 /* PHY Register Address */
+
+#define MMCFG_SME (1UL << 0) /* Serial Mode Enable */
+#define MMCFG_EN694 (1UL << 1) /* EN694 Pin */
+#define MMCFG_694LNK (1UL << 2) /* 694LNK Pin */
+#define MMCFG_PHY (1UL << 3) /* PHY Present */
+#define MMCFG_SMI (1UL << 4) /* Enable Serial Management */
+#define MMCFG_ALTCS (1UL << 5) /* Alternate Clock Source */
+#define MMCFG_ALTDATA (1UL << 6) /* Alternate Data */
+#define MMCFG_STXC (1UL << 14) /* Select TX Clock */
+#define MMCFG_SNTXC (1UL << 15) /* Set No TX Clock */
+
+#define RXCON_SEP (1UL << 0) /* Save Errored Packets */
+#define RXCON_RRF (1UL << 1) /* Receive Runt Frames */
+#define RXCON_RBF (1UL << 2) /* Receive Broadcast Frames */
+#define RXCON_RMF (1UL << 3) /* Receive Multicast Frames */
+#define RXCON_RIIA (1UL << 4) /* Receive Inverse Addresses */
+#define RXCON_PROMISC (1UL << 5) /* Promiscuous Mode */
+#define RXCON_MONITOR (1UL << 6) /* Monitor Mode */
+#define RXCON_ERE (1UL << 7) /* Early Receive Enable */
+#define RXCON_EB_INT (0UL << 8) /* External Buffer (Inernal) */
+#define RXCON_EB_16K (1UL << 8) /* External Buffer (16K) */
+#define RXCON_EB_32K (2UL << 8) /* External Buffer (32K) */
+#define RXCON_EB_128K (3UL << 8) /* External Buffer (128K) */
+
+#define RXSTAT_PRI (1UL << 0) /* Packet Received Intact */
+#define RXSTAT_FAE (1UL << 1) /* Frame Alignment Error */
+#define RXSTAT_CRC (1UL << 2) /* CRC Error */
+#define RXSTAT_MP (1UL << 3) /* Missed Packet */
+#define RXSTAT_MAR (1UL << 4) /* Multicast Address Recognized */
+#define RXSTAT_BAR (1UL << 5) /* Broadcast Address Recognized */
+#define RXSTAT_RD (1UL << 6) /* Receiver Disabled */
+#define RXSTAT_NSV (1UL << 12) /* Network Status Valid */
+#define RXSTAT_FLE (1UL << 13) /* Fragment List Error */
+#define RXSTAT_HC (1UL << 14) /* Header Copied */
+#define RXSTAT_OWNER (1UL << 15) /* Descriptor Ownership Bit */
+
+#define RXCTL_FRAGLIST (1UL << 0) /* Fragment List */
+#define RXCTL_LFFORM (1UL << 1) /* Fragment List Format */
+#define RXCTL_HEADER (1UL << 2) /* Header Copy */
+
+#define TXCON_ETE (1UL << 0) /* Early Transmit Enable */
+#define TXCON_LB_0 (0UL << 1) /* Normal Operation */
+#define TXCON_LB_1 (1UL << 1) /* Internal Loopback */
+#define TXCON_LB_2 (2UL << 1) /* External Loopback */
+#define TXCON_LB_3 (3UL << 1) /* Full Duplex Mode */
+#define TXCON_SLOT 3 /* Slot Time */
+
+#define TXSTAT_PTX (1UL << 0) /* Packet Transmitted */
+#define TXSTAT_ND (1UL << 1) /* Non-deferred Transmission */
+#define TXSTAT_COLL (1UL << 2) /* Transmitted w/Collisions */
+#define TXSTAT_CSL (1UL << 3) /* Carrier Sense Lost */
+#define TXSTAT_UFLO (1UL << 4) /* TX Underrun */
+#define TXSTAT_CDH (1UL << 5) /* Collision Detect Heartbeat */
+#define TXSTAT_OWC (1UL << 6) /* Out of Window Collision */
+#define TXSTAT_DEFER (1UL << 7) /* IGP Deferring */
+#define TXSTAT_CCNT 8 /* Collision Count */
+#define TXSTAT_CCNTMASK 0x1F /* Collision Count Mask */
+#define TXSTAT_EXCOLL (1UL << 12) /* Excessive Collisions */
+#define TXSTAT_OWNER (1UL << 15) /* Descriptor Ownership Bit */
+
+#define TXCTL_FRAGLIST (1UL << 0) /* Fragment List */
+#define TXCTL_LFFORM (1UL << 1) /* Fragment List Format */
+#define TXCTL_IAF (1UL << 2) /* Interrupt After Frame */
+#define TXCTL_NOCRC (1UL << 3) /* Disable CRC Generation */
+#define TXCTL_LASTDESCR (1UL << 4) /* Last Transmit Descriptor */
+
+/*
+ * Register access.
+ */
+#define GETCSR(efep, reg) \
+ ddi_get32((efep)->efe_regs_acch, \
+ (efep)->efe_regs + ((reg) / sizeof (uint32_t)))
+
+#define PUTCSR(efep, reg, val) \
+ ddi_put32((efep)->efe_regs_acch, \
+ (efep)->efe_regs + ((reg) / sizeof (uint32_t)), (val))
+
+#define CLRBIT(efep, reg, bit) \
+ PUTCSR(efep, reg, (GETCSR(efep, reg) & ~(bit)))
+
+#define SETBIT(efep, reg, bit) \
+ PUTCSR(efep, reg, (GETCSR(efep, reg) | (bit)))
+
+/*
+ * DMA access.
+ */
+#define DESCSZ(x) (sizeof (efe_desc_t) * (x))
+#define BUFPSZ(x) (sizeof (efe_buf_t *) * (x))
+
+#define DESCADDR(rp, x) ((rp)->r_dmac.dmac_address + DESCSZ(x))
+#define DESCLEN(rp) ((rp)->r_len)
+
+#define BUFADDR(bp) ((bp)->b_dmac.dmac_address)
+#define BUFLEN(bp) ((bp)->b_len)
+
+#define NEXTDESC(rp, x) (((x) + 1) % (rp)->r_len)
+#define NEXTDESCADDR(rp, x) DESCADDR(rp, NEXTDESC(rp, x))
+
+#define GETDESC(rp, x) (&(rp)->r_descp[(x)])
+
+#define GETDESC16(rp, addr) \
+ ddi_get16((rp)->r_acch, (addr))
+
+#define PUTDESC16(rp, addr, val) \
+ ddi_put16((rp)->r_acch, (addr), (val))
+
+#define GETDESC32(rp, addr) \
+ ddi_get32((rp)->r_acch, (addr))
+
+#define PUTDESC32(rp, addr, val) \
+ ddi_put32((rp)->r_acch, (addr), (val))
+
+#define SYNCDESC(rp, x, type) \
+ (void) ddi_dma_sync((rp)->r_dmah, DESCSZ(x), \
+ sizeof (efe_desc_t), (type))
+
+#define GETBUF(rp, x) ((rp)->r_bufpp[(x)])
+
+#define SYNCBUF(bp, type) \
+ (void) ddi_dma_sync((bp)->b_dmah, 0, (bp)->b_len, (type))
+
+/*
+ * Soft state.
+ */
+typedef struct {
+ uint16_t d_status;
+ uint16_t d_len;
+ uint32_t d_bufaddr;
+ uint16_t d_buflen;
+ uint16_t d_control;
+ uint32_t d_next;
+} efe_desc_t;
+
+typedef struct {
+ ddi_dma_handle_t b_dmah;
+ ddi_acc_handle_t b_acch;
+ ddi_dma_cookie_t b_dmac;
+ size_t b_len;
+ caddr_t b_kaddr;
+} efe_buf_t;
+
+typedef struct {
+ ddi_dma_handle_t r_dmah;
+ ddi_acc_handle_t r_acch;
+ ddi_dma_cookie_t r_dmac;
+ size_t r_len;
+ efe_desc_t *r_descp;
+ efe_buf_t **r_bufpp;
+} efe_ring_t;
+
+typedef struct {
+ dev_info_t *efe_dip;
+
+ mii_handle_t efe_miih;
+ mac_handle_t efe_mh;
+
+ uint32_t *efe_regs;
+ ddi_acc_handle_t efe_regs_acch;
+
+ ddi_intr_handle_t efe_intrh;
+
+ kmutex_t efe_intrlock;
+ kmutex_t efe_txlock;
+
+ int efe_flags;
+ boolean_t efe_promisc;
+
+ uint8_t efe_macaddr[ETHERADDRL];
+
+ uint_t efe_mccount[MCHASHL];
+ uint16_t efe_mchash[MCHASHL / MCHASHSZ];
+
+ efe_ring_t *efe_rx_ring;
+ uint_t efe_rx_desc;
+
+ efe_ring_t *efe_tx_ring;
+ uint_t efe_tx_desc;
+ uint_t efe_tx_sent;
+
+ /*
+ * Driver statistics.
+ */
+ uint64_t efe_multircv;
+ uint64_t efe_brdcstrcv;
+ uint64_t efe_multixmt;
+ uint64_t efe_brdcstxmt;
+ uint64_t efe_norcvbuf;
+ uint64_t efe_ierrors;
+ uint64_t efe_noxmtbuf;
+ uint64_t efe_oerrors;
+ uint64_t efe_collisions;
+ uint64_t efe_rbytes;
+ uint64_t efe_ipackets;
+ uint64_t efe_obytes;
+ uint64_t efe_opackets;
+ uint64_t efe_uflo;
+ uint64_t efe_oflo;
+ uint64_t efe_align_errors;
+ uint64_t efe_fcs_errors;
+ uint64_t efe_first_collisions;
+ uint64_t efe_tx_late_collisions;
+ uint64_t efe_defer_xmts;
+ uint64_t efe_ex_collisions;
+ uint64_t efe_macxmt_errors;
+ uint64_t efe_carrier_errors;
+ uint64_t efe_toolong_errors;
+ uint64_t efe_macrcv_errors;
+ uint64_t efe_runt_errors;
+ uint64_t efe_jabber_errors;
+} efe_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EFE_H */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 24f5056efd..f38a85a9b5 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -388,6 +388,7 @@ DRV_KMODS += atge
DRV_KMODS += bfe
DRV_KMODS += dmfe
DRV_KMODS += e1000g
+DRV_KMODS += efe
DRV_KMODS += elxl
DRV_KMODS += hme
DRV_KMODS += mxfe
diff --git a/usr/src/uts/intel/efe/Makefile b/usr/src/uts/intel/efe/Makefile
new file mode 100644
index 0000000000..602037b49b
--- /dev/null
+++ b/usr/src/uts/intel/efe/Makefile
@@ -0,0 +1,67 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = efe
+OBJECTS = $(EFE_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(EFE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Driver flags.
+#
+CFLAGS += $(CCVERBOSE)
+LDFLAGS += -dy -N misc/mac -N misc/mii
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/os/master b/usr/src/uts/intel/os/master
index 4969ae65aa..b860e1b405 100644
--- a/usr/src/uts/intel/os/master
+++ b/usr/src/uts/intel/os/master
@@ -300,7 +300,6 @@ pci8086,1229.8086.1016 pci8086,1229 net pci iprb.bef "Intel Pro100/B Fast Ethern
pci8086,1229.8086.1017 pci8086,1229 net pci iprb.bef "Intel Pro100/B Fast Ethernet"
pci8086,2449 pci8086,2449 net pci iprb.bef "Intel i815/82559 Ethernet"
pci8086,27dc pci8086,27dc net pci iprb.bef "Intel ICH7 82801G Ethernet"
-pci10b8,5 pci10b8,5 net pci spwr.bef "SMC EtherPower II 10/100 (9432)"
pci101a,f2d scsi msd pci none "NCR PDS/PQS 53C896-based PCI SCSI Adapter" compatible="pci101a,f2d"
pci101a,f2e scsi msd pci none "NCR PDS/PQS 53C896-based PCI SCSI Adapter" compatible="pci101a,f2e"
pci101a,f2f scsi msd pci none "NCR PDS/PQS 53C896-based PCI SCSI Adapter" compatible="pci101a,f2f"
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index b1c511e568..74b65ecd5e 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -249,6 +249,7 @@ DRV_KMODS += audiop16x
DRV_KMODS += audiopci
DRV_KMODS += audiots
DRV_KMODS += e1000g
+DRV_KMODS += efe
DRV_KMODS += hxge
DRV_KMODS += mxfe
DRV_KMODS += pcan
diff --git a/usr/src/uts/sparc/efe/Makefile b/usr/src/uts/sparc/efe/Makefile
new file mode 100644
index 0000000000..c26d8c875e
--- /dev/null
+++ b/usr/src/uts/sparc/efe/Makefile
@@ -0,0 +1,67 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = efe
+OBJECTS = $(EFE_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(EFE_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)
+
+#
+# Driver flags.
+#
+CFLAGS += $(CCVERBOSE)
+LDFLAGS += -dy -N misc/mac -N misc/mii
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ