--- /dev/null	Tue May  3 20:52:16 2016
+++ new/usr/src/uts/common/io/sfxge/sfxge_mac.c	Tue May  3 20:52:16 2016
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2008-2015 Solarflare Communications Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+/*
+ * All efx_mac_*() must be after efx_port_init()
+ * LOCKING STRATEGY: Aquire sm_lock and test sm_state==SFXGE_MAC_STARTED
+ * to serialise against sfxge_restart()
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include "sfxge.h"
+#include "efx.h"
+
+#define	SFXGE_MAC_POLL_PERIOD_MS 1000
+
+static void sfxge_mac_link_update_locked(sfxge_t *sp, efx_link_mode_t mode);
+
+
+/* MAC DMA attributes */
+static ddi_device_acc_attr_t sfxge_mac_devacc = {
+
+	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 sfxge_mac_dma_attr = {
+	DMA_ATTR_V0,		/* dma_attr_version	*/
+	0,			/* dma_attr_addr_lo	*/
+	0xffffffffffffffffull,	/* dma_attr_addr_hi	*/
+	0xffffffffffffffffull,	/* dma_attr_count_max	*/
+	0x1000,			/* dma_attr_align	*/
+	0xffffffff,		/* dma_attr_burstsizes	*/
+	1,			/* dma_attr_minxfer	*/
+	0xffffffffffffffffull,	/* dma_attr_maxxfer	*/
+	0xffffffffffffffffull,	/* dma_attr_seg		*/
+	1,			/* dma_attr_sgllen	*/
+	1,			/* dma_attr_granular	*/
+	0			/* dma_attr_flags	*/
+};
+
+
+static void
+_sfxge_mac_stat_update(sfxge_mac_t *smp, int tries, int delay_usec)
+{
+	sfxge_t *sp = smp->sm_sp;
+	efsys_mem_t *esmp = &(smp->sm_mem);
+	int i;
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+	ASSERT3U(smp->sm_state, !=, SFXGE_MAC_UNINITIALIZED);
+
+	/* if no stats pending then they are already freshly updated */
+	if (smp->sm_mac_stats_timer_reqd && !smp->sm_mac_stats_pend)
+		return;
+
+	for (i = 0; i < tries; i++) {
+		/* Try to update the cached counters */
+		if (efx_mac_stats_update(sp->s_enp, esmp, smp->sm_stat,
+		    NULL) != EAGAIN)
+			goto done;
+
+		drv_usecwait(delay_usec);
+	}
+
+	DTRACE_PROBE(mac_stat_timeout);
+	dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "MAC stats timeout");
+	return;
+
+done:
+	smp->sm_mac_stats_pend = B_FALSE;
+	smp->sm_lbolt = ddi_get_lbolt();
+}
+
+static void
+sfxge_mac_stat_update_quick(sfxge_mac_t *smp)
+{
+	/*
+	 * Update the statistics from the most recent DMA. This might race
+	 * with an inflight dma, so retry once. Otherwise get mac stat
+	 * values from the last mac_poll() or MC periodic stats.
+	 */
+	_sfxge_mac_stat_update(smp, 2, 50);
+}
+
+static void
+sfxge_mac_stat_update_wait(sfxge_mac_t *smp)
+{
+	/* Wait a max of 20 * 500us = 10ms */
+	_sfxge_mac_stat_update(smp, 20, 500);
+}
+
+static int
+sfxge_mac_kstat_update(kstat_t *ksp, int rw)
+{
+	sfxge_mac_t *smp = ksp->ks_private;
+	kstat_named_t *knp;
+	int rc;
+	unsigned int val;
+	sfxge_rx_coalesce_mode_t rxmode;
+
+	if (rw != KSTAT_READ) {
+		rc = EACCES;
+		goto fail1;
+	}
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+
+	if (smp->sm_state != SFXGE_MAC_STARTED)
+		goto done;
+
+	sfxge_mac_stat_update_quick(smp);
+
+	knp = smp->sm_stat;
+	knp += EFX_MAC_NSTATS;
+
+	knp->value.ui64 = (smp->sm_link_up) ? 1 : 0;
+	knp++;
+
+	knp->value.ui64 = smp->sm_link_speed;
+	knp++;
+
+	knp->value.ui64 = smp->sm_link_duplex;
+	knp++;
+
+	knp->value.ui64 = (smp->sm_fcntl & EFX_FCNTL_GENERATE) ? 1 : 0;
+	knp++;
+
+	knp->value.ui64 = (smp->sm_fcntl & EFX_FCNTL_RESPOND) ? 1 : 0;
+	knp++;
+
+	sfxge_ev_moderation_get(smp->sm_sp, &val);
+	knp->value.ui64 = val;
+	knp++;
+
+	sfxge_rx_coalesce_mode_get(smp->sm_sp, &rxmode);
+	knp->value.ui64 = (uint64_t)rxmode;
+	knp++;
+
+	if (sfxge_rx_scale_count_get(smp->sm_sp, &val) != 0)
+		val = 0;
+	knp->value.ui64 = val;
+	knp++;
+
+done:
+	return (0);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+static int
+sfxge_mac_kstat_init(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	dev_info_t *dip = sp->s_dip;
+	char name[MAXNAMELEN];
+	kstat_t *ksp;
+	kstat_named_t *knp;
+	unsigned int id;
+	int rc;
+
+	/* Create the set */
+	(void) snprintf(name, MAXNAMELEN - 1, "%s_mac", ddi_driver_name(dip));
+
+	if ((ksp = kstat_create((char *)ddi_driver_name(dip),
+	    ddi_get_instance(dip), name, "mac", KSTAT_TYPE_NAMED,
+	    EFX_MAC_NSTATS + 8, 0)) == NULL) {
+		rc = ENOMEM;
+		goto fail1;
+	}
+
+	smp->sm_ksp = ksp;
+
+	ksp->ks_update = sfxge_mac_kstat_update;
+	ksp->ks_private = smp;
+	ksp->ks_lock = &(smp->sm_lock);
+
+	/* Initialise the named stats */
+	smp->sm_stat = knp = ksp->ks_data;
+	for (id = 0; id < EFX_MAC_NSTATS; id++) {
+		kstat_named_init(knp, (char *)efx_mac_stat_name(sp->s_enp, id),
+		    KSTAT_DATA_UINT64);
+		knp++;
+	}
+
+	kstat_named_init(knp++, "link_up", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "link_speed", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "link_duplex", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "fcntl_generate", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "fcntl_respond", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "intr_moderation", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "rx_coalesce_mode", KSTAT_DATA_UINT64);
+	kstat_named_init(knp++, "rx_scale_count", KSTAT_DATA_UINT64);
+
+	kstat_install(ksp);
+
+	return (0);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+static void
+sfxge_mac_kstat_fini(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	/* Destroy the set */
+	kstat_delete(smp->sm_ksp);
+	smp->sm_ksp = NULL;
+	smp->sm_stat = NULL;
+}
+
+void
+sfxge_mac_stat_get(sfxge_t *sp, unsigned int id, uint64_t *valp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	/* Make sure the cached counter values are recent */
+	mutex_enter(&(smp->sm_lock));
+
+	if (smp->sm_state != SFXGE_MAC_STARTED)
+		goto done;
+
+	sfxge_mac_stat_update_quick(smp);
+
+	*valp = smp->sm_stat[id].value.ui64;
+
+done:
+	mutex_exit(&(smp->sm_lock));
+}
+
+static void
+sfxge_mac_poll(void *arg)
+{
+	sfxge_t *sp = arg;
+	efx_nic_t *enp = sp->s_enp;
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efsys_mem_t *esmp = &(smp->sm_mem);
+	efx_link_mode_t mode;
+	clock_t timeout;
+
+	mutex_enter(&(smp->sm_lock));
+	while (smp->sm_state == SFXGE_MAC_STARTED) {
+
+		/* clears smp->sm_mac_stats_pend if appropriate */
+		if (smp->sm_mac_stats_pend)
+			sfxge_mac_stat_update_wait(smp);
+
+		/* This may sleep waiting for MCDI completion */
+		mode = EFX_LINK_UNKNOWN;
+		if (efx_port_poll(enp, &mode) == 0)
+			sfxge_mac_link_update_locked(sp, mode);
+
+		if ((smp->sm_link_poll_reqd == B_FALSE) &&
+		    (smp->sm_mac_stats_timer_reqd == B_FALSE))
+			goto done;
+
+		/* Zero the memory */
+		bzero(esmp->esm_base, EFX_MAC_STATS_SIZE);
+
+		/* Trigger upload the MAC statistics counters */
+		if (smp->sm_link_up &&
+		    efx_mac_stats_upload(sp->s_enp, esmp) == 0)
+			smp->sm_mac_stats_pend = B_TRUE;
+
+		/* Wait for timeout or end of polling */
+		timeout = ddi_get_lbolt() + drv_usectohz(1000 *
+		    SFXGE_MAC_POLL_PERIOD_MS);
+		while (smp->sm_state == SFXGE_MAC_STARTED) {
+			if (cv_timedwait(&(smp->sm_link_poll_kv),
+			    &(smp->sm_lock), timeout) < 0) {
+				/* Timeout - poll if polling still enabled */
+				break;
+			}
+		}
+	}
+done:
+	mutex_exit(&(smp->sm_lock));
+
+}
+
+static void
+sfxge_mac_poll_start(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_STARTED);
+
+	/* Schedule a poll */
+	(void) ddi_taskq_dispatch(smp->sm_tqp, sfxge_mac_poll, sp, DDI_SLEEP);
+}
+
+static void
+sfxge_mac_poll_stop(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED);
+
+	cv_broadcast(&(smp->sm_link_poll_kv));
+
+	/* Wait for link polling to cease */
+	mutex_exit(&(smp->sm_lock));
+	ddi_taskq_wait(smp->sm_tqp);
+	mutex_enter(&(smp->sm_lock));
+
+	/* Collect the final statistics. */
+	sfxge_mac_stat_update_wait(smp);
+}
+
+int
+sfxge_mac_init(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efsys_mem_t *esmp = &(smp->sm_mem);
+	dev_info_t *dip = sp->s_dip;
+	sfxge_dma_buffer_attr_t dma_attr;
+	const efx_nic_cfg_t *encp;
+	unsigned char *bytes;
+	unsigned int n;
+	int err, rc;
+
+	SFXGE_OBJ_CHECK(smp, sfxge_mac_t);
+
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_UNINITIALIZED);
+
+	smp->sm_sp = sp;
+	encp = efx_nic_cfg_get(sp->s_enp);
+	smp->sm_link_poll_reqd = (~encp->enc_features &
+	    EFX_FEATURE_LINK_EVENTS);
+	smp->sm_mac_stats_timer_reqd = (~encp->enc_features &
+	    EFX_FEATURE_PERIODIC_MAC_STATS);
+
+	mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(sp->s_intr.si_intr_pri));
+	cv_init(&(smp->sm_link_poll_kv), NULL, CV_DRIVER, NULL);
+
+	/* Create link poll taskq */
+	smp->sm_tqp = ddi_taskq_create(dip, "mac_tq", 1, TASKQ_DEFAULTPRI, 0);
+	if (smp->sm_tqp == NULL) {
+		rc = ENOMEM;
+		goto fail1;
+	}
+
+	if ((rc = sfxge_phy_init(sp)) != 0)
+		goto fail2;
+
+	dma_attr.sdba_dip	 = dip;
+	dma_attr.sdba_dattrp	 = &sfxge_mac_dma_attr;
+	dma_attr.sdba_callback	 = DDI_DMA_SLEEP;
+	dma_attr.sdba_length	 = EFX_MAC_STATS_SIZE;
+	dma_attr.sdba_memflags	 = DDI_DMA_CONSISTENT;
+	dma_attr.sdba_devaccp	 = &sfxge_mac_devacc;
+	dma_attr.sdba_bindflags	 = DDI_DMA_READ | DDI_DMA_CONSISTENT;
+	dma_attr.sdba_maxcookies = 1;
+	dma_attr.sdba_zeroinit	 = B_TRUE;
+
+	if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
+		goto fail3;
+
+	/* Set the initial flow control values */
+	smp->sm_fcntl = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
+
+	/*
+	 * Determine the 'burnt-in' MAC address:
+	 *
+	 * A: if the "mac-address" property is set on our device node use that.
+	 * B: otherwise, use the value from NVRAM.
+	 */
+
+	/* A: property  */
+	err = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+	    "mac-address", &bytes, &n);
+	switch (err) {
+	case DDI_PROP_SUCCESS:
+		if (n == ETHERADDRL) {
+			bcopy(bytes, smp->sm_bia, ETHERADDRL);
+			goto done;
+		}
+
+		ddi_prop_free(bytes);
+		break;
+
+	default:
+		break;
+	}
+
+	/* B: NVRAM */
+	bcopy(encp->enc_mac_addr, smp->sm_bia, ETHERADDRL);
+
+done:
+	/* Initialize the statistics */
+	if ((rc = sfxge_mac_kstat_init(sp)) != 0)
+		goto fail4;
+
+	if ((rc = sfxge_phy_kstat_init(sp)) != 0)
+		goto fail5;
+
+	smp->sm_state = SFXGE_MAC_INITIALIZED;
+
+	return (0);
+
+fail5:
+	DTRACE_PROBE(fail5);
+
+	sfxge_mac_kstat_fini(sp);
+fail4:
+	DTRACE_PROBE(fail4);
+
+	/* Tear down DMA setup */
+	sfxge_dma_buffer_destroy(esmp);
+fail3:
+	DTRACE_PROBE(fail3);
+
+	sfxge_phy_fini(sp);
+fail2:
+	DTRACE_PROBE(fail2);
+
+	/* Destroy the link poll taskq */
+	ddi_taskq_destroy(smp->sm_tqp);
+	smp->sm_tqp = NULL;
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	cv_destroy(&(smp->sm_link_poll_kv));
+
+	mutex_destroy(&(smp->sm_lock));
+
+	smp->sm_sp = NULL;
+
+	return (rc);
+}
+
+static int
+sfxge_mac_filter_apply(sfxge_t *sp)
+{
+	efx_nic_t *enp = sp->s_enp;
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int rc;
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+
+	if (smp->sm_state == SFXGE_MAC_STARTED) {
+		boolean_t all_unicst;
+		boolean_t mulcst;
+		boolean_t all_mulcst;
+		boolean_t brdcst;
+
+		all_unicst = (smp->sm_promisc == SFXGE_PROMISC_ALL_PHYS);
+		mulcst = (smp->sm_mcast_count > 0);
+		all_mulcst = (smp->sm_promisc >= SFXGE_PROMISC_ALL_MULTI);
+		brdcst = B_TRUE;
+
+		if ((rc = efx_mac_filter_set(enp, all_unicst, mulcst,
+		    all_mulcst, brdcst)) != 0) {
+			goto fail1;
+		}
+		if ((rc = efx_mac_multicast_list_set(enp,
+		    smp->sm_mcast_addr, smp->sm_mcast_count)) != 0)
+			goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	DTRACE_PROBE(fail2);
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+int
+sfxge_mac_start(sfxge_t *sp, boolean_t restart)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efsys_mem_t *esmp = &(smp->sm_mem);
+	efx_nic_t *enp = sp->s_enp;
+	size_t pdu;
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED);
+
+	if ((rc = efx_port_init(enp)) != 0)
+		goto fail1;
+
+	/*
+	 * Set up the advertised capabilities that may have been asked for
+	 * before the call to efx_port_init().
+	 */
+	if ((rc = sfxge_phy_cap_apply(sp, !restart)) != 0)
+		goto fail2;
+
+	/* Set the SDU */
+	pdu = EFX_MAC_PDU(sp->s_mtu);
+	if ((rc = efx_mac_pdu_set(enp, pdu)) != 0)
+		goto fail3;
+
+	if ((rc = efx_mac_fcntl_set(enp, smp->sm_fcntl, B_TRUE)) != 0)
+		goto fail4;
+
+	/* Set the unicast address */
+	if ((rc = efx_mac_addr_set(enp, (smp->sm_laa_valid) ?
+	    smp->sm_laa : smp->sm_bia)) != 0)
+		goto fail5;
+
+	if ((rc = sfxge_mac_filter_apply(sp)) != 0)
+		goto fail6;
+
+	if (!smp->sm_mac_stats_timer_reqd) {
+		if ((rc = efx_mac_stats_periodic(enp, esmp,
+		    SFXGE_MAC_POLL_PERIOD_MS, B_FALSE)) != 0)
+			goto fail7;
+	}
+
+	if ((rc = efx_mac_drain(enp, B_FALSE)) != 0)
+		goto fail8;
+
+	smp->sm_state = SFXGE_MAC_STARTED;
+
+	/*
+	 * Start link state polling. For hardware that reports link change
+	 * events we still poll once to update the initial link state.
+	 */
+	sfxge_mac_poll_start(sp);
+
+	mutex_exit(&(smp->sm_lock));
+	return (0);
+
+fail8:
+	DTRACE_PROBE(fail8);
+	(void) efx_mac_stats_periodic(enp, esmp, 0, B_FALSE);
+fail7:
+	DTRACE_PROBE(fail7);
+fail6:
+	DTRACE_PROBE(fail6);
+fail5:
+	DTRACE_PROBE(fail5);
+fail4:
+	DTRACE_PROBE(fail4);
+fail3:
+	DTRACE_PROBE(fail3);
+fail2:
+	DTRACE_PROBE(fail2);
+	efx_port_fini(enp);
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+
+static void
+sfxge_mac_link_update_locked(sfxge_t *sp, efx_link_mode_t mode)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	const char *change, *duplex;
+	char info[sizeof (": now 10000Mbps FULL duplex")];
+
+	ASSERT(mutex_owned(&(smp->sm_lock)));
+	if (smp->sm_state != SFXGE_MAC_STARTED)
+		return;
+
+	if (smp->sm_link_mode == mode)
+		return;
+
+	smp->sm_link_mode = mode;
+	smp->sm_link_up = B_TRUE;
+
+	switch (smp->sm_link_mode) {
+	case EFX_LINK_UNKNOWN:
+	case EFX_LINK_DOWN:
+		smp->sm_link_speed = 0;
+		smp->sm_link_duplex = SFXGE_LINK_DUPLEX_UNKNOWN;
+		smp->sm_link_up = B_FALSE;
+		break;
+
+	case EFX_LINK_10HDX:
+	case EFX_LINK_10FDX:
+		smp->sm_link_speed = 10;
+		smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_10HDX) ?
+		    SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL;
+		break;
+
+	case EFX_LINK_100HDX:
+	case EFX_LINK_100FDX:
+		smp->sm_link_speed = 100;
+		smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_100HDX) ?
+		    SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL;
+		break;
+
+	case EFX_LINK_1000HDX:
+	case EFX_LINK_1000FDX:
+		smp->sm_link_speed = 1000;
+		smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_1000HDX) ?
+		    SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL;
+		break;
+
+	case EFX_LINK_10000FDX:
+		smp->sm_link_speed = 10000;
+		smp->sm_link_duplex = SFXGE_LINK_DUPLEX_FULL;
+		break;
+
+	case EFX_LINK_40000FDX:
+		smp->sm_link_speed = 40000;
+		smp->sm_link_duplex = SFXGE_LINK_DUPLEX_FULL;
+		break;
+
+	default:
+		ASSERT(B_FALSE);
+		break;
+	}
+
+	duplex = (smp->sm_link_duplex == SFXGE_LINK_DUPLEX_FULL) ?
+	    "full" : "half";
+	change = (smp->sm_link_up) ? "UP" : "DOWN";
+	(void) snprintf(info, sizeof (info), ": now %dMbps %s duplex",
+	    smp->sm_link_speed, duplex);
+
+	dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "Link %s%s",
+	    change, smp->sm_link_up ? info : "");
+
+	/* Push link state update to the OS */
+	sfxge_gld_link_update(sp);
+}
+
+void
+sfxge_mac_link_update(sfxge_t *sp, efx_link_mode_t mode)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	mutex_enter(&(smp->sm_lock));
+	sfxge_mac_link_update_locked(sp, mode);
+	mutex_exit(&(smp->sm_lock));
+}
+
+void
+sfxge_mac_link_check(sfxge_t *sp, boolean_t *upp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	mutex_enter(&(smp->sm_lock));
+	*upp = smp->sm_link_up;
+	mutex_exit(&(smp->sm_lock));
+}
+
+void
+sfxge_mac_link_speed_get(sfxge_t *sp, unsigned int *speedp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	mutex_enter(&(smp->sm_lock));
+	*speedp = smp->sm_link_speed;
+	mutex_exit(&(smp->sm_lock));
+}
+
+void
+sfxge_mac_link_duplex_get(sfxge_t *sp, sfxge_link_duplex_t *duplexp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	mutex_enter(&(smp->sm_lock));
+	*duplexp = smp->sm_link_duplex;
+	mutex_exit(&(smp->sm_lock));
+}
+
+void
+sfxge_mac_fcntl_get(sfxge_t *sp, unsigned int *fcntlp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+
+	mutex_enter(&(smp->sm_lock));
+	*fcntlp = smp->sm_fcntl;
+	mutex_exit(&(smp->sm_lock));
+}
+
+int
+sfxge_mac_fcntl_set(sfxge_t *sp, unsigned int fcntl)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	if (smp->sm_fcntl == fcntl)
+		goto done;
+
+	smp->sm_fcntl = fcntl;
+
+	if (smp->sm_state != SFXGE_MAC_STARTED)
+		goto done;
+
+	if ((rc = efx_mac_fcntl_set(sp->s_enp, smp->sm_fcntl, B_TRUE)) != 0)
+		goto fail1;
+
+done:
+	mutex_exit(&(smp->sm_lock));
+
+	return (0);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+int
+sfxge_mac_unicst_get(sfxge_t *sp, sfxge_unicst_type_t type, uint8_t *addr)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int rc;
+
+	if (type >= SFXGE_UNICST_NTYPES) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	mutex_enter(&(smp->sm_lock));
+
+	if (smp->sm_state != SFXGE_MAC_INITIALIZED &&
+	    smp->sm_state != SFXGE_MAC_STARTED) {
+		rc = EFAULT;
+		goto fail2;
+	}
+
+	switch (type) {
+	case SFXGE_UNICST_BIA:
+		bcopy(smp->sm_bia, addr, ETHERADDRL);
+		break;
+
+	case SFXGE_UNICST_LAA:
+		if (!(smp->sm_laa_valid)) {
+			rc = ENOENT;
+			goto fail3;
+		}
+
+		bcopy(smp->sm_laa, addr, ETHERADDRL);
+		break;
+
+	default:
+		ASSERT(B_FALSE);
+		break;
+	}
+
+	mutex_exit(&(smp->sm_lock));
+
+	return (0);
+
+
+fail3:
+	DTRACE_PROBE(fail3);
+fail2:
+	DTRACE_PROBE(fail2);
+
+	mutex_exit(&(smp->sm_lock));
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+int
+sfxge_mac_unicst_set(sfxge_t *sp, uint8_t *addr)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efx_nic_t *enp = sp->s_enp;
+	boolean_t old_mac_valid;
+	uint8_t old_mac[ETHERADDRL];
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	old_mac_valid = smp->sm_laa_valid;
+	if (old_mac_valid)
+		bcopy(smp->sm_laa, old_mac, ETHERADDRL);
+
+	bcopy(addr, smp->sm_laa, ETHERADDRL);
+	smp->sm_laa_valid = B_TRUE;
+
+	if (smp->sm_state != SFXGE_MAC_STARTED)
+		goto done;
+
+	if (efx_nic_cfg_get(enp)->enc_allow_set_mac_with_installed_filters) {
+		if ((rc = efx_mac_addr_set(enp, smp->sm_laa)) != 0) {
+			dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR
+			    "unable to set unicast MAC filter");
+			goto fail1;
+		}
+	} else {
+		/* Older EF10 firmware requires a device start */
+		mutex_exit(&smp->sm_lock);
+		sfxge_stop(sp);
+		if ((rc = sfxge_start(sp, B_TRUE)) != 0) {
+			dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR
+			    "unable to restart with a new MAC");
+			mutex_enter(&(smp->sm_lock));
+			goto fail1;
+		}
+		mutex_enter(&smp->sm_lock);
+	}
+
+	if ((rc = efx_mac_addr_set(enp, smp->sm_laa)) != 0)
+		goto fail1;
+
+done:
+	mutex_exit(&(smp->sm_lock));
+
+	return (0);
+
+fail1:
+	if (old_mac_valid)
+		bcopy(old_mac, smp->sm_laa, ETHERADDRL);
+	else
+		smp->sm_laa_valid = B_FALSE;
+
+	DTRACE_PROBE1(fail1, int, rc);
+
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+int
+sfxge_mac_promisc_set(sfxge_t *sp, sfxge_promisc_type_t promisc)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	if (smp->sm_promisc == promisc)
+		goto done;
+
+	smp->sm_promisc = promisc;
+
+	if ((rc = sfxge_mac_filter_apply(sp)) != 0)
+		goto fail1;
+
+done:
+	mutex_exit(&(smp->sm_lock));
+	return (0);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+int
+sfxge_mac_multicst_add(sfxge_t *sp, const uint8_t *addr)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int i;
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	if ((addr[0] & 0x1) == 0) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	/* Check if the address is already in the list */
+	i = 0;
+	while (i < smp->sm_mcast_count) {
+		if (bcmp(smp->sm_mcast_addr + (i * ETHERADDRL),
+		    addr, ETHERADDRL) == 0)
+			goto done;
+		else
+			i++;
+	}
+
+	if (smp->sm_mcast_count >= EFX_MAC_MULTICAST_LIST_MAX) {
+		rc = ENOENT;
+		goto fail1;
+	}
+
+	/* Add to the list */
+	bcopy(addr, smp->sm_mcast_addr + (smp->sm_mcast_count++ * ETHERADDRL),
+	    ETHERADDRL);
+
+	if ((rc = sfxge_mac_filter_apply(sp)) != 0)
+		goto fail2;
+
+done:
+	mutex_exit(&(smp->sm_lock));
+	return (0);
+
+fail2:
+	DTRACE_PROBE(fail2);
+	smp->sm_mcast_count--;
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+int
+sfxge_mac_multicst_remove(sfxge_t *sp, const uint8_t *addr)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	int i;
+	int rc;
+
+	mutex_enter(&(smp->sm_lock));
+
+	i = 0;
+	while (i < smp->sm_mcast_count) {
+		if (bcmp(smp->sm_mcast_addr + (i * ETHERADDRL),
+		    addr, ETHERADDRL) == 0) {
+			(void) memmove(smp->sm_mcast_addr + (i * ETHERADDRL),
+			    smp->sm_mcast_addr + ((i + 1) * ETHERADDRL),
+			    (smp->sm_mcast_count - (i + 1)) * ETHERADDRL);
+			smp->sm_mcast_count--;
+		} else
+			i++;
+	}
+
+	if ((rc = sfxge_mac_filter_apply(sp)) != 0)
+		goto fail1;
+
+	mutex_exit(&(smp->sm_lock));
+	return (0);
+
+fail1:
+	DTRACE_PROBE1(fail1, int, rc);
+	mutex_exit(&(smp->sm_lock));
+
+	return (rc);
+}
+
+void
+sfxge_mac_stop(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efx_nic_t *enp = sp->s_enp;
+	efsys_mem_t *esmp = &(smp->sm_mem);
+
+	mutex_enter(&(smp->sm_lock));
+
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_STARTED);
+	ASSERT3P(smp->sm_sp, ==, sp);
+	smp->sm_state = SFXGE_MAC_INITIALIZED;
+
+	/* If stopping in response to an MC reboot this may fail */
+	if (!smp->sm_mac_stats_timer_reqd)
+		(void) efx_mac_stats_periodic(enp, esmp, 0, B_FALSE);
+
+	sfxge_mac_poll_stop(sp);
+
+	smp->sm_lbolt = 0;
+
+	smp->sm_link_up = B_FALSE;
+	smp->sm_link_speed = 0;
+	smp->sm_link_duplex = SFXGE_LINK_DUPLEX_UNKNOWN;
+
+	/* This may call MCDI */
+	(void) efx_mac_drain(enp, B_TRUE);
+
+	smp->sm_link_mode = EFX_LINK_UNKNOWN;
+
+	efx_port_fini(enp);
+
+	mutex_exit(&(smp->sm_lock));
+}
+
+void
+sfxge_mac_fini(sfxge_t *sp)
+{
+	sfxge_mac_t *smp = &(sp->s_mac);
+	efsys_mem_t *esmp = &(smp->sm_mem);
+
+	ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED);
+	ASSERT3P(smp->sm_sp, ==, sp);
+
+	/* Tear down the statistics */
+	sfxge_phy_kstat_fini(sp);
+	sfxge_mac_kstat_fini(sp);
+
+	smp->sm_state = SFXGE_MAC_UNINITIALIZED;
+	smp->sm_link_mode = EFX_LINK_UNKNOWN;
+	smp->sm_promisc = SFXGE_PROMISC_OFF;
+
+	bzero(smp->sm_mcast_addr, sizeof (smp->sm_mcast_addr));
+	smp->sm_mcast_count = 0;
+
+	bzero(smp->sm_laa, ETHERADDRL);
+	smp->sm_laa_valid = B_FALSE;
+
+	bzero(smp->sm_bia, ETHERADDRL);
+
+	smp->sm_fcntl = 0;
+
+	/* Finish with PHY DMA memory */
+	sfxge_phy_fini(sp);
+
+	/* Teardown the DMA */
+	sfxge_dma_buffer_destroy(esmp);
+
+	/* Destroy the link poll taskq */
+	ddi_taskq_destroy(smp->sm_tqp);
+	smp->sm_tqp = NULL;
+
+	mutex_destroy(&(smp->sm_lock));
+
+	smp->sm_sp = NULL;
+}
