xref: /titanic_50/usr/src/uts/common/io/mxfe/mxfe.c (revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac)
129e83d4bSgd78059 /*
229e83d4bSgd78059  * Solaris driver for ethernet cards based on the Macronix 98715
329e83d4bSgd78059  *
429e83d4bSgd78059  * Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>.
529e83d4bSgd78059  * All rights reserved.
629e83d4bSgd78059  *
729e83d4bSgd78059  * Redistribution and use in source and binary forms, with or without
829e83d4bSgd78059  * modification, are permitted provided that the following conditions
929e83d4bSgd78059  * are met:
1029e83d4bSgd78059  * 1. Redistributions of source code must retain the above copyright
1129e83d4bSgd78059  *    notice, this list of conditions and the following disclaimer.
1229e83d4bSgd78059  * 2. Redistributions in binary form must reproduce the above copyright
1329e83d4bSgd78059  *    notice, this list of conditions and the following disclaimer in the
1429e83d4bSgd78059  *    documentation and/or other materials provided with the distribution.
1529e83d4bSgd78059  * 3. Neither the name of the author nor the names of any co-contributors
1629e83d4bSgd78059  *    may be used to endorse or promote products derived from this software
1729e83d4bSgd78059  *    without specific prior written permission.
1829e83d4bSgd78059  *
1929e83d4bSgd78059  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
2029e83d4bSgd78059  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2129e83d4bSgd78059  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2229e83d4bSgd78059  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2329e83d4bSgd78059  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2429e83d4bSgd78059  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2529e83d4bSgd78059  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2629e83d4bSgd78059  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2729e83d4bSgd78059  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2829e83d4bSgd78059  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2929e83d4bSgd78059  * POSSIBILITY OF SUCH DAMAGE.
3029e83d4bSgd78059  */
3196fb08b9Sgd78059 /*
32*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3396fb08b9Sgd78059  * Use is subject to license terms.
3496fb08b9Sgd78059  */
3529e83d4bSgd78059 
3629e83d4bSgd78059 
3729e83d4bSgd78059 #include <sys/varargs.h>
3829e83d4bSgd78059 #include <sys/types.h>
3929e83d4bSgd78059 #include <sys/modctl.h>
4029e83d4bSgd78059 #include <sys/conf.h>
4129e83d4bSgd78059 #include <sys/devops.h>
4229e83d4bSgd78059 #include <sys/stream.h>
4329e83d4bSgd78059 #include <sys/strsun.h>
4429e83d4bSgd78059 #include <sys/cmn_err.h>
4529e83d4bSgd78059 #include <sys/dlpi.h>
4629e83d4bSgd78059 #include <sys/ethernet.h>
4729e83d4bSgd78059 #include <sys/kmem.h>
4829e83d4bSgd78059 #include <sys/time.h>
4929e83d4bSgd78059 #include <sys/miiregs.h>
5029e83d4bSgd78059 #include <sys/strsun.h>
5129e83d4bSgd78059 #include <sys/mac.h>
5229e83d4bSgd78059 #include <sys/mac_ether.h>
5329e83d4bSgd78059 #include <sys/ddi.h>
5429e83d4bSgd78059 #include <sys/sunddi.h>
55d62bc4baSyz147064 #include <sys/vlan.h>
5629e83d4bSgd78059 
5729e83d4bSgd78059 #include "mxfe.h"
5829e83d4bSgd78059 #include "mxfeimpl.h"
5929e83d4bSgd78059 
6029e83d4bSgd78059 /*
6129e83d4bSgd78059  * Driver globals.
6229e83d4bSgd78059  */
6329e83d4bSgd78059 
6429e83d4bSgd78059 /* patchable debug flag ... must not be static! */
6529e83d4bSgd78059 #ifdef	DEBUG
6629e83d4bSgd78059 unsigned		mxfe_debug = DWARN;
6729e83d4bSgd78059 #endif
6829e83d4bSgd78059 
6929e83d4bSgd78059 /* table of supported devices */
7029e83d4bSgd78059 static mxfe_card_t mxfe_cards[] = {
7129e83d4bSgd78059 
7229e83d4bSgd78059 	/*
7329e83d4bSgd78059 	 * Lite-On products
7429e83d4bSgd78059 	 */
7529e83d4bSgd78059 	{ 0x11ad, 0xc115, 0, 0, "Lite-On LC82C115", MXFE_PNICII },
7629e83d4bSgd78059 
7729e83d4bSgd78059 	/*
7829e83d4bSgd78059 	 * Macronix chips
7929e83d4bSgd78059 	 */
8029e83d4bSgd78059 	{ 0x10d9, 0x0531, 0x25, 0xff, "Macronix MX98715AEC", MXFE_98715AEC },
8129e83d4bSgd78059 	{ 0x10d9, 0x0531, 0x20, 0xff, "Macronix MX98715A", MXFE_98715A },
8229e83d4bSgd78059 	{ 0x10d9, 0x0531, 0x60, 0xff, "Macronix MX98715B", MXFE_98715B },
8329e83d4bSgd78059 	{ 0x10d9, 0x0531, 0x30, 0xff, "Macronix MX98725", MXFE_98725 },
8429e83d4bSgd78059 	{ 0x10d9, 0x0531, 0x00, 0xff, "Macronix MX98715", MXFE_98715 },
8529e83d4bSgd78059 	{ 0x10d9, 0x0512, 0, 0, "Macronix MX98713", MXFE_98713 },
8629e83d4bSgd78059 
8729e83d4bSgd78059 	/*
8829e83d4bSgd78059 	 * Compex (relabeled Macronix products)
8929e83d4bSgd78059 	 */
9029e83d4bSgd78059 	{ 0x11fc, 0x9881, 0x00, 0x00, "Compex 9881", MXFE_98713 },
9129e83d4bSgd78059 	{ 0x11fc, 0x9881, 0x10, 0xff, "Compex 9881A", MXFE_98713A },
9229e83d4bSgd78059 	/*
9329e83d4bSgd78059 	 * Models listed here
9429e83d4bSgd78059 	 */
9529e83d4bSgd78059 	{ 0x11ad, 0xc001, 0, 0, "Linksys LNE100TX", MXFE_PNICII },
9629e83d4bSgd78059 	{ 0x2646, 0x000b, 0, 0, "Kingston KNE111TX", MXFE_PNICII },
9729e83d4bSgd78059 	{ 0x1154, 0x0308, 0, 0, "Buffalo LGY-PCI-TXL", MXFE_98715AEC },
9829e83d4bSgd78059 };
9929e83d4bSgd78059 
10029e83d4bSgd78059 #define	ETHERVLANMTU	(ETHERMAX + 4)
10129e83d4bSgd78059 
10229e83d4bSgd78059 /*
10329e83d4bSgd78059  * Function prototypes
10429e83d4bSgd78059  */
10529e83d4bSgd78059 static int	mxfe_attach(dev_info_t *, ddi_attach_cmd_t);
10629e83d4bSgd78059 static int	mxfe_detach(dev_info_t *, ddi_detach_cmd_t);
10729e83d4bSgd78059 static int	mxfe_resume(dev_info_t *);
10877860f66SGarrett D'Amore static int	mxfe_quiesce(dev_info_t *);
10929e83d4bSgd78059 static int	mxfe_m_unicst(void *, const uint8_t *);
11029e83d4bSgd78059 static int	mxfe_m_multicst(void *, boolean_t, const uint8_t *);
11129e83d4bSgd78059 static int	mxfe_m_promisc(void *, boolean_t);
11229e83d4bSgd78059 static mblk_t	*mxfe_m_tx(void *, mblk_t *);
11329e83d4bSgd78059 static int	mxfe_m_stat(void *, uint_t, uint64_t *);
11429e83d4bSgd78059 static int	mxfe_m_start(void *);
11529e83d4bSgd78059 static void	mxfe_m_stop(void *);
11696fb08b9Sgd78059 static int	mxfe_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
117*0dc2366fSVenugopal Iyer     void *);
11896fb08b9Sgd78059 static int	mxfe_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
11996fb08b9Sgd78059     const void *);
120*0dc2366fSVenugopal Iyer static void	mxfe_m_propinfo(void *, const char *, mac_prop_id_t,
121*0dc2366fSVenugopal Iyer     mac_prop_info_handle_t);
12229e83d4bSgd78059 static unsigned	mxfe_intr(caddr_t);
12329e83d4bSgd78059 static void	mxfe_startmac(mxfe_t *);
12429e83d4bSgd78059 static void	mxfe_stopmac(mxfe_t *);
12529e83d4bSgd78059 static void	mxfe_resetrings(mxfe_t *);
12629e83d4bSgd78059 static boolean_t	mxfe_initialize(mxfe_t *);
12729e83d4bSgd78059 static void	mxfe_startall(mxfe_t *);
12829e83d4bSgd78059 static void	mxfe_stopall(mxfe_t *);
12929e83d4bSgd78059 static void	mxfe_resetall(mxfe_t *);
13029e83d4bSgd78059 static mxfe_txbuf_t *mxfe_alloctxbuf(mxfe_t *);
13129e83d4bSgd78059 static void	mxfe_destroytxbuf(mxfe_txbuf_t *);
13229e83d4bSgd78059 static mxfe_rxbuf_t *mxfe_allocrxbuf(mxfe_t *);
13329e83d4bSgd78059 static void	mxfe_destroyrxbuf(mxfe_rxbuf_t *);
13429e83d4bSgd78059 static void	mxfe_send_setup(mxfe_t *);
13529e83d4bSgd78059 static boolean_t	mxfe_send(mxfe_t *, mblk_t *);
13629e83d4bSgd78059 static int	mxfe_allocrxring(mxfe_t *);
13729e83d4bSgd78059 static void	mxfe_freerxring(mxfe_t *);
13829e83d4bSgd78059 static int	mxfe_alloctxring(mxfe_t *);
13929e83d4bSgd78059 static void	mxfe_freetxring(mxfe_t *);
14029e83d4bSgd78059 static void	mxfe_error(dev_info_t *, char *, ...);
14129e83d4bSgd78059 static uint8_t	mxfe_sromwidth(mxfe_t *);
14229e83d4bSgd78059 static uint16_t	mxfe_readsromword(mxfe_t *, unsigned);
14329e83d4bSgd78059 static void	mxfe_readsrom(mxfe_t *, unsigned, unsigned, void *);
14429e83d4bSgd78059 static void	mxfe_getfactaddr(mxfe_t *, uchar_t *);
14596fb08b9Sgd78059 static uint8_t	mxfe_miireadbit(mxfe_t *);
14696fb08b9Sgd78059 static void	mxfe_miiwritebit(mxfe_t *, uint8_t);
14729e83d4bSgd78059 static void	mxfe_miitristate(mxfe_t *);
14896fb08b9Sgd78059 static uint16_t	mxfe_miiread(mxfe_t *, int, int);
14929e83d4bSgd78059 static void	mxfe_miiwrite(mxfe_t *, int, int, uint16_t);
15096fb08b9Sgd78059 static uint16_t	mxfe_miireadgeneral(mxfe_t *, int, int);
15129e83d4bSgd78059 static void	mxfe_miiwritegeneral(mxfe_t *, int, int, uint16_t);
15296fb08b9Sgd78059 static uint16_t	mxfe_miiread98713(mxfe_t *, int, int);
15329e83d4bSgd78059 static void	mxfe_miiwrite98713(mxfe_t *, int, int, uint16_t);
15429e83d4bSgd78059 static void	mxfe_startphy(mxfe_t *);
15529e83d4bSgd78059 static void	mxfe_stopphy(mxfe_t *);
15629e83d4bSgd78059 static void	mxfe_startphymii(mxfe_t *);
15729e83d4bSgd78059 static void	mxfe_startphynway(mxfe_t *);
15829e83d4bSgd78059 static void	mxfe_startnway(mxfe_t *);
15929e83d4bSgd78059 static void	mxfe_reportlink(mxfe_t *);
16029e83d4bSgd78059 static void	mxfe_checklink(mxfe_t *);
16129e83d4bSgd78059 static void	mxfe_checklinkmii(mxfe_t *);
16229e83d4bSgd78059 static void	mxfe_checklinknway(mxfe_t *);
16329e83d4bSgd78059 static void	mxfe_disableinterrupts(mxfe_t *);
16429e83d4bSgd78059 static void	mxfe_enableinterrupts(mxfe_t *);
16529e83d4bSgd78059 static void	mxfe_reclaim(mxfe_t *);
16677860f66SGarrett D'Amore static boolean_t	mxfe_receive(mxfe_t *, mblk_t **);
16729e83d4bSgd78059 
16829e83d4bSgd78059 #ifdef	DEBUG
16929e83d4bSgd78059 static void	mxfe_dprintf(mxfe_t *, const char *, int, char *, ...);
17029e83d4bSgd78059 #endif
17129e83d4bSgd78059 
17229e83d4bSgd78059 #define	KIOIP	KSTAT_INTR_PTR(mxfep->mxfe_intrstat)
17329e83d4bSgd78059 
17429e83d4bSgd78059 static mac_callbacks_t mxfe_m_callbacks = {
175*0dc2366fSVenugopal Iyer 	MC_SETPROP | MC_GETPROP | MC_PROPINFO,
17629e83d4bSgd78059 	mxfe_m_stat,
17729e83d4bSgd78059 	mxfe_m_start,
17829e83d4bSgd78059 	mxfe_m_stop,
17929e83d4bSgd78059 	mxfe_m_promisc,
18029e83d4bSgd78059 	mxfe_m_multicst,
18129e83d4bSgd78059 	mxfe_m_unicst,
18229e83d4bSgd78059 	mxfe_m_tx,
183*0dc2366fSVenugopal Iyer 	NULL,
18496fb08b9Sgd78059 	NULL,		/* mc_ioctl */
18596fb08b9Sgd78059 	NULL,		/* mc_getcapab */
18696fb08b9Sgd78059 	NULL,		/* mc_open */
18796fb08b9Sgd78059 	NULL,		/* mc_close */
18896fb08b9Sgd78059 	mxfe_m_setprop,
189*0dc2366fSVenugopal Iyer 	mxfe_m_getprop,
190*0dc2366fSVenugopal Iyer 	mxfe_m_propinfo
19129e83d4bSgd78059 };
19229e83d4bSgd78059 
19329e83d4bSgd78059 /*
19429e83d4bSgd78059  * Stream information
19529e83d4bSgd78059  */
19629e83d4bSgd78059 DDI_DEFINE_STREAM_OPS(mxfe_devops, nulldev, nulldev, mxfe_attach, mxfe_detach,
19777860f66SGarrett D'Amore     nodev, NULL, D_MP, NULL, mxfe_quiesce);
19829e83d4bSgd78059 
19929e83d4bSgd78059 /*
20029e83d4bSgd78059  * Module linkage information.
20129e83d4bSgd78059  */
20229e83d4bSgd78059 
20329e83d4bSgd78059 static struct modldrv mxfe_modldrv = {
20429e83d4bSgd78059 	&mod_driverops,			/* drv_modops */
20529e83d4bSgd78059 	"Macronix Fast Ethernet",	/* drv_linkinfo */
20629e83d4bSgd78059 	&mxfe_devops			/* drv_dev_ops */
20729e83d4bSgd78059 };
20829e83d4bSgd78059 
20929e83d4bSgd78059 static struct modlinkage mxfe_modlinkage = {
21029e83d4bSgd78059 	MODREV_1,		/* ml_rev */
21129e83d4bSgd78059 	{ &mxfe_modldrv, NULL } /* ml_linkage */
21229e83d4bSgd78059 };
21329e83d4bSgd78059 
21429e83d4bSgd78059 /*
21529e83d4bSgd78059  * Device attributes.
21629e83d4bSgd78059  */
21729e83d4bSgd78059 static ddi_device_acc_attr_t mxfe_devattr = {
21829e83d4bSgd78059 	DDI_DEVICE_ATTR_V0,
21929e83d4bSgd78059 	DDI_STRUCTURE_LE_ACC,
22029e83d4bSgd78059 	DDI_STRICTORDER_ACC
22129e83d4bSgd78059 };
22229e83d4bSgd78059 
22329e83d4bSgd78059 static ddi_device_acc_attr_t mxfe_bufattr = {
22429e83d4bSgd78059 	DDI_DEVICE_ATTR_V0,
22529e83d4bSgd78059 	DDI_NEVERSWAP_ACC,
22629e83d4bSgd78059 	DDI_STRICTORDER_ACC
22729e83d4bSgd78059 };
22829e83d4bSgd78059 
22929e83d4bSgd78059 static ddi_dma_attr_t mxfe_dma_attr = {
23029e83d4bSgd78059 	DMA_ATTR_V0,		/* dma_attr_version */
23129e83d4bSgd78059 	0,			/* dma_attr_addr_lo */
23229e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
23329e83d4bSgd78059 	0x7FFFFFFFU,		/* dma_attr_count_max */
23429e83d4bSgd78059 	4,			/* dma_attr_align */
23529e83d4bSgd78059 	0x3F,			/* dma_attr_burstsizes */
23629e83d4bSgd78059 	1,			/* dma_attr_minxfer */
23729e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
23829e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_seg */
23929e83d4bSgd78059 	1,			/* dma_attr_sgllen */
24029e83d4bSgd78059 	1,			/* dma_attr_granular */
24129e83d4bSgd78059 	0			/* dma_attr_flags */
24229e83d4bSgd78059 };
24329e83d4bSgd78059 
24429e83d4bSgd78059 /*
24529e83d4bSgd78059  * Tx buffers can be arbitrarily aligned.  Additionally, they can
24629e83d4bSgd78059  * cross a page boundary, so we use the two buffer addresses of the
24729e83d4bSgd78059  * chip to provide a two-entry scatter-gather list.
24829e83d4bSgd78059  */
24929e83d4bSgd78059 static ddi_dma_attr_t mxfe_dma_txattr = {
25029e83d4bSgd78059 	DMA_ATTR_V0,		/* dma_attr_version */
25129e83d4bSgd78059 	0,			/* dma_attr_addr_lo */
25229e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
25329e83d4bSgd78059 	0x7FFFFFFFU,		/* dma_attr_count_max */
25429e83d4bSgd78059 	1,			/* dma_attr_align */
25529e83d4bSgd78059 	0x3F,			/* dma_attr_burstsizes */
25629e83d4bSgd78059 	1,			/* dma_attr_minxfer */
25729e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
25829e83d4bSgd78059 	0xFFFFFFFFU,		/* dma_attr_seg */
25929e83d4bSgd78059 	2,			/* dma_attr_sgllen */
26029e83d4bSgd78059 	1,			/* dma_attr_granular */
26129e83d4bSgd78059 	0			/* dma_attr_flags */
26229e83d4bSgd78059 };
26329e83d4bSgd78059 
26429e83d4bSgd78059 /*
26529e83d4bSgd78059  * Ethernet addresses.
26629e83d4bSgd78059  */
26729e83d4bSgd78059 static uchar_t mxfe_broadcast[ETHERADDRL] = {
26829e83d4bSgd78059 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
26929e83d4bSgd78059 };
27029e83d4bSgd78059 
27129e83d4bSgd78059 /*
27229e83d4bSgd78059  * DDI entry points.
27329e83d4bSgd78059  */
27429e83d4bSgd78059 int
_init(void)27529e83d4bSgd78059 _init(void)
27629e83d4bSgd78059 {
27729e83d4bSgd78059 	int	rv;
27829e83d4bSgd78059 	mac_init_ops(&mxfe_devops, "mxfe");
27929e83d4bSgd78059 	if ((rv = mod_install(&mxfe_modlinkage)) != DDI_SUCCESS) {
28029e83d4bSgd78059 		mac_fini_ops(&mxfe_devops);
28129e83d4bSgd78059 	}
28229e83d4bSgd78059 	return (rv);
28329e83d4bSgd78059 }
28429e83d4bSgd78059 
28529e83d4bSgd78059 int
_fini(void)28629e83d4bSgd78059 _fini(void)
28729e83d4bSgd78059 {
28829e83d4bSgd78059 	int	rv;
28929e83d4bSgd78059 	if ((rv = mod_remove(&mxfe_modlinkage)) == DDI_SUCCESS) {
29029e83d4bSgd78059 		mac_fini_ops(&mxfe_devops);
29129e83d4bSgd78059 	}
29229e83d4bSgd78059 	return (rv);
29329e83d4bSgd78059 }
29429e83d4bSgd78059 
29529e83d4bSgd78059 int
_info(struct modinfo * modinfop)29629e83d4bSgd78059 _info(struct modinfo *modinfop)
29729e83d4bSgd78059 {
29829e83d4bSgd78059 	return (mod_info(&mxfe_modlinkage, modinfop));
29929e83d4bSgd78059 }
30029e83d4bSgd78059 
30129e83d4bSgd78059 int
mxfe_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30229e83d4bSgd78059 mxfe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30329e83d4bSgd78059 {
30429e83d4bSgd78059 	mxfe_t			*mxfep;
30529e83d4bSgd78059 	mac_register_t		*macp;
30629e83d4bSgd78059 	int			inst = ddi_get_instance(dip);
30729e83d4bSgd78059 	ddi_acc_handle_t	pci;
30829e83d4bSgd78059 	uint16_t		venid;
30929e83d4bSgd78059 	uint16_t		devid;
31029e83d4bSgd78059 	uint16_t		revid;
31129e83d4bSgd78059 	uint16_t		svid;
31229e83d4bSgd78059 	uint16_t		ssid;
31329e83d4bSgd78059 	uint16_t		cachesize;
31429e83d4bSgd78059 	mxfe_card_t		*cardp;
31529e83d4bSgd78059 	int			i;
31629e83d4bSgd78059 
31729e83d4bSgd78059 	switch (cmd) {
31829e83d4bSgd78059 	case DDI_RESUME:
31929e83d4bSgd78059 		return (mxfe_resume(dip));
32029e83d4bSgd78059 
32129e83d4bSgd78059 	case DDI_ATTACH:
32229e83d4bSgd78059 		break;
32329e83d4bSgd78059 
32429e83d4bSgd78059 	default:
32529e83d4bSgd78059 		return (DDI_FAILURE);
32629e83d4bSgd78059 	}
32729e83d4bSgd78059 
32829e83d4bSgd78059 	/* this card is a bus master, reject any slave-only slot */
32929e83d4bSgd78059 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
33029e83d4bSgd78059 		mxfe_error(dip, "slot does not support PCI bus-master");
33129e83d4bSgd78059 		return (DDI_FAILURE);
33229e83d4bSgd78059 	}
33329e83d4bSgd78059 	/* PCI devices shouldn't generate hilevel interrupts */
33429e83d4bSgd78059 	if (ddi_intr_hilevel(dip, 0) != 0) {
33529e83d4bSgd78059 		mxfe_error(dip, "hilevel interrupts not supported");
33629e83d4bSgd78059 		return (DDI_FAILURE);
33729e83d4bSgd78059 	}
33829e83d4bSgd78059 	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
33929e83d4bSgd78059 		mxfe_error(dip, "unable to setup PCI config handle");
34029e83d4bSgd78059 		return (DDI_FAILURE);
34129e83d4bSgd78059 	}
34229e83d4bSgd78059 
34329e83d4bSgd78059 	venid = pci_config_get16(pci, PCI_VID);
34429e83d4bSgd78059 	devid = pci_config_get16(pci, PCI_DID);
34529e83d4bSgd78059 	revid = pci_config_get16(pci, PCI_RID);
34629e83d4bSgd78059 	svid = pci_config_get16(pci, PCI_SVID);
34729e83d4bSgd78059 	ssid = pci_config_get16(pci, PCI_SSID);
34829e83d4bSgd78059 
34929e83d4bSgd78059 	/*
35029e83d4bSgd78059 	 * the last entry in the card table matches every possible
35129e83d4bSgd78059 	 * card, so the for-loop always terminates properly.
35229e83d4bSgd78059 	 */
35329e83d4bSgd78059 	cardp = NULL;
35429e83d4bSgd78059 	for (i = 0; i < (sizeof (mxfe_cards) / sizeof (mxfe_card_t)); i++) {
35529e83d4bSgd78059 		if ((venid == mxfe_cards[i].card_venid) &&
35629e83d4bSgd78059 		    (devid == mxfe_cards[i].card_devid) &&
35729e83d4bSgd78059 		    ((revid & mxfe_cards[i].card_revmask) ==
35829e83d4bSgd78059 		    mxfe_cards[i].card_revid)) {
35929e83d4bSgd78059 			cardp = &mxfe_cards[i];
36029e83d4bSgd78059 		}
36129e83d4bSgd78059 		if ((svid == mxfe_cards[i].card_venid) &&
36229e83d4bSgd78059 		    (ssid == mxfe_cards[i].card_devid) &&
36329e83d4bSgd78059 		    ((revid & mxfe_cards[i].card_revmask) ==
36429e83d4bSgd78059 		    mxfe_cards[i].card_revid)) {
36529e83d4bSgd78059 			cardp = &mxfe_cards[i];
36629e83d4bSgd78059 			break;
36729e83d4bSgd78059 		}
36829e83d4bSgd78059 	}
36929e83d4bSgd78059 
37029e83d4bSgd78059 	if (cardp == NULL) {
37129e83d4bSgd78059 		pci_config_teardown(&pci);
37229e83d4bSgd78059 		mxfe_error(dip, "Unable to identify PCI card");
37329e83d4bSgd78059 		return (DDI_FAILURE);
37429e83d4bSgd78059 	}
37529e83d4bSgd78059 
37629e83d4bSgd78059 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
37729e83d4bSgd78059 	    cardp->card_cardname) != DDI_PROP_SUCCESS) {
37829e83d4bSgd78059 		pci_config_teardown(&pci);
37929e83d4bSgd78059 		mxfe_error(dip, "Unable to create model property");
38029e83d4bSgd78059 		return (DDI_FAILURE);
38129e83d4bSgd78059 	}
38229e83d4bSgd78059 
38329e83d4bSgd78059 	/*
38429e83d4bSgd78059 	 * Grab the PCI cachesize -- we use this to program the
38529e83d4bSgd78059 	 * cache-optimization bus access bits.
38629e83d4bSgd78059 	 */
38729e83d4bSgd78059 	cachesize = pci_config_get8(pci, PCI_CLS);
38829e83d4bSgd78059 
38929e83d4bSgd78059 	/* this cannot fail */
39029e83d4bSgd78059 	mxfep = kmem_zalloc(sizeof (mxfe_t), KM_SLEEP);
39129e83d4bSgd78059 	ddi_set_driver_private(dip, mxfep);
39229e83d4bSgd78059 
39329e83d4bSgd78059 	/* get the interrupt block cookie */
39429e83d4bSgd78059 	if (ddi_get_iblock_cookie(dip, 0, &mxfep->mxfe_icookie)
39529e83d4bSgd78059 	    != DDI_SUCCESS) {
39629e83d4bSgd78059 		mxfe_error(dip, "ddi_get_iblock_cookie failed");
39729e83d4bSgd78059 		pci_config_teardown(&pci);
39829e83d4bSgd78059 		kmem_free(mxfep, sizeof (mxfe_t));
39929e83d4bSgd78059 		return (DDI_FAILURE);
40029e83d4bSgd78059 	}
40129e83d4bSgd78059 
40229e83d4bSgd78059 	mxfep->mxfe_dip = dip;
40329e83d4bSgd78059 	mxfep->mxfe_cardp = cardp;
40429e83d4bSgd78059 	mxfep->mxfe_phyaddr = -1;
40529e83d4bSgd78059 	mxfep->mxfe_cachesize = cachesize;
40629e83d4bSgd78059 
40729e83d4bSgd78059 	/* default properties */
40829e83d4bSgd78059 	mxfep->mxfe_adv_aneg = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
40929e83d4bSgd78059 	    "adv_autoneg_cap", 1);
41029e83d4bSgd78059 	mxfep->mxfe_adv_100T4 = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
41129e83d4bSgd78059 	    "adv_100T4_cap", 1);
41229e83d4bSgd78059 	mxfep->mxfe_adv_100fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
41329e83d4bSgd78059 	    "adv_100fdx_cap", 1);
41429e83d4bSgd78059 	mxfep->mxfe_adv_100hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
41529e83d4bSgd78059 	    "adv_100hdx_cap", 1);
41629e83d4bSgd78059 	mxfep->mxfe_adv_10fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
41729e83d4bSgd78059 	    "adv_10fdx_cap", 1);
41829e83d4bSgd78059 	mxfep->mxfe_adv_10hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
41929e83d4bSgd78059 	    "adv_10hdx_cap", 1);
42029e83d4bSgd78059 
42129e83d4bSgd78059 	DBG(DPCI, "PCI vendor id = %x", venid);
42229e83d4bSgd78059 	DBG(DPCI, "PCI device id = %x", devid);
42329e83d4bSgd78059 	DBG(DPCI, "PCI revision id = %x", revid);
42429e83d4bSgd78059 	DBG(DPCI, "PCI cachesize = %d", cachesize);
42529e83d4bSgd78059 	DBG(DPCI, "PCI COMM = %x", pci_config_get8(pci, PCI_CMD));
42629e83d4bSgd78059 	DBG(DPCI, "PCI STAT = %x", pci_config_get8(pci, PCI_STAT));
42729e83d4bSgd78059 
42829e83d4bSgd78059 	mutex_init(&mxfep->mxfe_xmtlock, NULL, MUTEX_DRIVER,
42929e83d4bSgd78059 	    mxfep->mxfe_icookie);
43029e83d4bSgd78059 	mutex_init(&mxfep->mxfe_intrlock, NULL, MUTEX_DRIVER,
43129e83d4bSgd78059 	    mxfep->mxfe_icookie);
43229e83d4bSgd78059 
43329e83d4bSgd78059 	/*
43429e83d4bSgd78059 	 * Enable bus master, IO space, and memory space accesses.
43529e83d4bSgd78059 	 */
43629e83d4bSgd78059 	pci_config_put16(pci, PCI_CMD,
43729e83d4bSgd78059 	    pci_config_get16(pci, PCI_CMD) |
43829e83d4bSgd78059 	    PCI_CMD_BME | PCI_CMD_MAE | PCI_CMD_MWIE);
43929e83d4bSgd78059 
44029e83d4bSgd78059 	/* we're done with this now, drop it */
44129e83d4bSgd78059 	pci_config_teardown(&pci);
44229e83d4bSgd78059 
44329e83d4bSgd78059 	/*
44429e83d4bSgd78059 	 * Initialize interrupt kstat.  This should not normally fail, since
44529e83d4bSgd78059 	 * we don't use a persistent stat.  We do it this way to avoid having
44629e83d4bSgd78059 	 * to test for it at run time on the hot path.
44729e83d4bSgd78059 	 */
44829e83d4bSgd78059 	mxfep->mxfe_intrstat = kstat_create("mxfe", inst, "intr", "controller",
44929e83d4bSgd78059 	    KSTAT_TYPE_INTR, 1, 0);
45029e83d4bSgd78059 	if (mxfep->mxfe_intrstat == NULL) {
45129e83d4bSgd78059 		mxfe_error(dip, "kstat_create failed");
45229e83d4bSgd78059 		goto failed;
45329e83d4bSgd78059 	}
45429e83d4bSgd78059 	kstat_install(mxfep->mxfe_intrstat);
45529e83d4bSgd78059 
45629e83d4bSgd78059 	/*
45729e83d4bSgd78059 	 * Map in the device registers.
45829e83d4bSgd78059 	 */
45929e83d4bSgd78059 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mxfep->mxfe_regs,
46029e83d4bSgd78059 	    0, 0, &mxfe_devattr, &mxfep->mxfe_regshandle)) {
46129e83d4bSgd78059 		mxfe_error(dip, "ddi_regs_map_setup failed");
46229e83d4bSgd78059 		goto failed;
46329e83d4bSgd78059 	}
46429e83d4bSgd78059 
46529e83d4bSgd78059 	/*
46629e83d4bSgd78059 	 * Allocate DMA resources (descriptor rings and buffers).
46729e83d4bSgd78059 	 */
46829e83d4bSgd78059 	if ((mxfe_allocrxring(mxfep) != DDI_SUCCESS) ||
46929e83d4bSgd78059 	    (mxfe_alloctxring(mxfep) != DDI_SUCCESS)) {
47029e83d4bSgd78059 		mxfe_error(dip, "unable to allocate DMA resources");
47129e83d4bSgd78059 		goto failed;
47229e83d4bSgd78059 	}
47329e83d4bSgd78059 
47429e83d4bSgd78059 	/* Initialize the chip. */
47529e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
47629e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
47729e83d4bSgd78059 	if (!mxfe_initialize(mxfep)) {
47829e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
47929e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
48029e83d4bSgd78059 		goto failed;
48129e83d4bSgd78059 	}
48229e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
48329e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
48429e83d4bSgd78059 
48529e83d4bSgd78059 	/* Determine the number of address bits to our EEPROM. */
48629e83d4bSgd78059 	mxfep->mxfe_sromwidth = mxfe_sromwidth(mxfep);
48729e83d4bSgd78059 
48829e83d4bSgd78059 	/*
48929e83d4bSgd78059 	 * Get the factory ethernet address.  This becomes the current
49029e83d4bSgd78059 	 * ethernet address (it can be overridden later via ifconfig).
49129e83d4bSgd78059 	 */
49229e83d4bSgd78059 	mxfe_getfactaddr(mxfep, mxfep->mxfe_curraddr);
49329e83d4bSgd78059 	mxfep->mxfe_promisc = B_FALSE;
49429e83d4bSgd78059 
49529e83d4bSgd78059 	/*
49629e83d4bSgd78059 	 * Establish interrupt handler.
49729e83d4bSgd78059 	 */
49829e83d4bSgd78059 	if (ddi_add_intr(dip, 0, NULL, NULL, mxfe_intr, (caddr_t)mxfep) !=
49929e83d4bSgd78059 	    DDI_SUCCESS) {
50029e83d4bSgd78059 		mxfe_error(dip, "unable to add interrupt");
50129e83d4bSgd78059 		goto failed;
50229e83d4bSgd78059 	}
50329e83d4bSgd78059 
50429e83d4bSgd78059 	/* TODO: do the power management stuff */
50529e83d4bSgd78059 
50629e83d4bSgd78059 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
50729e83d4bSgd78059 		mxfe_error(dip, "mac_alloc failed");
50829e83d4bSgd78059 		goto failed;
50929e83d4bSgd78059 	}
51029e83d4bSgd78059 
51129e83d4bSgd78059 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
51229e83d4bSgd78059 	macp->m_driver = mxfep;
51329e83d4bSgd78059 	macp->m_dip = dip;
51429e83d4bSgd78059 	macp->m_src_addr = mxfep->mxfe_curraddr;
51529e83d4bSgd78059 	macp->m_callbacks = &mxfe_m_callbacks;
51629e83d4bSgd78059 	macp->m_min_sdu = 0;
51729e83d4bSgd78059 	macp->m_max_sdu = ETHERMTU;
518d62bc4baSyz147064 	macp->m_margin = VLAN_TAGSZ;
51929e83d4bSgd78059 
52029e83d4bSgd78059 	if (mac_register(macp, &mxfep->mxfe_mh) == DDI_SUCCESS) {
52129e83d4bSgd78059 		mac_free(macp);
52229e83d4bSgd78059 		return (DDI_SUCCESS);
52329e83d4bSgd78059 	}
52429e83d4bSgd78059 
52529e83d4bSgd78059 	/* failed to register with MAC */
52629e83d4bSgd78059 	mac_free(macp);
52729e83d4bSgd78059 failed:
52829e83d4bSgd78059 	if (mxfep->mxfe_icookie != NULL) {
52929e83d4bSgd78059 		ddi_remove_intr(dip, 0, mxfep->mxfe_icookie);
53029e83d4bSgd78059 	}
53129e83d4bSgd78059 	if (mxfep->mxfe_intrstat) {
53229e83d4bSgd78059 		kstat_delete(mxfep->mxfe_intrstat);
53329e83d4bSgd78059 	}
53429e83d4bSgd78059 	mutex_destroy(&mxfep->mxfe_intrlock);
53529e83d4bSgd78059 	mutex_destroy(&mxfep->mxfe_xmtlock);
53629e83d4bSgd78059 
53729e83d4bSgd78059 	mxfe_freerxring(mxfep);
53829e83d4bSgd78059 	mxfe_freetxring(mxfep);
53929e83d4bSgd78059 
54029e83d4bSgd78059 	if (mxfep->mxfe_regshandle != NULL) {
54129e83d4bSgd78059 		ddi_regs_map_free(&mxfep->mxfe_regshandle);
54229e83d4bSgd78059 	}
54329e83d4bSgd78059 	kmem_free(mxfep, sizeof (mxfe_t));
54429e83d4bSgd78059 	return (DDI_FAILURE);
54529e83d4bSgd78059 }
54629e83d4bSgd78059 
54729e83d4bSgd78059 int
mxfe_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)54829e83d4bSgd78059 mxfe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
54929e83d4bSgd78059 {
55029e83d4bSgd78059 	mxfe_t		*mxfep;
55129e83d4bSgd78059 
55229e83d4bSgd78059 	mxfep = ddi_get_driver_private(dip);
55329e83d4bSgd78059 	if (mxfep == NULL) {
55429e83d4bSgd78059 		mxfe_error(dip, "no soft state in detach!");
55529e83d4bSgd78059 		return (DDI_FAILURE);
55629e83d4bSgd78059 	}
55729e83d4bSgd78059 
55829e83d4bSgd78059 	switch (cmd) {
55929e83d4bSgd78059 	case DDI_DETACH:
56029e83d4bSgd78059 
56129e83d4bSgd78059 		if (mac_unregister(mxfep->mxfe_mh) != 0) {
56229e83d4bSgd78059 			return (DDI_FAILURE);
56329e83d4bSgd78059 		}
56429e83d4bSgd78059 
56529e83d4bSgd78059 		/* make sure hardware is quiesced */
56629e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_intrlock);
56729e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_xmtlock);
56829e83d4bSgd78059 		mxfep->mxfe_flags &= ~MXFE_RUNNING;
56929e83d4bSgd78059 		mxfe_stopall(mxfep);
57029e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
57129e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
57229e83d4bSgd78059 
57329e83d4bSgd78059 		/* clean up and shut down device */
57429e83d4bSgd78059 		ddi_remove_intr(dip, 0, mxfep->mxfe_icookie);
57529e83d4bSgd78059 
57629e83d4bSgd78059 		/* clean up kstats */
57729e83d4bSgd78059 		kstat_delete(mxfep->mxfe_intrstat);
57829e83d4bSgd78059 
57929e83d4bSgd78059 		ddi_prop_remove_all(dip);
58029e83d4bSgd78059 
58129e83d4bSgd78059 		/* free up any left over buffers or DMA resources */
58229e83d4bSgd78059 		mxfe_freerxring(mxfep);
58329e83d4bSgd78059 		mxfe_freetxring(mxfep);
58429e83d4bSgd78059 
58529e83d4bSgd78059 		ddi_regs_map_free(&mxfep->mxfe_regshandle);
58629e83d4bSgd78059 		mutex_destroy(&mxfep->mxfe_intrlock);
58729e83d4bSgd78059 		mutex_destroy(&mxfep->mxfe_xmtlock);
58829e83d4bSgd78059 
58929e83d4bSgd78059 		kmem_free(mxfep, sizeof (mxfe_t));
59029e83d4bSgd78059 		return (DDI_SUCCESS);
59129e83d4bSgd78059 
59229e83d4bSgd78059 	case DDI_SUSPEND:
59329e83d4bSgd78059 		/* quiesce the hardware */
59429e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_intrlock);
59529e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_xmtlock);
59629e83d4bSgd78059 		mxfep->mxfe_flags |= MXFE_SUSPENDED;
59729e83d4bSgd78059 		mxfe_stopall(mxfep);
59829e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
59929e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
60029e83d4bSgd78059 		return (DDI_SUCCESS);
60129e83d4bSgd78059 	default:
60229e83d4bSgd78059 		return (DDI_FAILURE);
60329e83d4bSgd78059 	}
60429e83d4bSgd78059 }
60529e83d4bSgd78059 
60629e83d4bSgd78059 int
mxfe_resume(dev_info_t * dip)60729e83d4bSgd78059 mxfe_resume(dev_info_t *dip)
60829e83d4bSgd78059 {
60929e83d4bSgd78059 	mxfe_t		*mxfep;
61029e83d4bSgd78059 
61129e83d4bSgd78059 	if ((mxfep = ddi_get_driver_private(dip)) == NULL) {
61229e83d4bSgd78059 		return (DDI_FAILURE);
61329e83d4bSgd78059 	}
61429e83d4bSgd78059 
61529e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
61629e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
61729e83d4bSgd78059 
61829e83d4bSgd78059 	mxfep->mxfe_flags &= ~MXFE_SUSPENDED;
61929e83d4bSgd78059 
62029e83d4bSgd78059 	/* re-initialize chip */
62129e83d4bSgd78059 	if (!mxfe_initialize(mxfep)) {
62229e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "unable to resume chip!");
62329e83d4bSgd78059 		mxfep->mxfe_flags |= MXFE_SUSPENDED;
62429e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
62529e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
62629e83d4bSgd78059 		return (DDI_SUCCESS);
62729e83d4bSgd78059 	}
62829e83d4bSgd78059 
62929e83d4bSgd78059 	/* start the chip */
63029e83d4bSgd78059 	if (mxfep->mxfe_flags & MXFE_RUNNING) {
63129e83d4bSgd78059 		mxfe_startall(mxfep);
63229e83d4bSgd78059 	}
63329e83d4bSgd78059 
63429e83d4bSgd78059 	/* drop locks */
63529e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
63629e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
63729e83d4bSgd78059 
63829e83d4bSgd78059 	return (DDI_SUCCESS);
63929e83d4bSgd78059 }
64029e83d4bSgd78059 
64177860f66SGarrett D'Amore int
mxfe_quiesce(dev_info_t * dip)64277860f66SGarrett D'Amore mxfe_quiesce(dev_info_t *dip)
64377860f66SGarrett D'Amore {
64477860f66SGarrett D'Amore 	mxfe_t	*mxfep;
64577860f66SGarrett D'Amore 
64677860f66SGarrett D'Amore 	if ((mxfep = ddi_get_driver_private(dip)) == NULL) {
64777860f66SGarrett D'Amore 		return (DDI_FAILURE);
64877860f66SGarrett D'Amore 	}
64977860f66SGarrett D'Amore 
65077860f66SGarrett D'Amore 	/* just do a hard reset of everything */
65177860f66SGarrett D'Amore 	SETBIT(mxfep, CSR_PAR, PAR_RESET);
65277860f66SGarrett D'Amore 
65377860f66SGarrett D'Amore 	return (DDI_SUCCESS);
65477860f66SGarrett D'Amore }
65577860f66SGarrett D'Amore 
65629e83d4bSgd78059 /*ARGSUSED*/
65729e83d4bSgd78059 int
mxfe_m_multicst(void * arg,boolean_t add,const uint8_t * macaddr)65829e83d4bSgd78059 mxfe_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
65929e83d4bSgd78059 {
66029e83d4bSgd78059 	/* we already receive all multicast frames */
66129e83d4bSgd78059 	return (0);
66229e83d4bSgd78059 }
66329e83d4bSgd78059 
66429e83d4bSgd78059 int
mxfe_m_promisc(void * arg,boolean_t on)66529e83d4bSgd78059 mxfe_m_promisc(void *arg, boolean_t on)
66629e83d4bSgd78059 {
66729e83d4bSgd78059 	mxfe_t		*mxfep = arg;
66829e83d4bSgd78059 
66929e83d4bSgd78059 	/* exclusive access to the card while we reprogram it */
67029e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
67129e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
67229e83d4bSgd78059 	/* save current promiscuous mode state for replay in resume */
67329e83d4bSgd78059 	mxfep->mxfe_promisc = on;
67429e83d4bSgd78059 
67529e83d4bSgd78059 	if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) ==
67629e83d4bSgd78059 	    MXFE_RUNNING) {
67729e83d4bSgd78059 		if (on)
67829e83d4bSgd78059 			SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC);
67929e83d4bSgd78059 		else
68029e83d4bSgd78059 			CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC);
68129e83d4bSgd78059 	}
68229e83d4bSgd78059 
68329e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
68429e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
68529e83d4bSgd78059 
68629e83d4bSgd78059 	return (0);
68729e83d4bSgd78059 }
68829e83d4bSgd78059 
68929e83d4bSgd78059 int
mxfe_m_unicst(void * arg,const uint8_t * macaddr)69029e83d4bSgd78059 mxfe_m_unicst(void *arg, const uint8_t *macaddr)
69129e83d4bSgd78059 {
69229e83d4bSgd78059 	mxfe_t		*mxfep = arg;
69329e83d4bSgd78059 
69429e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
69529e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
69629e83d4bSgd78059 	bcopy(macaddr, mxfep->mxfe_curraddr, ETHERADDRL);
69729e83d4bSgd78059 
69829e83d4bSgd78059 	mxfe_resetall(mxfep);
69929e83d4bSgd78059 
70029e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
70129e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
70229e83d4bSgd78059 
70329e83d4bSgd78059 	return (0);
70429e83d4bSgd78059 }
70529e83d4bSgd78059 
70629e83d4bSgd78059 mblk_t *
mxfe_m_tx(void * arg,mblk_t * mp)70729e83d4bSgd78059 mxfe_m_tx(void *arg, mblk_t *mp)
70829e83d4bSgd78059 {
70929e83d4bSgd78059 	mxfe_t	*mxfep = arg;
71029e83d4bSgd78059 	mblk_t	*nmp;
71129e83d4bSgd78059 
71229e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
71329e83d4bSgd78059 
71429e83d4bSgd78059 	if (mxfep->mxfe_flags & MXFE_SUSPENDED) {
71529e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
71629e83d4bSgd78059 		return (mp);
71729e83d4bSgd78059 	}
71829e83d4bSgd78059 
71929e83d4bSgd78059 	while (mp != NULL) {
72029e83d4bSgd78059 		nmp = mp->b_next;
72129e83d4bSgd78059 		mp->b_next = NULL;
72229e83d4bSgd78059 
72329e83d4bSgd78059 		if (!mxfe_send(mxfep, mp)) {
72429e83d4bSgd78059 			mp->b_next = nmp;
72529e83d4bSgd78059 			break;
72629e83d4bSgd78059 		}
72729e83d4bSgd78059 		mp = nmp;
72829e83d4bSgd78059 	}
72929e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
73029e83d4bSgd78059 
73129e83d4bSgd78059 	return (mp);
73229e83d4bSgd78059 }
73329e83d4bSgd78059 
73429e83d4bSgd78059 /*
73529e83d4bSgd78059  * Hardware management.
73629e83d4bSgd78059  */
73729e83d4bSgd78059 boolean_t
mxfe_initialize(mxfe_t * mxfep)73829e83d4bSgd78059 mxfe_initialize(mxfe_t *mxfep)
73929e83d4bSgd78059 {
74029e83d4bSgd78059 	int		i;
74129e83d4bSgd78059 	unsigned	val;
74229e83d4bSgd78059 	uint32_t	par, nar;
74329e83d4bSgd78059 
74429e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_intrlock));
74529e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
74629e83d4bSgd78059 
74729e83d4bSgd78059 	DBG(DCHATTY, "resetting!");
74829e83d4bSgd78059 	SETBIT(mxfep, CSR_PAR, PAR_RESET);
74929e83d4bSgd78059 	for (i = 1; i < 10; i++) {
75029e83d4bSgd78059 		drv_usecwait(5);
75129e83d4bSgd78059 		val = GETCSR(mxfep, CSR_PAR);
75229e83d4bSgd78059 		if (!(val & PAR_RESET)) {
75329e83d4bSgd78059 			break;
75429e83d4bSgd78059 		}
75529e83d4bSgd78059 	}
75629e83d4bSgd78059 	if (i == 10) {
75729e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "timed out waiting for reset!");
75829e83d4bSgd78059 		return (B_FALSE);
75929e83d4bSgd78059 	}
76029e83d4bSgd78059 
76129e83d4bSgd78059 	/* initialize busctl register */
76229e83d4bSgd78059 	par = PAR_BAR | PAR_MRME | PAR_MRLE | PAR_MWIE;
76329e83d4bSgd78059 
76429e83d4bSgd78059 	/* set the cache alignment if its supported */
76529e83d4bSgd78059 	switch (mxfep->mxfe_cachesize) {
76629e83d4bSgd78059 	case 8:
76729e83d4bSgd78059 		par |= PAR_CALIGN_8;
76829e83d4bSgd78059 		break;
76929e83d4bSgd78059 	case 16:
77029e83d4bSgd78059 		par |= PAR_CALIGN_16;
77129e83d4bSgd78059 		break;
77229e83d4bSgd78059 	case 32:
77329e83d4bSgd78059 		par |= PAR_CALIGN_32;
77429e83d4bSgd78059 		break;
77529e83d4bSgd78059 	default:
77629e83d4bSgd78059 		par &= ~(PAR_MWIE | PAR_MRME | PAR_MRLE);
77729e83d4bSgd78059 	}
77829e83d4bSgd78059 
77929e83d4bSgd78059 	/* leave the burst length at zero, indicating infinite burst */
78029e83d4bSgd78059 	PUTCSR(mxfep, CSR_PAR, par);
78129e83d4bSgd78059 
78229e83d4bSgd78059 	mxfe_resetrings(mxfep);
78329e83d4bSgd78059 
78429e83d4bSgd78059 	/* clear the lost packet counter (cleared on read) */
78529e83d4bSgd78059 	(void) GETCSR(mxfep, CSR_LPC);
78629e83d4bSgd78059 
78729e83d4bSgd78059 	/* a few other NAR bits */
78829e83d4bSgd78059 	nar = GETCSR(mxfep, CSR_NAR);
78929e83d4bSgd78059 	nar &= ~NAR_RX_HO;	/* disable hash only filtering */
79029e83d4bSgd78059 	nar |= NAR_RX_HP;	/* hash perfect forwarding */
79129e83d4bSgd78059 	nar |= NAR_RX_MULTI;	/* receive all multicast */
79229e83d4bSgd78059 	nar |= NAR_SF;	/* store-and-forward */
79329e83d4bSgd78059 
79429e83d4bSgd78059 	if (mxfep->mxfe_promisc) {
79529e83d4bSgd78059 		nar |= NAR_RX_PROMISC;
79629e83d4bSgd78059 	} else {
79729e83d4bSgd78059 		nar &= ~NAR_RX_PROMISC;
79829e83d4bSgd78059 	}
79929e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar);
80029e83d4bSgd78059 
80129e83d4bSgd78059 	mxfe_send_setup(mxfep);
80229e83d4bSgd78059 
80329e83d4bSgd78059 	return (B_TRUE);
80429e83d4bSgd78059 }
80529e83d4bSgd78059 
80629e83d4bSgd78059 /*
80729e83d4bSgd78059  * Serial EEPROM access - inspired by the FreeBSD implementation.
80829e83d4bSgd78059  */
80929e83d4bSgd78059 
81029e83d4bSgd78059 uint8_t
mxfe_sromwidth(mxfe_t * mxfep)81129e83d4bSgd78059 mxfe_sromwidth(mxfe_t *mxfep)
81229e83d4bSgd78059 {
81329e83d4bSgd78059 	int		i;
81429e83d4bSgd78059 	int		eeread;
81529e83d4bSgd78059 	uint8_t		addrlen = 8;
81629e83d4bSgd78059 
81729e83d4bSgd78059 	eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP;
81829e83d4bSgd78059 
81929e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP);
82029e83d4bSgd78059 	drv_usecwait(1);
82129e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread);
82229e83d4bSgd78059 
82329e83d4bSgd78059 	/* command bits first */
82429e83d4bSgd78059 	for (i = 4; i != 0; i >>= 1) {
82529e83d4bSgd78059 		unsigned val = (SROM_READCMD & i) ? SPR_SROM_DIN : 0;
82629e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | val);
82729e83d4bSgd78059 		drv_usecwait(1);
82829e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK);
82929e83d4bSgd78059 		drv_usecwait(1);
83029e83d4bSgd78059 	}
83129e83d4bSgd78059 
83229e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread);
83329e83d4bSgd78059 
83429e83d4bSgd78059 	for (addrlen = 1; addrlen <= 12; addrlen++) {
83529e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK);
83629e83d4bSgd78059 		drv_usecwait(1);
83729e83d4bSgd78059 		if (!(GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT)) {
83829e83d4bSgd78059 			PUTCSR(mxfep, CSR_SPR, eeread);
83929e83d4bSgd78059 			drv_usecwait(1);
84029e83d4bSgd78059 			break;
84129e83d4bSgd78059 		}
84229e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread);
84329e83d4bSgd78059 		drv_usecwait(1);
84429e83d4bSgd78059 	}
84529e83d4bSgd78059 
84629e83d4bSgd78059 	/* turn off accesses to the EEPROM */
84729e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP);
84829e83d4bSgd78059 
84929e83d4bSgd78059 	DBG(DSROM, "detected srom width = %d bits", addrlen);
85029e83d4bSgd78059 
85129e83d4bSgd78059 	return ((addrlen < 4 || addrlen > 12) ? 6 : addrlen);
85229e83d4bSgd78059 }
85329e83d4bSgd78059 
85429e83d4bSgd78059 /*
85529e83d4bSgd78059  * The words in EEPROM are stored in little endian order.  We
85629e83d4bSgd78059  * shift bits out in big endian order, though.  This requires
85729e83d4bSgd78059  * a byte swap on some platforms.
85829e83d4bSgd78059  */
85929e83d4bSgd78059 uint16_t
mxfe_readsromword(mxfe_t * mxfep,unsigned romaddr)86029e83d4bSgd78059 mxfe_readsromword(mxfe_t *mxfep, unsigned romaddr)
86129e83d4bSgd78059 {
86229e83d4bSgd78059 	int		i;
86329e83d4bSgd78059 	uint16_t	word = 0;
86429e83d4bSgd78059 	uint16_t	retval;
86529e83d4bSgd78059 	int		eeread;
86629e83d4bSgd78059 	uint8_t		addrlen;
86729e83d4bSgd78059 	int		readcmd;
86829e83d4bSgd78059 	uchar_t		*ptr;
86929e83d4bSgd78059 
87029e83d4bSgd78059 	eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP;
87129e83d4bSgd78059 	addrlen = mxfep->mxfe_sromwidth;
87229e83d4bSgd78059 	readcmd = (SROM_READCMD << addrlen) | romaddr;
87329e83d4bSgd78059 
87429e83d4bSgd78059 	if (romaddr >= (1 << addrlen)) {
87529e83d4bSgd78059 		/* too big to fit! */
87629e83d4bSgd78059 		return (0);
87729e83d4bSgd78059 	}
87829e83d4bSgd78059 
87929e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP);
88029e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread);
88129e83d4bSgd78059 
88229e83d4bSgd78059 	/* command and address bits */
88329e83d4bSgd78059 	for (i = 4 + addrlen; i >= 0; i--) {
88429e83d4bSgd78059 		short val = (readcmd & (1 << i)) ?  SPR_SROM_DIN : 0;
88529e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | val);
88629e83d4bSgd78059 		drv_usecwait(1);
88729e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK);
88829e83d4bSgd78059 		drv_usecwait(1);
88929e83d4bSgd78059 	}
89029e83d4bSgd78059 
89129e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread);
89229e83d4bSgd78059 
89329e83d4bSgd78059 	for (i = 0; i < 16; i++) {
89429e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK);
89529e83d4bSgd78059 		drv_usecwait(1);
89629e83d4bSgd78059 		word <<= 1;
89729e83d4bSgd78059 		if (GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT) {
89829e83d4bSgd78059 			word |= 1;
89929e83d4bSgd78059 		}
90029e83d4bSgd78059 		PUTCSR(mxfep, CSR_SPR, eeread);
90129e83d4bSgd78059 		drv_usecwait(1);
90229e83d4bSgd78059 	}
90329e83d4bSgd78059 
90429e83d4bSgd78059 	/* turn off accesses to the EEPROM */
90529e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP);
90629e83d4bSgd78059 
90729e83d4bSgd78059 	/*
90829e83d4bSgd78059 	 * Fix up the endianness thing.  Note that the values
90929e83d4bSgd78059 	 * are stored in little endian format on the SROM.
91029e83d4bSgd78059 	 */
91129e83d4bSgd78059 	DBG(DSROM, "got value %d from SROM (before swap)", word);
91229e83d4bSgd78059 	ptr = (uchar_t *)&word;
91329e83d4bSgd78059 	retval = (ptr[1] << 8) | ptr[0];
91429e83d4bSgd78059 	return (retval);
91529e83d4bSgd78059 }
91629e83d4bSgd78059 
91729e83d4bSgd78059 void
mxfe_readsrom(mxfe_t * mxfep,unsigned romaddr,unsigned len,void * dest)91829e83d4bSgd78059 mxfe_readsrom(mxfe_t *mxfep, unsigned romaddr, unsigned len, void *dest)
91929e83d4bSgd78059 {
92029e83d4bSgd78059 	char		*ptr = dest;
92129e83d4bSgd78059 	int		i;
92229e83d4bSgd78059 	uint16_t	word;
92329e83d4bSgd78059 
92429e83d4bSgd78059 	for (i = 0; i < len; i++) {
92529e83d4bSgd78059 		word = mxfe_readsromword(mxfep, romaddr + i);
92629e83d4bSgd78059 		bcopy(&word, ptr, 2);
92729e83d4bSgd78059 		ptr += 2;
92829e83d4bSgd78059 		DBG(DSROM, "word at %d is 0x%x", romaddr + i, word);
92929e83d4bSgd78059 	}
93029e83d4bSgd78059 }
93129e83d4bSgd78059 
93229e83d4bSgd78059 void
mxfe_getfactaddr(mxfe_t * mxfep,uchar_t * eaddr)93329e83d4bSgd78059 mxfe_getfactaddr(mxfe_t *mxfep, uchar_t *eaddr)
93429e83d4bSgd78059 {
93529e83d4bSgd78059 	uint16_t	word;
93629e83d4bSgd78059 	uchar_t		*ptr;
93729e83d4bSgd78059 
93829e83d4bSgd78059 	/* first read to get the location of mac address in srom */
93929e83d4bSgd78059 	word = mxfe_readsromword(mxfep, SROM_ENADDR / 2);
94029e83d4bSgd78059 	ptr = (uchar_t *)&word;
94129e83d4bSgd78059 	word = (ptr[1] << 8) | ptr[0];
94229e83d4bSgd78059 
94329e83d4bSgd78059 	/* then read the actual mac address */
94429e83d4bSgd78059 	mxfe_readsrom(mxfep, word / 2, ETHERADDRL / 2, eaddr);
94529e83d4bSgd78059 	DBG(DMACID,
94629e83d4bSgd78059 	    "factory ethernet address = %02x:%02x:%02x:%02x:%02x:%02x",
94729e83d4bSgd78059 	    eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]);
94829e83d4bSgd78059 }
94929e83d4bSgd78059 
95029e83d4bSgd78059 void
mxfe_startphy(mxfe_t * mxfep)95129e83d4bSgd78059 mxfe_startphy(mxfe_t *mxfep)
95229e83d4bSgd78059 {
95329e83d4bSgd78059 	switch (MXFE_MODEL(mxfep)) {
95429e83d4bSgd78059 	case MXFE_98713A:
95529e83d4bSgd78059 		mxfe_startphymii(mxfep);
95629e83d4bSgd78059 		break;
95729e83d4bSgd78059 	default:
95829e83d4bSgd78059 		mxfe_startphynway(mxfep);
95929e83d4bSgd78059 		break;
96029e83d4bSgd78059 	}
96129e83d4bSgd78059 }
96229e83d4bSgd78059 
96329e83d4bSgd78059 void
mxfe_stopphy(mxfe_t * mxfep)96429e83d4bSgd78059 mxfe_stopphy(mxfe_t *mxfep)
96529e83d4bSgd78059 {
96629e83d4bSgd78059 	uint32_t	nar;
96729e83d4bSgd78059 	int		i;
96829e83d4bSgd78059 
96929e83d4bSgd78059 	/* stop the phy timer */
97029e83d4bSgd78059 	PUTCSR(mxfep, CSR_TIMER, 0);
97129e83d4bSgd78059 
97229e83d4bSgd78059 	switch (MXFE_MODEL(mxfep)) {
97329e83d4bSgd78059 	case MXFE_98713A:
97429e83d4bSgd78059 		for (i = 0; i < 32; i++) {
97529e83d4bSgd78059 			mxfe_miiwrite(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL,
97629e83d4bSgd78059 			    MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE);
97729e83d4bSgd78059 		}
97829e83d4bSgd78059 		break;
97929e83d4bSgd78059 	default:
98029e83d4bSgd78059 		DBG(DPHY, "resetting SIA");
98129e83d4bSgd78059 		PUTCSR(mxfep, CSR_SIA, SIA_RESET);
98229e83d4bSgd78059 		drv_usecwait(500);
98329e83d4bSgd78059 		CLRBIT(mxfep, CSR_TCTL, TCTL_PWR | TCTL_ANE);
98429e83d4bSgd78059 		nar = GETCSR(mxfep, CSR_NAR);
98529e83d4bSgd78059 		nar &= ~(NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX);
98629e83d4bSgd78059 		nar |= NAR_SPEED;
98729e83d4bSgd78059 		PUTCSR(mxfep, CSR_NAR, nar);
98829e83d4bSgd78059 		break;
98929e83d4bSgd78059 	}
99029e83d4bSgd78059 
99129e83d4bSgd78059 	/*
99229e83d4bSgd78059 	 * mark the link state unknown
99329e83d4bSgd78059 	 */
99429e83d4bSgd78059 	if (!mxfep->mxfe_resetting) {
99529e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_UNKNOWN;
99629e83d4bSgd78059 		mxfep->mxfe_ifspeed = 0;
99729e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN;
99829e83d4bSgd78059 		if (mxfep->mxfe_flags & MXFE_RUNNING)
99929e83d4bSgd78059 			mxfe_reportlink(mxfep);
100029e83d4bSgd78059 	}
100129e83d4bSgd78059 }
100229e83d4bSgd78059 
100329e83d4bSgd78059 /*
100429e83d4bSgd78059  * NWay support.
100529e83d4bSgd78059  */
100629e83d4bSgd78059 void
mxfe_startnway(mxfe_t * mxfep)100729e83d4bSgd78059 mxfe_startnway(mxfe_t *mxfep)
100829e83d4bSgd78059 {
100929e83d4bSgd78059 	unsigned	nar;
101029e83d4bSgd78059 	unsigned	tctl;
101129e83d4bSgd78059 	unsigned	restart;
101229e83d4bSgd78059 
101329e83d4bSgd78059 	/* this should not happen in a healthy system */
101496fb08b9Sgd78059 	if (mxfep->mxfe_nwaystate != MXFE_NOLINK) {
101529e83d4bSgd78059 		DBG(DWARN, "link start called out of state (%x)",
101696fb08b9Sgd78059 		    mxfep->mxfe_nwaystate);
101729e83d4bSgd78059 		return;
101829e83d4bSgd78059 	}
101929e83d4bSgd78059 
102029e83d4bSgd78059 	if (mxfep->mxfe_adv_aneg == 0) {
102129e83d4bSgd78059 		/* not done for forced mode */
102229e83d4bSgd78059 		return;
102329e83d4bSgd78059 	}
102429e83d4bSgd78059 
102529e83d4bSgd78059 	nar = GETCSR(mxfep, CSR_NAR);
102629e83d4bSgd78059 	restart = nar & (NAR_TX_ENABLE | NAR_RX_ENABLE);
102729e83d4bSgd78059 	nar &= ~restart;
102829e83d4bSgd78059 
102929e83d4bSgd78059 	if (restart != 0)
103029e83d4bSgd78059 		mxfe_stopmac(mxfep);
103129e83d4bSgd78059 
103229e83d4bSgd78059 	nar |= NAR_SCR | NAR_PCS | NAR_HBD;
103329e83d4bSgd78059 	nar &= ~(NAR_FDX);
103429e83d4bSgd78059 
103529e83d4bSgd78059 	tctl = GETCSR(mxfep, CSR_TCTL);
103629e83d4bSgd78059 	tctl &= ~(TCTL_100FDX | TCTL_100HDX | TCTL_HDX);
103729e83d4bSgd78059 
103829e83d4bSgd78059 	if (mxfep->mxfe_adv_100fdx) {
103929e83d4bSgd78059 		tctl |= TCTL_100FDX;
104029e83d4bSgd78059 	}
104129e83d4bSgd78059 	if (mxfep->mxfe_adv_100hdx) {
104229e83d4bSgd78059 		tctl |= TCTL_100HDX;
104329e83d4bSgd78059 	}
104429e83d4bSgd78059 	if (mxfep->mxfe_adv_10fdx) {
104529e83d4bSgd78059 		nar |= NAR_FDX;
104629e83d4bSgd78059 	}
104729e83d4bSgd78059 	if (mxfep->mxfe_adv_10hdx) {
104829e83d4bSgd78059 		tctl |= TCTL_HDX;
104929e83d4bSgd78059 	}
105029e83d4bSgd78059 	tctl |= TCTL_PWR | TCTL_ANE | TCTL_LTE | TCTL_RSQ;
105129e83d4bSgd78059 
105229e83d4bSgd78059 	/* possibly we should add in support for PAUSE frames */
105329e83d4bSgd78059 	DBG(DPHY, "writing nar = 0x%x", nar);
105429e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar);
105529e83d4bSgd78059 
105629e83d4bSgd78059 	DBG(DPHY, "writing tctl = 0x%x", tctl);
105729e83d4bSgd78059 	PUTCSR(mxfep, CSR_TCTL, tctl);
105829e83d4bSgd78059 
105929e83d4bSgd78059 	/* restart autonegotation */
106029e83d4bSgd78059 	DBG(DPHY, "writing tstat = 0x%x", TSTAT_ANS_START);
106129e83d4bSgd78059 	PUTCSR(mxfep, CSR_TSTAT, TSTAT_ANS_START);
106229e83d4bSgd78059 
106329e83d4bSgd78059 	/* restart tx/rx processes... */
106429e83d4bSgd78059 	if (restart != 0)
106529e83d4bSgd78059 		mxfe_startmac(mxfep);
106629e83d4bSgd78059 
106729e83d4bSgd78059 	/* Macronix initializations from Bolo Tsai */
106829e83d4bSgd78059 	PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000);
106929e83d4bSgd78059 	PUTCSR(mxfep, CSR_ACOMP, 0x11000);
107029e83d4bSgd78059 
107196fb08b9Sgd78059 	mxfep->mxfe_nwaystate = MXFE_NWAYCHECK;
107229e83d4bSgd78059 }
107329e83d4bSgd78059 
107429e83d4bSgd78059 void
mxfe_checklinknway(mxfe_t * mxfep)107529e83d4bSgd78059 mxfe_checklinknway(mxfe_t *mxfep)
107629e83d4bSgd78059 {
107796fb08b9Sgd78059 	unsigned	tstat;
107896fb08b9Sgd78059 	uint16_t	lpar;
107929e83d4bSgd78059 
108096fb08b9Sgd78059 	DBG(DPHY, "NWay check, state %x", mxfep->mxfe_nwaystate);
108129e83d4bSgd78059 	tstat = GETCSR(mxfep, CSR_TSTAT);
108229e83d4bSgd78059 	lpar = TSTAT_LPAR(tstat);
108329e83d4bSgd78059 
108429e83d4bSgd78059 	mxfep->mxfe_anlpar = lpar;
108529e83d4bSgd78059 	if (tstat & TSTAT_LPN) {
108629e83d4bSgd78059 		mxfep->mxfe_aner |= MII_AN_EXP_LPCANAN;
108729e83d4bSgd78059 	} else {
108829e83d4bSgd78059 		mxfep->mxfe_aner &= ~(MII_AN_EXP_LPCANAN);
108929e83d4bSgd78059 	}
109029e83d4bSgd78059 
109129e83d4bSgd78059 	DBG(DPHY, "tstat(CSR12) = 0x%x", tstat);
109229e83d4bSgd78059 	DBG(DPHY, "ANEG state = 0x%x", (tstat & TSTAT_ANS) >> 12);
109329e83d4bSgd78059 
109429e83d4bSgd78059 	if ((tstat & TSTAT_ANS) != TSTAT_ANS_OK) {
109529e83d4bSgd78059 		/* autoneg did not complete */
109629e83d4bSgd78059 		mxfep->mxfe_bmsr &= ~MII_STATUS_ANDONE;
109729e83d4bSgd78059 	} else {
109829e83d4bSgd78059 		mxfep->mxfe_bmsr |= ~MII_STATUS_ANDONE;
109929e83d4bSgd78059 	}
110029e83d4bSgd78059 
110129e83d4bSgd78059 	if ((tstat & TSTAT_100F) && (tstat & TSTAT_10F)) {
110229e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_DOWN;
110329e83d4bSgd78059 		mxfep->mxfe_ifspeed = 0;
110429e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN;
110596fb08b9Sgd78059 		mxfep->mxfe_nwaystate = MXFE_NOLINK;
110629e83d4bSgd78059 		mxfe_reportlink(mxfep);
110729e83d4bSgd78059 		mxfe_startnway(mxfep);
110829e83d4bSgd78059 		return;
110929e83d4bSgd78059 	}
111029e83d4bSgd78059 
111129e83d4bSgd78059 	/*
111229e83d4bSgd78059 	 * if the link is newly up, then we might need to set various
111329e83d4bSgd78059 	 * mode bits, or negotiate for parameters, etc.
111429e83d4bSgd78059 	 */
111529e83d4bSgd78059 	if (mxfep->mxfe_adv_aneg) {
111629e83d4bSgd78059 
111729e83d4bSgd78059 		uint16_t	anlpar;
111829e83d4bSgd78059 
111929e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_UP;
112029e83d4bSgd78059 		anlpar = mxfep->mxfe_anlpar;
112129e83d4bSgd78059 
112229e83d4bSgd78059 		if (tstat & TSTAT_LPN) {
112329e83d4bSgd78059 			/* partner has NWay */
112429e83d4bSgd78059 
112529e83d4bSgd78059 			if ((anlpar & MII_ABILITY_100BASE_TX_FD) &&
112629e83d4bSgd78059 			    mxfep->mxfe_adv_100fdx) {
112729e83d4bSgd78059 				mxfep->mxfe_ifspeed = 100000000;
112829e83d4bSgd78059 				mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
112929e83d4bSgd78059 			} else if ((anlpar & MII_ABILITY_100BASE_TX) &&
113029e83d4bSgd78059 			    mxfep->mxfe_adv_100hdx) {
113129e83d4bSgd78059 				mxfep->mxfe_ifspeed = 100000000;
113229e83d4bSgd78059 				mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
113329e83d4bSgd78059 			} else if ((anlpar & MII_ABILITY_10BASE_T_FD) &&
113429e83d4bSgd78059 			    mxfep->mxfe_adv_10fdx) {
113529e83d4bSgd78059 				mxfep->mxfe_ifspeed = 10000000;
113629e83d4bSgd78059 				mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
113729e83d4bSgd78059 			} else if ((anlpar & MII_ABILITY_10BASE_T) &&
113829e83d4bSgd78059 			    mxfep->mxfe_adv_10hdx) {
113929e83d4bSgd78059 				mxfep->mxfe_ifspeed = 10000000;
114029e83d4bSgd78059 				mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
114129e83d4bSgd78059 			} else {
114229e83d4bSgd78059 				mxfep->mxfe_ifspeed = 0;
114329e83d4bSgd78059 			}
114429e83d4bSgd78059 		} else {
114529e83d4bSgd78059 			/* link partner does not have NWay */
114629e83d4bSgd78059 			/* just assume half duplex, since we can't detect */
114729e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
114829e83d4bSgd78059 			if (!(tstat & TSTAT_100F)) {
114929e83d4bSgd78059 				DBG(DPHY, "Partner doesn't have NWAY");
115029e83d4bSgd78059 				mxfep->mxfe_ifspeed = 100000000;
115129e83d4bSgd78059 			} else {
115229e83d4bSgd78059 				mxfep->mxfe_ifspeed = 10000000;
115329e83d4bSgd78059 			}
115429e83d4bSgd78059 		}
115529e83d4bSgd78059 	} else {
115629e83d4bSgd78059 		/* forced modes */
115729e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_UP;
115829e83d4bSgd78059 		if (mxfep->mxfe_adv_100fdx) {
115929e83d4bSgd78059 			mxfep->mxfe_ifspeed = 100000000;
116029e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
116129e83d4bSgd78059 		} else if (mxfep->mxfe_adv_100hdx) {
116229e83d4bSgd78059 			mxfep->mxfe_ifspeed = 100000000;
116329e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
116429e83d4bSgd78059 		} else if (mxfep->mxfe_adv_10fdx) {
116529e83d4bSgd78059 			mxfep->mxfe_ifspeed = 10000000;
116629e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
116729e83d4bSgd78059 		} else if (mxfep->mxfe_adv_10hdx) {
116829e83d4bSgd78059 			mxfep->mxfe_ifspeed = 10000000;
116929e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
117029e83d4bSgd78059 		} else {
117129e83d4bSgd78059 			mxfep->mxfe_ifspeed = 0;
117229e83d4bSgd78059 		}
117329e83d4bSgd78059 	}
117429e83d4bSgd78059 	mxfe_reportlink(mxfep);
117596fb08b9Sgd78059 	mxfep->mxfe_nwaystate = MXFE_GOODLINK;
117629e83d4bSgd78059 }
117729e83d4bSgd78059 
117829e83d4bSgd78059 void
mxfe_startphynway(mxfe_t * mxfep)117929e83d4bSgd78059 mxfe_startphynway(mxfe_t *mxfep)
118029e83d4bSgd78059 {
118129e83d4bSgd78059 	/* take NWay and PHY out of reset */
118229e83d4bSgd78059 	PUTCSR(mxfep, CSR_SIA, SIA_NRESET);
118329e83d4bSgd78059 	drv_usecwait(500);
118429e83d4bSgd78059 
118596fb08b9Sgd78059 	mxfep->mxfe_nwaystate = MXFE_NOLINK;
118629e83d4bSgd78059 	mxfep->mxfe_bmsr = MII_STATUS_CANAUTONEG |
118729e83d4bSgd78059 	    MII_STATUS_100_BASEX_FD | MII_STATUS_100_BASEX |
118829e83d4bSgd78059 	    MII_STATUS_10_FD | MII_STATUS_10;
118996fb08b9Sgd78059 	mxfep->mxfe_cap_aneg =
119096fb08b9Sgd78059 	    mxfep->mxfe_cap_100fdx = mxfep->mxfe_cap_100hdx =
119196fb08b9Sgd78059 	    mxfep->mxfe_cap_10fdx = mxfep->mxfe_cap_10hdx = 1;
119229e83d4bSgd78059 
119329e83d4bSgd78059 	/* lie about the transceiver... its not really 802.3u compliant */
119429e83d4bSgd78059 	mxfep->mxfe_phyaddr = 0;
119529e83d4bSgd78059 	mxfep->mxfe_phyinuse = XCVR_100X;
119629e83d4bSgd78059 	mxfep->mxfe_phyid = 0;
119729e83d4bSgd78059 
119829e83d4bSgd78059 	/* 100-T4 not supported with NWay */
119929e83d4bSgd78059 	mxfep->mxfe_adv_100T4 = 0;
120096fb08b9Sgd78059 	mxfep->mxfe_cap_100T4 = 0;
120129e83d4bSgd78059 
120229e83d4bSgd78059 	/* make sure at least one valid mode is selected */
120329e83d4bSgd78059 	if ((!mxfep->mxfe_adv_100fdx) &&
120429e83d4bSgd78059 	    (!mxfep->mxfe_adv_100hdx) &&
120529e83d4bSgd78059 	    (!mxfep->mxfe_adv_10fdx) &&
120629e83d4bSgd78059 	    (!mxfep->mxfe_adv_10hdx)) {
120729e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "No valid link mode selected.");
120829e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "Powering down PHY.");
120929e83d4bSgd78059 		mxfe_stopphy(mxfep);
121029e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_DOWN;
121129e83d4bSgd78059 		if (mxfep->mxfe_flags & MXFE_RUNNING)
121229e83d4bSgd78059 			mxfe_reportlink(mxfep);
121329e83d4bSgd78059 		return;
121429e83d4bSgd78059 	}
121529e83d4bSgd78059 
121629e83d4bSgd78059 	if (mxfep->mxfe_adv_aneg == 0) {
121729e83d4bSgd78059 		/* forced mode */
121829e83d4bSgd78059 		unsigned	nar;
121929e83d4bSgd78059 		unsigned	tctl;
122029e83d4bSgd78059 
122129e83d4bSgd78059 		nar = GETCSR(mxfep, CSR_NAR);
122229e83d4bSgd78059 		tctl = GETCSR(mxfep, CSR_TCTL);
122329e83d4bSgd78059 
122429e83d4bSgd78059 		ASSERT((nar & (NAR_TX_ENABLE | NAR_RX_ENABLE)) == 0);
122529e83d4bSgd78059 
122629e83d4bSgd78059 		nar &= ~(NAR_FDX | NAR_PORTSEL | NAR_SCR | NAR_SPEED);
122729e83d4bSgd78059 		tctl &= ~TCTL_ANE;
122829e83d4bSgd78059 		if (mxfep->mxfe_adv_100fdx) {
122929e83d4bSgd78059 			nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX;
123029e83d4bSgd78059 		} else if (mxfep->mxfe_adv_100hdx) {
123129e83d4bSgd78059 			nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR;
123229e83d4bSgd78059 		} else if (mxfep->mxfe_adv_10fdx) {
123329e83d4bSgd78059 			nar |= NAR_FDX | NAR_SPEED;
123429e83d4bSgd78059 		} else { /* mxfep->mxfe_adv_10hdx */
123529e83d4bSgd78059 			nar |= NAR_SPEED;
123629e83d4bSgd78059 		}
123729e83d4bSgd78059 
123829e83d4bSgd78059 		PUTCSR(mxfep, CSR_NAR, nar);
123929e83d4bSgd78059 		PUTCSR(mxfep, CSR_TCTL, tctl);
124029e83d4bSgd78059 
124129e83d4bSgd78059 		/* Macronix initializations from Bolo Tsai */
124229e83d4bSgd78059 		PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000);
124329e83d4bSgd78059 		PUTCSR(mxfep, CSR_ACOMP, 0x11000);
124429e83d4bSgd78059 	} else {
124529e83d4bSgd78059 		mxfe_startnway(mxfep);
124629e83d4bSgd78059 	}
124729e83d4bSgd78059 	PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP |
124829e83d4bSgd78059 	    (MXFE_LINKTIMER * 1000 / TIMER_USEC));
124929e83d4bSgd78059 }
125029e83d4bSgd78059 
125129e83d4bSgd78059 /*
125229e83d4bSgd78059  * MII management.
125329e83d4bSgd78059  */
125429e83d4bSgd78059 void
mxfe_startphymii(mxfe_t * mxfep)125529e83d4bSgd78059 mxfe_startphymii(mxfe_t *mxfep)
125629e83d4bSgd78059 {
125729e83d4bSgd78059 	unsigned	phyaddr;
125829e83d4bSgd78059 	unsigned	bmcr;
125929e83d4bSgd78059 	unsigned	bmsr;
126029e83d4bSgd78059 	unsigned	anar;
126129e83d4bSgd78059 	unsigned	phyidr1;
126229e83d4bSgd78059 	unsigned	phyidr2;
126329e83d4bSgd78059 	int		retries;
126429e83d4bSgd78059 	int		cnt;
126529e83d4bSgd78059 
126629e83d4bSgd78059 	mxfep->mxfe_phyaddr = -1;
126729e83d4bSgd78059 
126829e83d4bSgd78059 	/* search for first PHY we can find */
126929e83d4bSgd78059 	for (phyaddr = 0; phyaddr < 32; phyaddr++) {
127029e83d4bSgd78059 		bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS);
127129e83d4bSgd78059 		if ((bmsr != 0) && (bmsr != 0xffff)) {
127229e83d4bSgd78059 			mxfep->mxfe_phyaddr = phyaddr;
127329e83d4bSgd78059 			break;
127429e83d4bSgd78059 		}
127529e83d4bSgd78059 	}
127629e83d4bSgd78059 
127729e83d4bSgd78059 	phyidr1 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDH);
127829e83d4bSgd78059 	phyidr2 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDL);
127929e83d4bSgd78059 	mxfep->mxfe_phyid = (phyidr1 << 16) | (phyidr2);
128029e83d4bSgd78059 
128129e83d4bSgd78059 	/*
128229e83d4bSgd78059 	 * Generally, all Macronix based devices use an internal
128329e83d4bSgd78059 	 * 100BASE-TX internal transceiver.  If we ever run into a
128429e83d4bSgd78059 	 * variation on this, then the following logic will need to be
128529e83d4bSgd78059 	 * enhanced.
128629e83d4bSgd78059 	 *
128729e83d4bSgd78059 	 * One could question the value of the XCVR_INUSE field in the
128829e83d4bSgd78059 	 * MII statistics.
128929e83d4bSgd78059 	 */
129029e83d4bSgd78059 	if (bmsr & MII_STATUS_100_BASE_T4) {
129129e83d4bSgd78059 		mxfep->mxfe_phyinuse = XCVR_100T4;
129229e83d4bSgd78059 	} else {
129329e83d4bSgd78059 		mxfep->mxfe_phyinuse = XCVR_100X;
129429e83d4bSgd78059 	}
129529e83d4bSgd78059 
129696fb08b9Sgd78059 	/* assume we support everything to start */
129796fb08b9Sgd78059 	mxfep->mxfe_cap_aneg = mxfep->mxfe_cap_100T4 =
129896fb08b9Sgd78059 	    mxfep->mxfe_cap_100fdx = mxfep->mxfe_cap_100hdx =
129996fb08b9Sgd78059 	    mxfep->mxfe_cap_10fdx = mxfep->mxfe_cap_10hdx = 1;
130096fb08b9Sgd78059 
130129e83d4bSgd78059 	DBG(DPHY, "phy at %d: %x,%x", phyaddr, phyidr1, phyidr2);
130229e83d4bSgd78059 	DBG(DPHY, "bmsr = %x", mxfe_miiread(mxfep,
130329e83d4bSgd78059 	    mxfep->mxfe_phyaddr, MII_STATUS));
130429e83d4bSgd78059 	DBG(DPHY, "anar = %x", mxfe_miiread(mxfep,
130529e83d4bSgd78059 	    mxfep->mxfe_phyaddr, MII_AN_ADVERT));
130629e83d4bSgd78059 	DBG(DPHY, "anlpar = %x", mxfe_miiread(mxfep,
130729e83d4bSgd78059 	    mxfep->mxfe_phyaddr, MII_AN_LPABLE));
130829e83d4bSgd78059 	DBG(DPHY, "aner = %x", mxfe_miiread(mxfep,
130929e83d4bSgd78059 	    mxfep->mxfe_phyaddr, MII_AN_EXPANSION));
131029e83d4bSgd78059 
131129e83d4bSgd78059 	DBG(DPHY, "resetting phy");
131229e83d4bSgd78059 
131329e83d4bSgd78059 	/* we reset the phy block */
131429e83d4bSgd78059 	mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, MII_CONTROL_RESET);
131529e83d4bSgd78059 	/*
131629e83d4bSgd78059 	 * wait for it to complete -- 500usec is still to short to
131729e83d4bSgd78059 	 * bother getting the system clock involved.
131829e83d4bSgd78059 	 */
131929e83d4bSgd78059 	drv_usecwait(500);
132029e83d4bSgd78059 	for (retries = 0; retries < 10; retries++) {
132129e83d4bSgd78059 		if (mxfe_miiread(mxfep, phyaddr, MII_CONTROL) &
132229e83d4bSgd78059 		    MII_CONTROL_RESET) {
132329e83d4bSgd78059 			drv_usecwait(500);
132429e83d4bSgd78059 			continue;
132529e83d4bSgd78059 		}
132629e83d4bSgd78059 		break;
132729e83d4bSgd78059 	}
132829e83d4bSgd78059 	if (retries == 100) {
132929e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "timeout waiting on phy to reset");
133029e83d4bSgd78059 		return;
133129e83d4bSgd78059 	}
133229e83d4bSgd78059 
133329e83d4bSgd78059 	DBG(DPHY, "phy reset complete");
133429e83d4bSgd78059 
133529e83d4bSgd78059 	bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS);
133629e83d4bSgd78059 	bmcr = mxfe_miiread(mxfep, phyaddr, MII_CONTROL);
133729e83d4bSgd78059 	anar = mxfe_miiread(mxfep, phyaddr, MII_AN_ADVERT);
133829e83d4bSgd78059 
133929e83d4bSgd78059 	anar &= ~(MII_ABILITY_100BASE_T4 |
134029e83d4bSgd78059 	    MII_ABILITY_100BASE_TX_FD | MII_ABILITY_100BASE_TX |
134129e83d4bSgd78059 	    MII_ABILITY_10BASE_T_FD | MII_ABILITY_10BASE_T);
134229e83d4bSgd78059 
134329e83d4bSgd78059 	/* disable modes not supported in hardware */
134429e83d4bSgd78059 	if (!(bmsr & MII_STATUS_100_BASE_T4)) {
134529e83d4bSgd78059 		mxfep->mxfe_adv_100T4 = 0;
134696fb08b9Sgd78059 		mxfep->mxfe_cap_100T4 = 0;
134729e83d4bSgd78059 	}
134829e83d4bSgd78059 	if (!(bmsr & MII_STATUS_100_BASEX_FD)) {
134929e83d4bSgd78059 		mxfep->mxfe_adv_100fdx = 0;
135096fb08b9Sgd78059 		mxfep->mxfe_cap_100fdx = 0;
135129e83d4bSgd78059 	}
135229e83d4bSgd78059 	if (!(bmsr & MII_STATUS_100_BASEX)) {
135329e83d4bSgd78059 		mxfep->mxfe_adv_100hdx = 0;
135496fb08b9Sgd78059 		mxfep->mxfe_cap_100hdx = 0;
135529e83d4bSgd78059 	}
135629e83d4bSgd78059 	if (!(bmsr & MII_STATUS_10_FD)) {
135729e83d4bSgd78059 		mxfep->mxfe_adv_10fdx = 0;
135896fb08b9Sgd78059 		mxfep->mxfe_cap_10fdx = 0;
135929e83d4bSgd78059 	}
136029e83d4bSgd78059 	if (!(bmsr & MII_STATUS_10)) {
136129e83d4bSgd78059 		mxfep->mxfe_adv_10hdx = 0;
136296fb08b9Sgd78059 		mxfep->mxfe_cap_10hdx = 0;
136329e83d4bSgd78059 	}
136429e83d4bSgd78059 	if (!(bmsr & MII_STATUS_CANAUTONEG)) {
136529e83d4bSgd78059 		mxfep->mxfe_adv_aneg = 0;
136696fb08b9Sgd78059 		mxfep->mxfe_cap_aneg = 0;
136729e83d4bSgd78059 	}
136829e83d4bSgd78059 
136929e83d4bSgd78059 	cnt = 0;
137029e83d4bSgd78059 	if (mxfep->mxfe_adv_100T4) {
137129e83d4bSgd78059 		anar |= MII_ABILITY_100BASE_T4;
137229e83d4bSgd78059 		cnt++;
137329e83d4bSgd78059 	}
137429e83d4bSgd78059 	if (mxfep->mxfe_adv_100fdx) {
137529e83d4bSgd78059 		anar |= MII_ABILITY_100BASE_TX_FD;
137629e83d4bSgd78059 		cnt++;
137729e83d4bSgd78059 	}
137829e83d4bSgd78059 	if (mxfep->mxfe_adv_100hdx) {
137929e83d4bSgd78059 		anar |= MII_ABILITY_100BASE_TX;
138029e83d4bSgd78059 		cnt++;
138129e83d4bSgd78059 	}
138229e83d4bSgd78059 	if (mxfep->mxfe_adv_10fdx) {
138329e83d4bSgd78059 		anar |= MII_ABILITY_10BASE_T_FD;
138429e83d4bSgd78059 		cnt++;
138529e83d4bSgd78059 	}
138629e83d4bSgd78059 	if (mxfep->mxfe_adv_10hdx) {
138729e83d4bSgd78059 		anar |= MII_ABILITY_10BASE_T;
138829e83d4bSgd78059 		cnt++;
138929e83d4bSgd78059 	}
139029e83d4bSgd78059 
139129e83d4bSgd78059 	/*
139229e83d4bSgd78059 	 * Make certain at least one valid link mode is selected.
139329e83d4bSgd78059 	 */
139429e83d4bSgd78059 	if (!cnt) {
139529e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "No valid link mode selected.");
139629e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "Powering down PHY.");
139729e83d4bSgd78059 		mxfe_stopphy(mxfep);
139829e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_DOWN;
139929e83d4bSgd78059 		if (mxfep->mxfe_flags & MXFE_RUNNING)
140029e83d4bSgd78059 			mxfe_reportlink(mxfep);
140129e83d4bSgd78059 		return;
140229e83d4bSgd78059 	}
140329e83d4bSgd78059 
140429e83d4bSgd78059 	if ((mxfep->mxfe_adv_aneg) && (bmsr & MII_STATUS_CANAUTONEG)) {
140529e83d4bSgd78059 		DBG(DPHY, "using autoneg mode");
140629e83d4bSgd78059 		bmcr = (MII_CONTROL_ANE | MII_CONTROL_RSAN);
140729e83d4bSgd78059 	} else {
140829e83d4bSgd78059 		DBG(DPHY, "using forced mode");
140929e83d4bSgd78059 		if (mxfep->mxfe_adv_100fdx) {
141029e83d4bSgd78059 			bmcr = (MII_CONTROL_100MB | MII_CONTROL_FDUPLEX);
141129e83d4bSgd78059 		} else if (mxfep->mxfe_adv_100hdx) {
141229e83d4bSgd78059 			bmcr = MII_CONTROL_100MB;
141329e83d4bSgd78059 		} else if (mxfep->mxfe_adv_10fdx) {
141429e83d4bSgd78059 			bmcr = MII_CONTROL_FDUPLEX;
141529e83d4bSgd78059 		} else {
141629e83d4bSgd78059 			/* 10HDX */
141729e83d4bSgd78059 			bmcr = 0;
141829e83d4bSgd78059 		}
141929e83d4bSgd78059 	}
142029e83d4bSgd78059 
142129e83d4bSgd78059 	DBG(DPHY, "programming anar to 0x%x", anar);
142229e83d4bSgd78059 	mxfe_miiwrite(mxfep, phyaddr, MII_AN_ADVERT, anar);
142329e83d4bSgd78059 	DBG(DPHY, "programming bmcr to 0x%x", bmcr);
142429e83d4bSgd78059 	mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, bmcr);
142529e83d4bSgd78059 
142629e83d4bSgd78059 	/*
142729e83d4bSgd78059 	 * schedule a query of the link status
142829e83d4bSgd78059 	 */
142929e83d4bSgd78059 	PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP |
143029e83d4bSgd78059 	    (MXFE_LINKTIMER * 1000 / TIMER_USEC));
143129e83d4bSgd78059 }
143229e83d4bSgd78059 
143329e83d4bSgd78059 void
mxfe_reportlink(mxfe_t * mxfep)143429e83d4bSgd78059 mxfe_reportlink(mxfe_t *mxfep)
143529e83d4bSgd78059 {
143629e83d4bSgd78059 	int changed = 0;
143729e83d4bSgd78059 
143829e83d4bSgd78059 	if (mxfep->mxfe_ifspeed != mxfep->mxfe_lastifspeed) {
143929e83d4bSgd78059 		mxfep->mxfe_lastifspeed = mxfep->mxfe_ifspeed;
144029e83d4bSgd78059 		changed++;
144129e83d4bSgd78059 	}
144229e83d4bSgd78059 	if (mxfep->mxfe_duplex != mxfep->mxfe_lastduplex) {
144329e83d4bSgd78059 		mxfep->mxfe_lastduplex = mxfep->mxfe_duplex;
144429e83d4bSgd78059 		changed++;
144529e83d4bSgd78059 	}
144629e83d4bSgd78059 	if (mxfep->mxfe_linkup != mxfep->mxfe_lastlinkup) {
144729e83d4bSgd78059 		mxfep->mxfe_lastlinkup = mxfep->mxfe_linkup;
144829e83d4bSgd78059 		changed++;
144929e83d4bSgd78059 	}
145029e83d4bSgd78059 	if (changed)
145129e83d4bSgd78059 		mac_link_update(mxfep->mxfe_mh, mxfep->mxfe_linkup);
145229e83d4bSgd78059 }
145329e83d4bSgd78059 
145429e83d4bSgd78059 void
mxfe_checklink(mxfe_t * mxfep)145529e83d4bSgd78059 mxfe_checklink(mxfe_t *mxfep)
145629e83d4bSgd78059 {
145729e83d4bSgd78059 	if ((mxfep->mxfe_flags & MXFE_RUNNING) == 0)
145829e83d4bSgd78059 		return;
145929e83d4bSgd78059 
146029e83d4bSgd78059 	if ((mxfep->mxfe_txstall_time != 0) &&
146129e83d4bSgd78059 	    (gethrtime() > mxfep->mxfe_txstall_time) &&
146229e83d4bSgd78059 	    (mxfep->mxfe_txavail != MXFE_TXRING)) {
146329e83d4bSgd78059 		mxfep->mxfe_txstall_time = 0;
146429e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "TX stall detected!");
146529e83d4bSgd78059 		mxfe_resetall(mxfep);
146629e83d4bSgd78059 		return;
146729e83d4bSgd78059 	}
146829e83d4bSgd78059 
146929e83d4bSgd78059 	switch (MXFE_MODEL(mxfep)) {
147029e83d4bSgd78059 	case MXFE_98713A:
147129e83d4bSgd78059 		mxfe_checklinkmii(mxfep);
147229e83d4bSgd78059 		break;
147329e83d4bSgd78059 	default:
147429e83d4bSgd78059 		mxfe_checklinknway(mxfep);
147529e83d4bSgd78059 	}
147629e83d4bSgd78059 }
147729e83d4bSgd78059 
147829e83d4bSgd78059 void
mxfe_checklinkmii(mxfe_t * mxfep)147929e83d4bSgd78059 mxfe_checklinkmii(mxfe_t *mxfep)
148029e83d4bSgd78059 {
148129e83d4bSgd78059 	/* read MII state registers */
148229e83d4bSgd78059 	uint16_t 	bmsr;
148329e83d4bSgd78059 	uint16_t 	bmcr;
148429e83d4bSgd78059 	uint16_t 	anar;
148529e83d4bSgd78059 	uint16_t 	anlpar;
148629e83d4bSgd78059 	uint16_t 	aner;
148729e83d4bSgd78059 
148829e83d4bSgd78059 	/* read this twice, to clear latched link state */
148929e83d4bSgd78059 	bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS);
149029e83d4bSgd78059 	bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS);
149129e83d4bSgd78059 	bmcr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL);
149229e83d4bSgd78059 	anar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_ADVERT);
149329e83d4bSgd78059 	anlpar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_LPABLE);
149429e83d4bSgd78059 	aner = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_EXPANSION);
149529e83d4bSgd78059 
149629e83d4bSgd78059 	mxfep->mxfe_bmsr = bmsr;
149729e83d4bSgd78059 	mxfep->mxfe_anlpar = anlpar;
149829e83d4bSgd78059 	mxfep->mxfe_aner = aner;
149929e83d4bSgd78059 
150029e83d4bSgd78059 	if (bmsr & MII_STATUS_REMFAULT) {
150129e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "Remote fault detected.");
150229e83d4bSgd78059 	}
150329e83d4bSgd78059 	if (bmsr & MII_STATUS_JABBERING) {
150429e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip, "Jabber condition detected.");
150529e83d4bSgd78059 	}
150629e83d4bSgd78059 	if ((bmsr & MII_STATUS_LINKUP) == 0) {
150729e83d4bSgd78059 		/* no link */
150829e83d4bSgd78059 		mxfep->mxfe_ifspeed = 0;
150929e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN;
151029e83d4bSgd78059 		mxfep->mxfe_linkup = LINK_STATE_DOWN;
151129e83d4bSgd78059 		mxfe_reportlink(mxfep);
151229e83d4bSgd78059 		return;
151329e83d4bSgd78059 	}
151429e83d4bSgd78059 
151529e83d4bSgd78059 	DBG(DCHATTY, "link up!");
151629e83d4bSgd78059 	mxfep->mxfe_linkup = LINK_STATE_UP;
151729e83d4bSgd78059 
151829e83d4bSgd78059 	if (!(bmcr & MII_CONTROL_ANE)) {
151929e83d4bSgd78059 		/* forced mode */
152029e83d4bSgd78059 		if (bmcr & MII_CONTROL_100MB) {
152129e83d4bSgd78059 			mxfep->mxfe_ifspeed = 100000000;
152229e83d4bSgd78059 		} else {
152329e83d4bSgd78059 			mxfep->mxfe_ifspeed = 10000000;
152429e83d4bSgd78059 		}
152529e83d4bSgd78059 		if (bmcr & MII_CONTROL_FDUPLEX) {
152629e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
152729e83d4bSgd78059 		} else {
152829e83d4bSgd78059 			mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
152929e83d4bSgd78059 		}
153029e83d4bSgd78059 	} else if ((!(bmsr & MII_STATUS_CANAUTONEG)) ||
153129e83d4bSgd78059 	    (!(bmsr & MII_STATUS_ANDONE))) {
153229e83d4bSgd78059 		mxfep->mxfe_ifspeed = 0;
153329e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN;
153429e83d4bSgd78059 	} else if (anar & anlpar & MII_ABILITY_100BASE_TX_FD) {
153529e83d4bSgd78059 		mxfep->mxfe_ifspeed = 100000000;
153629e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
153729e83d4bSgd78059 	} else if (anar & anlpar & MII_ABILITY_100BASE_T4) {
153829e83d4bSgd78059 		mxfep->mxfe_ifspeed = 100000000;
153929e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
154029e83d4bSgd78059 	} else if (anar & anlpar & MII_ABILITY_100BASE_TX) {
154129e83d4bSgd78059 		mxfep->mxfe_ifspeed = 100000000;
154229e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
154329e83d4bSgd78059 	} else if (anar & anlpar & MII_ABILITY_10BASE_T_FD) {
154429e83d4bSgd78059 		mxfep->mxfe_ifspeed = 10000000;
154529e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_FULL;
154629e83d4bSgd78059 	} else if (anar & anlpar & MII_ABILITY_10BASE_T) {
154729e83d4bSgd78059 		mxfep->mxfe_ifspeed = 10000000;
154829e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_HALF;
154929e83d4bSgd78059 	} else {
155029e83d4bSgd78059 		mxfep->mxfe_ifspeed = 0;
155129e83d4bSgd78059 		mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN;
155229e83d4bSgd78059 	}
155329e83d4bSgd78059 
155429e83d4bSgd78059 	mxfe_reportlink(mxfep);
155529e83d4bSgd78059 }
155629e83d4bSgd78059 
155729e83d4bSgd78059 void
mxfe_miitristate(mxfe_t * mxfep)155829e83d4bSgd78059 mxfe_miitristate(mxfe_t *mxfep)
155929e83d4bSgd78059 {
156029e83d4bSgd78059 	unsigned val = SPR_SROM_WRITE | SPR_MII_CTRL;
156129e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val);
156229e83d4bSgd78059 	drv_usecwait(1);
156329e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK);
156429e83d4bSgd78059 	drv_usecwait(1);
156529e83d4bSgd78059 }
156629e83d4bSgd78059 
156729e83d4bSgd78059 void
mxfe_miiwritebit(mxfe_t * mxfep,uint8_t bit)156896fb08b9Sgd78059 mxfe_miiwritebit(mxfe_t *mxfep, uint8_t bit)
156929e83d4bSgd78059 {
157029e83d4bSgd78059 	unsigned val = bit ? SPR_MII_DOUT : 0;
157129e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val);
157229e83d4bSgd78059 	drv_usecwait(1);
157329e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK);
157429e83d4bSgd78059 	drv_usecwait(1);
157529e83d4bSgd78059 }
157629e83d4bSgd78059 
157796fb08b9Sgd78059 uint8_t
mxfe_miireadbit(mxfe_t * mxfep)157829e83d4bSgd78059 mxfe_miireadbit(mxfe_t *mxfep)
157929e83d4bSgd78059 {
158029e83d4bSgd78059 	unsigned val = SPR_MII_CTRL | SPR_SROM_READ;
158196fb08b9Sgd78059 	uint8_t bit;
158229e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val);
158329e83d4bSgd78059 	drv_usecwait(1);
158429e83d4bSgd78059 	bit = (GETCSR(mxfep, CSR_SPR) & SPR_MII_DIN) ? 1 : 0;
158529e83d4bSgd78059 	PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK);
158629e83d4bSgd78059 	drv_usecwait(1);
158729e83d4bSgd78059 	return (bit);
158829e83d4bSgd78059 }
158929e83d4bSgd78059 
159096fb08b9Sgd78059 uint16_t
mxfe_miiread(mxfe_t * mxfep,int phy,int reg)159129e83d4bSgd78059 mxfe_miiread(mxfe_t *mxfep, int phy, int reg)
159229e83d4bSgd78059 {
159329e83d4bSgd78059 	switch (MXFE_MODEL(mxfep)) {
159429e83d4bSgd78059 	case MXFE_98713A:
159529e83d4bSgd78059 		return (mxfe_miiread98713(mxfep, phy, reg));
159629e83d4bSgd78059 	default:
159729e83d4bSgd78059 		return (0xffff);
159829e83d4bSgd78059 	}
159929e83d4bSgd78059 }
160029e83d4bSgd78059 
160196fb08b9Sgd78059 uint16_t
mxfe_miireadgeneral(mxfe_t * mxfep,int phy,int reg)160229e83d4bSgd78059 mxfe_miireadgeneral(mxfe_t *mxfep, int phy, int reg)
160329e83d4bSgd78059 {
160496fb08b9Sgd78059 	uint16_t	value = 0;
160529e83d4bSgd78059 	int		i;
160629e83d4bSgd78059 
160729e83d4bSgd78059 	/* send the 32 bit preamble */
160829e83d4bSgd78059 	for (i = 0; i < 32; i++) {
160929e83d4bSgd78059 		mxfe_miiwritebit(mxfep, 1);
161029e83d4bSgd78059 	}
161129e83d4bSgd78059 
161229e83d4bSgd78059 	/* send the start code - 01b */
161329e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
161429e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 1);
161529e83d4bSgd78059 
161629e83d4bSgd78059 	/* send the opcode for read, - 10b */
161729e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 1);
161829e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
161929e83d4bSgd78059 
162029e83d4bSgd78059 	/* next we send the 5 bit phy address */
162129e83d4bSgd78059 	for (i = 0x10; i > 0; i >>= 1) {
162229e83d4bSgd78059 		mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0);
162329e83d4bSgd78059 	}
162429e83d4bSgd78059 
162529e83d4bSgd78059 	/* the 5 bit register address goes next */
162629e83d4bSgd78059 	for (i = 0x10; i > 0; i >>= 1) {
162729e83d4bSgd78059 		mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0);
162829e83d4bSgd78059 	}
162929e83d4bSgd78059 
163029e83d4bSgd78059 	/* turnaround - tristate followed by logic 0 */
163129e83d4bSgd78059 	mxfe_miitristate(mxfep);
163229e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
163329e83d4bSgd78059 
163429e83d4bSgd78059 	/* read the 16 bit register value */
163529e83d4bSgd78059 	for (i = 0x8000; i > 0; i >>= 1) {
163629e83d4bSgd78059 		value <<= 1;
163729e83d4bSgd78059 		value |= mxfe_miireadbit(mxfep);
163829e83d4bSgd78059 	}
163929e83d4bSgd78059 	mxfe_miitristate(mxfep);
164029e83d4bSgd78059 	return (value);
164129e83d4bSgd78059 }
164229e83d4bSgd78059 
164396fb08b9Sgd78059 uint16_t
mxfe_miiread98713(mxfe_t * mxfep,int phy,int reg)164429e83d4bSgd78059 mxfe_miiread98713(mxfe_t *mxfep, int phy, int reg)
164529e83d4bSgd78059 {
164629e83d4bSgd78059 	unsigned nar;
164796fb08b9Sgd78059 	uint16_t retval;
164829e83d4bSgd78059 	/*
164929e83d4bSgd78059 	 * like an ordinary MII, but we have to turn off portsel while
165029e83d4bSgd78059 	 * we read it.
165129e83d4bSgd78059 	 */
165229e83d4bSgd78059 	nar = GETCSR(mxfep, CSR_NAR);
165329e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL);
165429e83d4bSgd78059 	retval = mxfe_miireadgeneral(mxfep, phy, reg);
165529e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar);
165629e83d4bSgd78059 	return (retval);
165729e83d4bSgd78059 }
165829e83d4bSgd78059 
165929e83d4bSgd78059 void
mxfe_miiwrite(mxfe_t * mxfep,int phy,int reg,uint16_t val)166029e83d4bSgd78059 mxfe_miiwrite(mxfe_t *mxfep, int phy, int reg, uint16_t val)
166129e83d4bSgd78059 {
166229e83d4bSgd78059 	switch (MXFE_MODEL(mxfep)) {
166329e83d4bSgd78059 	case MXFE_98713A:
166429e83d4bSgd78059 		mxfe_miiwrite98713(mxfep, phy, reg, val);
166529e83d4bSgd78059 		break;
166629e83d4bSgd78059 	default:
166729e83d4bSgd78059 		break;
166829e83d4bSgd78059 	}
166929e83d4bSgd78059 }
167029e83d4bSgd78059 
167129e83d4bSgd78059 void
mxfe_miiwritegeneral(mxfe_t * mxfep,int phy,int reg,uint16_t val)167229e83d4bSgd78059 mxfe_miiwritegeneral(mxfe_t *mxfep, int phy, int reg, uint16_t val)
167329e83d4bSgd78059 {
167429e83d4bSgd78059 	int i;
167529e83d4bSgd78059 
167629e83d4bSgd78059 	/* send the 32 bit preamble */
167729e83d4bSgd78059 	for (i = 0; i < 32; i++) {
167829e83d4bSgd78059 		mxfe_miiwritebit(mxfep, 1);
167929e83d4bSgd78059 	}
168029e83d4bSgd78059 
168129e83d4bSgd78059 	/* send the start code - 01b */
168229e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
168329e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 1);
168429e83d4bSgd78059 
168529e83d4bSgd78059 	/* send the opcode for write, - 01b */
168629e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
168729e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 1);
168829e83d4bSgd78059 
168929e83d4bSgd78059 	/* next we send the 5 bit phy address */
169029e83d4bSgd78059 	for (i = 0x10; i > 0; i >>= 1) {
169129e83d4bSgd78059 		mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0);
169229e83d4bSgd78059 	}
169329e83d4bSgd78059 
169429e83d4bSgd78059 	/* the 5 bit register address goes next */
169529e83d4bSgd78059 	for (i = 0x10; i > 0; i >>= 1) {
169629e83d4bSgd78059 		mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0);
169729e83d4bSgd78059 	}
169829e83d4bSgd78059 
169929e83d4bSgd78059 	/* turnaround - tristate followed by logic 0 */
170029e83d4bSgd78059 	mxfe_miitristate(mxfep);
170129e83d4bSgd78059 	mxfe_miiwritebit(mxfep, 0);
170229e83d4bSgd78059 
170329e83d4bSgd78059 	/* now write out our data (16 bits) */
170429e83d4bSgd78059 	for (i = 0x8000; i > 0; i >>= 1) {
170529e83d4bSgd78059 		mxfe_miiwritebit(mxfep, (val & i) ? 1 : 0);
170629e83d4bSgd78059 	}
170729e83d4bSgd78059 
170829e83d4bSgd78059 	/* idle mode */
170929e83d4bSgd78059 	mxfe_miitristate(mxfep);
171029e83d4bSgd78059 }
171129e83d4bSgd78059 
171229e83d4bSgd78059 void
mxfe_miiwrite98713(mxfe_t * mxfep,int phy,int reg,uint16_t val)171329e83d4bSgd78059 mxfe_miiwrite98713(mxfe_t *mxfep, int phy, int reg, uint16_t val)
171429e83d4bSgd78059 {
171529e83d4bSgd78059 	unsigned nar;
171629e83d4bSgd78059 	/*
171729e83d4bSgd78059 	 * like an ordinary MII, but we have to turn off portsel while
171829e83d4bSgd78059 	 * we read it.
171929e83d4bSgd78059 	 */
172029e83d4bSgd78059 	nar = GETCSR(mxfep, CSR_NAR);
172129e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL);
172229e83d4bSgd78059 	mxfe_miiwritegeneral(mxfep, phy, reg, val);
172329e83d4bSgd78059 	PUTCSR(mxfep, CSR_NAR, nar);
172429e83d4bSgd78059 }
172529e83d4bSgd78059 
172629e83d4bSgd78059 int
mxfe_m_start(void * arg)172729e83d4bSgd78059 mxfe_m_start(void *arg)
172829e83d4bSgd78059 {
172929e83d4bSgd78059 	mxfe_t	*mxfep = arg;
173029e83d4bSgd78059 
173129e83d4bSgd78059 	/* grab exclusive access to the card */
173229e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
173329e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
173429e83d4bSgd78059 
173529e83d4bSgd78059 	mxfe_startall(mxfep);
173629e83d4bSgd78059 	mxfep->mxfe_flags |= MXFE_RUNNING;
173729e83d4bSgd78059 
173829e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
173929e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
174029e83d4bSgd78059 	return (0);
174129e83d4bSgd78059 }
174229e83d4bSgd78059 
174329e83d4bSgd78059 void
mxfe_m_stop(void * arg)174429e83d4bSgd78059 mxfe_m_stop(void *arg)
174529e83d4bSgd78059 {
174629e83d4bSgd78059 	mxfe_t	*mxfep = arg;
174729e83d4bSgd78059 
174829e83d4bSgd78059 	/* exclusive access to the hardware! */
174929e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
175029e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
175129e83d4bSgd78059 
175229e83d4bSgd78059 	mxfe_stopall(mxfep);
175329e83d4bSgd78059 	mxfep->mxfe_flags &= ~MXFE_RUNNING;
175429e83d4bSgd78059 
175529e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
175629e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
175729e83d4bSgd78059 }
175829e83d4bSgd78059 
175929e83d4bSgd78059 void
mxfe_startmac(mxfe_t * mxfep)176029e83d4bSgd78059 mxfe_startmac(mxfe_t *mxfep)
176129e83d4bSgd78059 {
176229e83d4bSgd78059 	/* verify exclusive access to the card */
176329e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_intrlock));
176429e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
176529e83d4bSgd78059 
176629e83d4bSgd78059 	/* start the card */
176729e83d4bSgd78059 	SETBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE);
176829e83d4bSgd78059 
176929e83d4bSgd78059 	if (mxfep->mxfe_txavail != MXFE_TXRING)
177029e83d4bSgd78059 		PUTCSR(mxfep, CSR_TDR, 0);
177129e83d4bSgd78059 
177229e83d4bSgd78059 	/* tell the mac that we are ready to go! */
177329e83d4bSgd78059 	if (mxfep->mxfe_flags & MXFE_RUNNING)
177429e83d4bSgd78059 		mac_tx_update(mxfep->mxfe_mh);
177529e83d4bSgd78059 }
177629e83d4bSgd78059 
177729e83d4bSgd78059 void
mxfe_stopmac(mxfe_t * mxfep)177829e83d4bSgd78059 mxfe_stopmac(mxfe_t *mxfep)
177929e83d4bSgd78059 {
178029e83d4bSgd78059 	int		i;
178129e83d4bSgd78059 
178229e83d4bSgd78059 	/* exclusive access to the hardware! */
178329e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_intrlock));
178429e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
178529e83d4bSgd78059 
178629e83d4bSgd78059 	CLRBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE);
178729e83d4bSgd78059 
178829e83d4bSgd78059 	/*
178929e83d4bSgd78059 	 * A 1518 byte frame at 10Mbps takes about 1.2 msec to drain.
179029e83d4bSgd78059 	 * We just add up to the nearest msec (2), which should be
179129e83d4bSgd78059 	 * plenty to complete.
179229e83d4bSgd78059 	 *
179329e83d4bSgd78059 	 * Note that some chips never seem to indicate the transition to
179429e83d4bSgd78059 	 * the stopped state properly.  Experience shows that we can safely
179529e83d4bSgd78059 	 * proceed anyway, after waiting the requisite timeout.
179629e83d4bSgd78059 	 */
179729e83d4bSgd78059 	for (i = 2000; i != 0; i -= 10) {
179829e83d4bSgd78059 		if ((GETCSR(mxfep, CSR_SR) & (SR_TX_STATE | SR_RX_STATE)) == 0)
179929e83d4bSgd78059 			break;
180029e83d4bSgd78059 		drv_usecwait(10);
180129e83d4bSgd78059 	}
180229e83d4bSgd78059 
180329e83d4bSgd78059 	/* prevent an interrupt */
180429e83d4bSgd78059 	PUTCSR(mxfep, CSR_SR, INT_RXSTOPPED | INT_TXSTOPPED);
180529e83d4bSgd78059 }
180629e83d4bSgd78059 
180729e83d4bSgd78059 void
mxfe_resetrings(mxfe_t * mxfep)180829e83d4bSgd78059 mxfe_resetrings(mxfe_t *mxfep)
180929e83d4bSgd78059 {
181029e83d4bSgd78059 	int	i;
181129e83d4bSgd78059 
181229e83d4bSgd78059 	/* now we need to reset the pointers... */
181329e83d4bSgd78059 	PUTCSR(mxfep, CSR_RDB, 0);
181429e83d4bSgd78059 	PUTCSR(mxfep, CSR_TDB, 0);
181529e83d4bSgd78059 
181629e83d4bSgd78059 	/* reset the descriptor ring pointers */
181729e83d4bSgd78059 	mxfep->mxfe_rxhead = 0;
181829e83d4bSgd78059 	mxfep->mxfe_txreclaim = 0;
181929e83d4bSgd78059 	mxfep->mxfe_txsend = 0;
182029e83d4bSgd78059 	mxfep->mxfe_txavail = MXFE_TXRING;
182129e83d4bSgd78059 
182229e83d4bSgd78059 	/* set up transmit descriptor ring */
182329e83d4bSgd78059 	for (i = 0; i < MXFE_TXRING; i++) {
182429e83d4bSgd78059 		mxfe_desc_t	*tmdp = &mxfep->mxfe_txdescp[i];
182529e83d4bSgd78059 		unsigned	control = 0;
182629e83d4bSgd78059 		if (i == (MXFE_TXRING - 1)) {
182729e83d4bSgd78059 			control |= TXCTL_ENDRING;
182829e83d4bSgd78059 		}
182929e83d4bSgd78059 		PUTTXDESC(mxfep, tmdp->desc_status, 0);
183029e83d4bSgd78059 		PUTTXDESC(mxfep, tmdp->desc_control, control);
183129e83d4bSgd78059 		PUTTXDESC(mxfep, tmdp->desc_buffer1, 0);
183229e83d4bSgd78059 		PUTTXDESC(mxfep, tmdp->desc_buffer2, 0);
183329e83d4bSgd78059 		SYNCTXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV);
183429e83d4bSgd78059 	}
183529e83d4bSgd78059 	PUTCSR(mxfep, CSR_TDB, mxfep->mxfe_txdesc_paddr);
183629e83d4bSgd78059 
183729e83d4bSgd78059 	/* make the receive buffers available */
183829e83d4bSgd78059 	for (i = 0; i < MXFE_RXRING; i++) {
183929e83d4bSgd78059 		mxfe_rxbuf_t	*rxb = mxfep->mxfe_rxbufs[i];
184029e83d4bSgd78059 		mxfe_desc_t	*rmdp = &mxfep->mxfe_rxdescp[i];
184129e83d4bSgd78059 		unsigned	control;
184229e83d4bSgd78059 
184329e83d4bSgd78059 		control = MXFE_BUFSZ & RXCTL_BUFLEN1;
184429e83d4bSgd78059 		if (i == (MXFE_RXRING - 1)) {
184529e83d4bSgd78059 			control |= RXCTL_ENDRING;
184629e83d4bSgd78059 		}
184729e83d4bSgd78059 		PUTRXDESC(mxfep, rmdp->desc_buffer1, rxb->rxb_paddr);
184829e83d4bSgd78059 		PUTRXDESC(mxfep, rmdp->desc_buffer2, 0);
184929e83d4bSgd78059 		PUTRXDESC(mxfep, rmdp->desc_control, control);
185029e83d4bSgd78059 		PUTRXDESC(mxfep, rmdp->desc_status, RXSTAT_OWN);
185129e83d4bSgd78059 		SYNCRXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV);
185229e83d4bSgd78059 	}
185329e83d4bSgd78059 	PUTCSR(mxfep, CSR_RDB, mxfep->mxfe_rxdesc_paddr);
185429e83d4bSgd78059 }
185529e83d4bSgd78059 
185629e83d4bSgd78059 void
mxfe_stopall(mxfe_t * mxfep)185729e83d4bSgd78059 mxfe_stopall(mxfe_t *mxfep)
185829e83d4bSgd78059 {
185929e83d4bSgd78059 	mxfe_disableinterrupts(mxfep);
186029e83d4bSgd78059 
186129e83d4bSgd78059 	mxfe_stopmac(mxfep);
186229e83d4bSgd78059 
186329e83d4bSgd78059 	/* stop the phy */
186429e83d4bSgd78059 	mxfe_stopphy(mxfep);
186529e83d4bSgd78059 }
186629e83d4bSgd78059 
186729e83d4bSgd78059 void
mxfe_startall(mxfe_t * mxfep)186829e83d4bSgd78059 mxfe_startall(mxfe_t *mxfep)
186929e83d4bSgd78059 {
187029e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_intrlock));
187129e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
187229e83d4bSgd78059 
187329e83d4bSgd78059 	/* make sure interrupts are disabled to begin */
187429e83d4bSgd78059 	mxfe_disableinterrupts(mxfep);
187529e83d4bSgd78059 
187629e83d4bSgd78059 	/* initialize the chip */
187729e83d4bSgd78059 	(void) mxfe_initialize(mxfep);
187829e83d4bSgd78059 
187929e83d4bSgd78059 	/* now we can enable interrupts */
188029e83d4bSgd78059 	mxfe_enableinterrupts(mxfep);
188129e83d4bSgd78059 
188229e83d4bSgd78059 	/* start up the phy */
188329e83d4bSgd78059 	mxfe_startphy(mxfep);
188429e83d4bSgd78059 
188529e83d4bSgd78059 	/* start up the mac */
188629e83d4bSgd78059 	mxfe_startmac(mxfep);
188729e83d4bSgd78059 }
188829e83d4bSgd78059 
188929e83d4bSgd78059 void
mxfe_resetall(mxfe_t * mxfep)189029e83d4bSgd78059 mxfe_resetall(mxfe_t *mxfep)
189129e83d4bSgd78059 {
189229e83d4bSgd78059 	mxfep->mxfe_resetting = B_TRUE;
189329e83d4bSgd78059 	mxfe_stopall(mxfep);
189429e83d4bSgd78059 	mxfep->mxfe_resetting = B_FALSE;
189529e83d4bSgd78059 	mxfe_startall(mxfep);
189629e83d4bSgd78059 }
189729e83d4bSgd78059 
189829e83d4bSgd78059 mxfe_txbuf_t *
mxfe_alloctxbuf(mxfe_t * mxfep)189929e83d4bSgd78059 mxfe_alloctxbuf(mxfe_t *mxfep)
190029e83d4bSgd78059 {
190129e83d4bSgd78059 	ddi_dma_cookie_t	dmac;
190229e83d4bSgd78059 	unsigned		ncookies;
190329e83d4bSgd78059 	mxfe_txbuf_t		*txb;
190429e83d4bSgd78059 	size_t			len;
190529e83d4bSgd78059 
190629e83d4bSgd78059 	txb = kmem_zalloc(sizeof (*txb), KM_SLEEP);
190729e83d4bSgd78059 
190829e83d4bSgd78059 	if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_txattr,
190929e83d4bSgd78059 	    DDI_DMA_SLEEP, NULL, &txb->txb_dmah) != DDI_SUCCESS) {
191029e83d4bSgd78059 		return (NULL);
191129e83d4bSgd78059 	}
191229e83d4bSgd78059 
191329e83d4bSgd78059 	if (ddi_dma_mem_alloc(txb->txb_dmah, MXFE_BUFSZ, &mxfe_bufattr,
191429e83d4bSgd78059 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &txb->txb_buf,
191529e83d4bSgd78059 	    &len, &txb->txb_acch) != DDI_SUCCESS) {
191629e83d4bSgd78059 		return (NULL);
191729e83d4bSgd78059 	}
191829e83d4bSgd78059 	if (ddi_dma_addr_bind_handle(txb->txb_dmah, NULL, txb->txb_buf,
191929e83d4bSgd78059 	    len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
192029e83d4bSgd78059 	    &dmac, &ncookies) != DDI_DMA_MAPPED) {
192129e83d4bSgd78059 		return (NULL);
192229e83d4bSgd78059 	}
192329e83d4bSgd78059 	txb->txb_paddr = dmac.dmac_address;
192429e83d4bSgd78059 
192529e83d4bSgd78059 	return (txb);
192629e83d4bSgd78059 }
192729e83d4bSgd78059 
192829e83d4bSgd78059 void
mxfe_destroytxbuf(mxfe_txbuf_t * txb)192929e83d4bSgd78059 mxfe_destroytxbuf(mxfe_txbuf_t *txb)
193029e83d4bSgd78059 {
193129e83d4bSgd78059 	if (txb != NULL) {
193229e83d4bSgd78059 		if (txb->txb_paddr)
193329e83d4bSgd78059 			(void) ddi_dma_unbind_handle(txb->txb_dmah);
193429e83d4bSgd78059 		if (txb->txb_acch)
193529e83d4bSgd78059 			ddi_dma_mem_free(&txb->txb_acch);
193629e83d4bSgd78059 		if (txb->txb_dmah)
193729e83d4bSgd78059 			ddi_dma_free_handle(&txb->txb_dmah);
193829e83d4bSgd78059 		kmem_free(txb, sizeof (*txb));
193929e83d4bSgd78059 	}
194029e83d4bSgd78059 }
194129e83d4bSgd78059 
194229e83d4bSgd78059 mxfe_rxbuf_t *
mxfe_allocrxbuf(mxfe_t * mxfep)194329e83d4bSgd78059 mxfe_allocrxbuf(mxfe_t *mxfep)
194429e83d4bSgd78059 {
194529e83d4bSgd78059 	mxfe_rxbuf_t 		*rxb;
194629e83d4bSgd78059 	size_t			len;
194729e83d4bSgd78059 	unsigned		ccnt;
194829e83d4bSgd78059 	ddi_dma_cookie_t	dmac;
194929e83d4bSgd78059 
195029e83d4bSgd78059 	rxb = kmem_zalloc(sizeof (*rxb), KM_SLEEP);
195129e83d4bSgd78059 
195229e83d4bSgd78059 	if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr,
195329e83d4bSgd78059 	    DDI_DMA_SLEEP, NULL, &rxb->rxb_dmah) != DDI_SUCCESS) {
195429e83d4bSgd78059 		kmem_free(rxb, sizeof (*rxb));
195529e83d4bSgd78059 		return (NULL);
195629e83d4bSgd78059 	}
195729e83d4bSgd78059 	if (ddi_dma_mem_alloc(rxb->rxb_dmah, MXFE_BUFSZ, &mxfe_bufattr,
195829e83d4bSgd78059 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
195929e83d4bSgd78059 	    &rxb->rxb_buf, &len, &rxb->rxb_acch) != DDI_SUCCESS) {
196029e83d4bSgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
196129e83d4bSgd78059 		kmem_free(rxb, sizeof (*rxb));
196229e83d4bSgd78059 		return (NULL);
196329e83d4bSgd78059 	}
196429e83d4bSgd78059 	if (ddi_dma_addr_bind_handle(rxb->rxb_dmah, NULL, rxb->rxb_buf, len,
196529e83d4bSgd78059 	    DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
196629e83d4bSgd78059 	    &ccnt) != DDI_DMA_MAPPED) {
196729e83d4bSgd78059 		ddi_dma_mem_free(&rxb->rxb_acch);
196829e83d4bSgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
196929e83d4bSgd78059 		kmem_free(rxb, sizeof (*rxb));
197029e83d4bSgd78059 		return (NULL);
197129e83d4bSgd78059 	}
197229e83d4bSgd78059 	rxb->rxb_paddr = dmac.dmac_address;
197329e83d4bSgd78059 
197429e83d4bSgd78059 	return (rxb);
197529e83d4bSgd78059 }
197629e83d4bSgd78059 
197729e83d4bSgd78059 void
mxfe_destroyrxbuf(mxfe_rxbuf_t * rxb)197829e83d4bSgd78059 mxfe_destroyrxbuf(mxfe_rxbuf_t *rxb)
197929e83d4bSgd78059 {
198029e83d4bSgd78059 	if (rxb != NULL) {
198129e83d4bSgd78059 		(void) ddi_dma_unbind_handle(rxb->rxb_dmah);
198229e83d4bSgd78059 		ddi_dma_mem_free(&rxb->rxb_acch);
198329e83d4bSgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
198429e83d4bSgd78059 		kmem_free(rxb, sizeof (*rxb));
198529e83d4bSgd78059 	}
198629e83d4bSgd78059 }
198729e83d4bSgd78059 
198829e83d4bSgd78059 /*
198929e83d4bSgd78059  * Allocate receive resources.
199029e83d4bSgd78059  */
199129e83d4bSgd78059 int
mxfe_allocrxring(mxfe_t * mxfep)199229e83d4bSgd78059 mxfe_allocrxring(mxfe_t *mxfep)
199329e83d4bSgd78059 {
199429e83d4bSgd78059 	int			rval;
199529e83d4bSgd78059 	int			i;
199629e83d4bSgd78059 	size_t			size;
199729e83d4bSgd78059 	size_t			len;
199829e83d4bSgd78059 	ddi_dma_cookie_t	dmac;
199929e83d4bSgd78059 	unsigned		ncookies;
200029e83d4bSgd78059 	caddr_t			kaddr;
200129e83d4bSgd78059 
200229e83d4bSgd78059 	size = MXFE_RXRING * sizeof (mxfe_desc_t);
200329e83d4bSgd78059 
200429e83d4bSgd78059 	rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr,
200529e83d4bSgd78059 	    DDI_DMA_SLEEP, NULL, &mxfep->mxfe_rxdesc_dmah);
200629e83d4bSgd78059 	if (rval != DDI_SUCCESS) {
200729e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
200829e83d4bSgd78059 		    "unable to allocate DMA handle for rx descriptors");
200929e83d4bSgd78059 		return (DDI_FAILURE);
201029e83d4bSgd78059 	}
201129e83d4bSgd78059 
201229e83d4bSgd78059 	rval = ddi_dma_mem_alloc(mxfep->mxfe_rxdesc_dmah, size, &mxfe_devattr,
201329e83d4bSgd78059 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
201429e83d4bSgd78059 	    &mxfep->mxfe_rxdesc_acch);
201529e83d4bSgd78059 	if (rval != DDI_SUCCESS) {
201629e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
201729e83d4bSgd78059 		    "unable to allocate DMA memory for rx descriptors");
201829e83d4bSgd78059 		return (DDI_FAILURE);
201929e83d4bSgd78059 	}
202029e83d4bSgd78059 
202129e83d4bSgd78059 	rval = ddi_dma_addr_bind_handle(mxfep->mxfe_rxdesc_dmah, NULL, kaddr,
202229e83d4bSgd78059 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
202329e83d4bSgd78059 	    &dmac, &ncookies);
202429e83d4bSgd78059 	if (rval != DDI_DMA_MAPPED) {
202529e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
202629e83d4bSgd78059 		    "unable to bind DMA for rx descriptors");
202729e83d4bSgd78059 		return (DDI_FAILURE);
202829e83d4bSgd78059 	}
202929e83d4bSgd78059 
203029e83d4bSgd78059 	/* because of mxfe_dma_attr */
203129e83d4bSgd78059 	ASSERT(ncookies == 1);
203229e83d4bSgd78059 
203329e83d4bSgd78059 	/* we take the 32-bit physical address out of the cookie */
203429e83d4bSgd78059 	mxfep->mxfe_rxdesc_paddr = dmac.dmac_address;
203529e83d4bSgd78059 	mxfep->mxfe_rxdescp = (void *)kaddr;
203629e83d4bSgd78059 
203729e83d4bSgd78059 	/* allocate buffer pointers (not the buffers themselves, yet) */
203829e83d4bSgd78059 	mxfep->mxfe_rxbufs = kmem_zalloc(MXFE_RXRING * sizeof (mxfe_rxbuf_t *),
203929e83d4bSgd78059 	    KM_SLEEP);
204029e83d4bSgd78059 
204129e83d4bSgd78059 	/* now allocate rx buffers */
204229e83d4bSgd78059 	for (i = 0; i < MXFE_RXRING; i++) {
204329e83d4bSgd78059 		mxfe_rxbuf_t *rxb = mxfe_allocrxbuf(mxfep);
204429e83d4bSgd78059 		if (rxb == NULL)
204529e83d4bSgd78059 			return (DDI_FAILURE);
204629e83d4bSgd78059 		mxfep->mxfe_rxbufs[i] = rxb;
204729e83d4bSgd78059 	}
204829e83d4bSgd78059 
204929e83d4bSgd78059 	return (DDI_SUCCESS);
205029e83d4bSgd78059 }
205129e83d4bSgd78059 
205229e83d4bSgd78059 /*
205329e83d4bSgd78059  * Allocate transmit resources.
205429e83d4bSgd78059  */
205529e83d4bSgd78059 int
mxfe_alloctxring(mxfe_t * mxfep)205629e83d4bSgd78059 mxfe_alloctxring(mxfe_t *mxfep)
205729e83d4bSgd78059 {
205829e83d4bSgd78059 	int			rval;
205929e83d4bSgd78059 	int			i;
206029e83d4bSgd78059 	size_t			size;
206129e83d4bSgd78059 	size_t			len;
206229e83d4bSgd78059 	ddi_dma_cookie_t	dmac;
206329e83d4bSgd78059 	unsigned		ncookies;
206429e83d4bSgd78059 	caddr_t			kaddr;
206529e83d4bSgd78059 
206629e83d4bSgd78059 	size = MXFE_TXRING * sizeof (mxfe_desc_t);
206729e83d4bSgd78059 
206829e83d4bSgd78059 	rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr,
206929e83d4bSgd78059 	    DDI_DMA_SLEEP, NULL, &mxfep->mxfe_txdesc_dmah);
207029e83d4bSgd78059 	if (rval != DDI_SUCCESS) {
207129e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
207229e83d4bSgd78059 		    "unable to allocate DMA handle for tx descriptors");
207329e83d4bSgd78059 		return (DDI_FAILURE);
207429e83d4bSgd78059 	}
207529e83d4bSgd78059 
207629e83d4bSgd78059 	rval = ddi_dma_mem_alloc(mxfep->mxfe_txdesc_dmah, size, &mxfe_devattr,
207729e83d4bSgd78059 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
207829e83d4bSgd78059 	    &mxfep->mxfe_txdesc_acch);
207929e83d4bSgd78059 	if (rval != DDI_SUCCESS) {
208029e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
208129e83d4bSgd78059 		    "unable to allocate DMA memory for tx descriptors");
208229e83d4bSgd78059 		return (DDI_FAILURE);
208329e83d4bSgd78059 	}
208429e83d4bSgd78059 
208529e83d4bSgd78059 	rval = ddi_dma_addr_bind_handle(mxfep->mxfe_txdesc_dmah, NULL, kaddr,
208629e83d4bSgd78059 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
208729e83d4bSgd78059 	    &dmac, &ncookies);
208829e83d4bSgd78059 	if (rval != DDI_DMA_MAPPED) {
208929e83d4bSgd78059 		mxfe_error(mxfep->mxfe_dip,
209029e83d4bSgd78059 		    "unable to bind DMA for tx descriptors");
209129e83d4bSgd78059 		return (DDI_FAILURE);
209229e83d4bSgd78059 	}
209329e83d4bSgd78059 
209429e83d4bSgd78059 	/* because of mxfe_dma_attr */
209529e83d4bSgd78059 	ASSERT(ncookies == 1);
209629e83d4bSgd78059 
209729e83d4bSgd78059 	/* we take the 32-bit physical address out of the cookie */
209829e83d4bSgd78059 	mxfep->mxfe_txdesc_paddr = dmac.dmac_address;
209929e83d4bSgd78059 	mxfep->mxfe_txdescp = (void *)kaddr;
210029e83d4bSgd78059 
210129e83d4bSgd78059 	/* allocate buffer pointers (not the buffers themselves, yet) */
210229e83d4bSgd78059 	mxfep->mxfe_txbufs = kmem_zalloc(MXFE_TXRING * sizeof (mxfe_txbuf_t *),
210329e83d4bSgd78059 	    KM_SLEEP);
210429e83d4bSgd78059 
210529e83d4bSgd78059 	/* now allocate tx buffers */
210629e83d4bSgd78059 	for (i = 0; i < MXFE_TXRING; i++) {
210729e83d4bSgd78059 		mxfe_txbuf_t *txb = mxfe_alloctxbuf(mxfep);
210829e83d4bSgd78059 		if (txb == NULL)
210929e83d4bSgd78059 			return (DDI_FAILURE);
211029e83d4bSgd78059 		/* stick it in the stack */
211129e83d4bSgd78059 		mxfep->mxfe_txbufs[i] = txb;
211229e83d4bSgd78059 	}
211329e83d4bSgd78059 
211429e83d4bSgd78059 	return (DDI_SUCCESS);
211529e83d4bSgd78059 }
211629e83d4bSgd78059 
211729e83d4bSgd78059 void
mxfe_freerxring(mxfe_t * mxfep)211829e83d4bSgd78059 mxfe_freerxring(mxfe_t *mxfep)
211929e83d4bSgd78059 {
212029e83d4bSgd78059 	int		i;
212129e83d4bSgd78059 
212229e83d4bSgd78059 	for (i = 0; i < MXFE_RXRING; i++) {
212329e83d4bSgd78059 		mxfe_destroyrxbuf(mxfep->mxfe_rxbufs[i]);
212429e83d4bSgd78059 	}
212529e83d4bSgd78059 
212629e83d4bSgd78059 	if (mxfep->mxfe_rxbufs) {
212729e83d4bSgd78059 		kmem_free(mxfep->mxfe_rxbufs,
212829e83d4bSgd78059 		    MXFE_RXRING * sizeof (mxfe_rxbuf_t *));
212929e83d4bSgd78059 	}
213029e83d4bSgd78059 
213129e83d4bSgd78059 	if (mxfep->mxfe_rxdesc_paddr)
213229e83d4bSgd78059 		(void) ddi_dma_unbind_handle(mxfep->mxfe_rxdesc_dmah);
213329e83d4bSgd78059 	if (mxfep->mxfe_rxdesc_acch)
213429e83d4bSgd78059 		ddi_dma_mem_free(&mxfep->mxfe_rxdesc_acch);
213529e83d4bSgd78059 	if (mxfep->mxfe_rxdesc_dmah)
213629e83d4bSgd78059 		ddi_dma_free_handle(&mxfep->mxfe_rxdesc_dmah);
213729e83d4bSgd78059 }
213829e83d4bSgd78059 
213929e83d4bSgd78059 void
mxfe_freetxring(mxfe_t * mxfep)214029e83d4bSgd78059 mxfe_freetxring(mxfe_t *mxfep)
214129e83d4bSgd78059 {
214229e83d4bSgd78059 	int			i;
214329e83d4bSgd78059 
214429e83d4bSgd78059 	for (i = 0; i < MXFE_TXRING; i++) {
214529e83d4bSgd78059 		mxfe_destroytxbuf(mxfep->mxfe_txbufs[i]);
214629e83d4bSgd78059 	}
214729e83d4bSgd78059 
214829e83d4bSgd78059 	if (mxfep->mxfe_txbufs) {
214929e83d4bSgd78059 		kmem_free(mxfep->mxfe_txbufs,
215029e83d4bSgd78059 		    MXFE_TXRING * sizeof (mxfe_txbuf_t *));
215129e83d4bSgd78059 	}
215229e83d4bSgd78059 	if (mxfep->mxfe_txdesc_paddr)
215329e83d4bSgd78059 		(void) ddi_dma_unbind_handle(mxfep->mxfe_txdesc_dmah);
215429e83d4bSgd78059 	if (mxfep->mxfe_txdesc_acch)
215529e83d4bSgd78059 		ddi_dma_mem_free(&mxfep->mxfe_txdesc_acch);
215629e83d4bSgd78059 	if (mxfep->mxfe_txdesc_dmah)
215729e83d4bSgd78059 		ddi_dma_free_handle(&mxfep->mxfe_txdesc_dmah);
215829e83d4bSgd78059 }
215929e83d4bSgd78059 
216029e83d4bSgd78059 /*
216129e83d4bSgd78059  * Interrupt service routine.
216229e83d4bSgd78059  */
216329e83d4bSgd78059 unsigned
mxfe_intr(caddr_t arg)216429e83d4bSgd78059 mxfe_intr(caddr_t arg)
216529e83d4bSgd78059 {
216629e83d4bSgd78059 	mxfe_t		*mxfep = (void *)arg;
216729e83d4bSgd78059 	uint32_t	status;
216829e83d4bSgd78059 	mblk_t		*mp = NULL;
216977860f66SGarrett D'Amore 	boolean_t	error = B_FALSE;
217029e83d4bSgd78059 
217129e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
217229e83d4bSgd78059 
217329e83d4bSgd78059 	if (mxfep->mxfe_flags & MXFE_SUSPENDED) {
217429e83d4bSgd78059 		/* we cannot receive interrupts! */
217529e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
217629e83d4bSgd78059 		return (DDI_INTR_UNCLAIMED);
217729e83d4bSgd78059 	}
217829e83d4bSgd78059 
217929e83d4bSgd78059 	/* check interrupt status bits, did we interrupt? */
218029e83d4bSgd78059 	status = GETCSR(mxfep, CSR_SR) & INT_ALL;
218129e83d4bSgd78059 
218229e83d4bSgd78059 	if (status == 0) {
218329e83d4bSgd78059 		KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
218429e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
218529e83d4bSgd78059 		return (DDI_INTR_UNCLAIMED);
218629e83d4bSgd78059 	}
218729e83d4bSgd78059 	/* ack the interrupt */
218829e83d4bSgd78059 	PUTCSR(mxfep, CSR_SR, status);
218929e83d4bSgd78059 	KIOIP->intrs[KSTAT_INTR_HARD]++;
219029e83d4bSgd78059 
219129e83d4bSgd78059 	if (!(mxfep->mxfe_flags & MXFE_RUNNING)) {
219229e83d4bSgd78059 		/* not running, don't touch anything */
219329e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_intrlock);
219429e83d4bSgd78059 		return (DDI_INTR_CLAIMED);
219529e83d4bSgd78059 	}
219629e83d4bSgd78059 
219729e83d4bSgd78059 	if (status & INT_RXOK) {
219829e83d4bSgd78059 		/* receive packets */
219977860f66SGarrett D'Amore 		if (mxfe_receive(mxfep, &mp)) {
220077860f66SGarrett D'Amore 			error = B_TRUE;
220177860f66SGarrett D'Amore 		}
220229e83d4bSgd78059 	}
220329e83d4bSgd78059 
220429e83d4bSgd78059 	if (status & INT_TXOK) {
220529e83d4bSgd78059 		/* transmit completed */
220629e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_xmtlock);
220729e83d4bSgd78059 		mxfe_reclaim(mxfep);
220829e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
220929e83d4bSgd78059 	}
221029e83d4bSgd78059 
221129e83d4bSgd78059 	if (((status & (INT_TIMER|INT_ANEG)) != 0) ||
221229e83d4bSgd78059 	    ((mxfep->mxfe_linkup == LINK_STATE_UP) &&
221329e83d4bSgd78059 	    ((status & (INT_10LINK|INT_100LINK)) != 0))) {
221429e83d4bSgd78059 		/* rescan the link */
221529e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_xmtlock);
221629e83d4bSgd78059 		mxfe_checklink(mxfep);
221729e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
221829e83d4bSgd78059 	}
221929e83d4bSgd78059 
222029e83d4bSgd78059 	if (status & (INT_RXSTOPPED|INT_TXSTOPPED|INT_RXNOBUF|
222129e83d4bSgd78059 	    INT_RXJABBER|INT_TXJABBER|INT_TXUNDERFLOW)) {
222229e83d4bSgd78059 
222329e83d4bSgd78059 		if (status & (INT_RXJABBER | INT_TXJABBER)) {
222429e83d4bSgd78059 			mxfep->mxfe_jabber++;
222529e83d4bSgd78059 		}
222677860f66SGarrett D'Amore 		DBG(DWARN, "error interrupt: status %x", status);
222777860f66SGarrett D'Amore 		error = B_TRUE;
222829e83d4bSgd78059 	}
222929e83d4bSgd78059 
223029e83d4bSgd78059 	if (status & INT_BUSERR) {
223129e83d4bSgd78059 		switch (status & SR_BERR_TYPE) {
223229e83d4bSgd78059 		case SR_BERR_PARITY:
223329e83d4bSgd78059 			mxfe_error(mxfep->mxfe_dip, "PCI parity error");
223429e83d4bSgd78059 			break;
223529e83d4bSgd78059 		case SR_BERR_TARGET_ABORT:
223629e83d4bSgd78059 			mxfe_error(mxfep->mxfe_dip, "PCI target abort");
223729e83d4bSgd78059 			break;
223829e83d4bSgd78059 		case SR_BERR_MASTER_ABORT:
223929e83d4bSgd78059 			mxfe_error(mxfep->mxfe_dip, "PCI master abort");
224029e83d4bSgd78059 			break;
224129e83d4bSgd78059 		default:
224229e83d4bSgd78059 			mxfe_error(mxfep->mxfe_dip, "Unknown PCI error");
224329e83d4bSgd78059 			break;
224429e83d4bSgd78059 		}
224529e83d4bSgd78059 
224677860f66SGarrett D'Amore 		error = B_TRUE;
224777860f66SGarrett D'Amore 	}
224877860f66SGarrett D'Amore 
224977860f66SGarrett D'Amore 	if (error) {
225029e83d4bSgd78059 		/* reset the chip in an attempt to fix things */
225129e83d4bSgd78059 		mutex_enter(&mxfep->mxfe_xmtlock);
225229e83d4bSgd78059 		mxfe_resetall(mxfep);
225329e83d4bSgd78059 		mutex_exit(&mxfep->mxfe_xmtlock);
225429e83d4bSgd78059 	}
225529e83d4bSgd78059 
225629e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
225729e83d4bSgd78059 
225829e83d4bSgd78059 	/*
225929e83d4bSgd78059 	 * Send up packets.  We do this outside of the intrlock.
226029e83d4bSgd78059 	 */
226129e83d4bSgd78059 	if (mp) {
226229e83d4bSgd78059 		mac_rx(mxfep->mxfe_mh, NULL, mp);
226329e83d4bSgd78059 	}
226429e83d4bSgd78059 
226529e83d4bSgd78059 	return (DDI_INTR_CLAIMED);
226629e83d4bSgd78059 }
226729e83d4bSgd78059 
226829e83d4bSgd78059 void
mxfe_enableinterrupts(mxfe_t * mxfep)226929e83d4bSgd78059 mxfe_enableinterrupts(mxfe_t *mxfep)
227029e83d4bSgd78059 {
227129e83d4bSgd78059 	unsigned mask = INT_WANTED;
227229e83d4bSgd78059 
227329e83d4bSgd78059 	if (mxfep->mxfe_wantw)
227429e83d4bSgd78059 		mask |= INT_TXOK;
227529e83d4bSgd78059 
227629e83d4bSgd78059 	if (MXFE_MODEL(mxfep) != MXFE_98713A)
227729e83d4bSgd78059 		mask |= INT_LINKSTATUS;
227829e83d4bSgd78059 
227929e83d4bSgd78059 	DBG(DINTR, "setting int mask to 0x%x", mask);
228029e83d4bSgd78059 	PUTCSR(mxfep, CSR_IER, mask);
228129e83d4bSgd78059 }
228229e83d4bSgd78059 
228329e83d4bSgd78059 void
mxfe_disableinterrupts(mxfe_t * mxfep)228429e83d4bSgd78059 mxfe_disableinterrupts(mxfe_t *mxfep)
228529e83d4bSgd78059 {
228629e83d4bSgd78059 	/* disable further interrupts */
228729e83d4bSgd78059 	PUTCSR(mxfep, CSR_IER, 0);
228829e83d4bSgd78059 
228929e83d4bSgd78059 	/* clear any pending interrupts */
229029e83d4bSgd78059 	PUTCSR(mxfep, CSR_SR, INT_ALL);
229129e83d4bSgd78059 }
229229e83d4bSgd78059 
229329e83d4bSgd78059 void
mxfe_send_setup(mxfe_t * mxfep)229429e83d4bSgd78059 mxfe_send_setup(mxfe_t *mxfep)
229529e83d4bSgd78059 {
229629e83d4bSgd78059 	mxfe_txbuf_t	*txb;
229729e83d4bSgd78059 	mxfe_desc_t	*tmdp;
229829e83d4bSgd78059 
229929e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
230029e83d4bSgd78059 
230129e83d4bSgd78059 	/* setup frame -- must be at head of list -- guaranteed by caller! */
230229e83d4bSgd78059 	ASSERT(mxfep->mxfe_txsend == 0);
230329e83d4bSgd78059 
230429e83d4bSgd78059 	txb = mxfep->mxfe_txbufs[0];
230529e83d4bSgd78059 	tmdp = &mxfep->mxfe_txdescp[0];
230629e83d4bSgd78059 
230729e83d4bSgd78059 	bzero(txb->txb_buf, MXFE_SETUP_LEN);
230829e83d4bSgd78059 
230929e83d4bSgd78059 	/* program the unicast address */
231029e83d4bSgd78059 	txb->txb_buf[156] = mxfep->mxfe_curraddr[0];
231129e83d4bSgd78059 	txb->txb_buf[157] = mxfep->mxfe_curraddr[1];
231229e83d4bSgd78059 	txb->txb_buf[160] = mxfep->mxfe_curraddr[2];
231329e83d4bSgd78059 	txb->txb_buf[161] = mxfep->mxfe_curraddr[3];
231429e83d4bSgd78059 	txb->txb_buf[164] = mxfep->mxfe_curraddr[4];
231529e83d4bSgd78059 	txb->txb_buf[165] = mxfep->mxfe_curraddr[5];
231629e83d4bSgd78059 
231729e83d4bSgd78059 	/* make sure that the hardware can see it */
231829e83d4bSgd78059 	SYNCTXBUF(txb, MXFE_SETUP_LEN, DDI_DMA_SYNC_FORDEV);
231929e83d4bSgd78059 
232029e83d4bSgd78059 	PUTTXDESC(mxfep, tmdp->desc_control,
232129e83d4bSgd78059 	    TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE | TXCTL_HASHPERF |
232229e83d4bSgd78059 	    TXCTL_SETUP | MXFE_SETUP_LEN);
232329e83d4bSgd78059 
232429e83d4bSgd78059 	PUTTXDESC(mxfep, tmdp->desc_buffer1, txb->txb_paddr);
232529e83d4bSgd78059 	PUTTXDESC(mxfep, tmdp->desc_buffer2, 0);
232629e83d4bSgd78059 	PUTTXDESC(mxfep, tmdp->desc_status, TXSTAT_OWN);
232729e83d4bSgd78059 
232829e83d4bSgd78059 	/* sync the descriptor out to the device */
232929e83d4bSgd78059 	SYNCTXDESC(mxfep, 0, DDI_DMA_SYNC_FORDEV);
233029e83d4bSgd78059 
233129e83d4bSgd78059 	/*
233229e83d4bSgd78059 	 * wake up the chip ... inside the lock to protect against DR suspend,
233329e83d4bSgd78059 	 * etc.
233429e83d4bSgd78059 	 */
233529e83d4bSgd78059 	PUTCSR(mxfep, CSR_TDR, 0);
233629e83d4bSgd78059 	mxfep->mxfe_txsend++;
233729e83d4bSgd78059 	mxfep->mxfe_txavail--;
233829e83d4bSgd78059 
233929e83d4bSgd78059 	/*
234029e83d4bSgd78059 	 * Program promiscuous mode.
234129e83d4bSgd78059 	 */
234229e83d4bSgd78059 	if (mxfep->mxfe_promisc) {
234329e83d4bSgd78059 		SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC);
234429e83d4bSgd78059 	} else {
234529e83d4bSgd78059 		CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC);
234629e83d4bSgd78059 	}
234729e83d4bSgd78059 }
234829e83d4bSgd78059 
234929e83d4bSgd78059 boolean_t
mxfe_send(mxfe_t * mxfep,mblk_t * mp)235029e83d4bSgd78059 mxfe_send(mxfe_t *mxfep, mblk_t *mp)
235129e83d4bSgd78059 {
235229e83d4bSgd78059 	size_t			len;
235329e83d4bSgd78059 	mxfe_txbuf_t		*txb;
235429e83d4bSgd78059 	mxfe_desc_t		*tmd;
235529e83d4bSgd78059 	uint32_t		control;
235629e83d4bSgd78059 	int			txsend;
235729e83d4bSgd78059 
235829e83d4bSgd78059 	ASSERT(mutex_owned(&mxfep->mxfe_xmtlock));
235929e83d4bSgd78059 	ASSERT(mp != NULL);
236029e83d4bSgd78059 
236129e83d4bSgd78059 	len = msgsize(mp);
236229e83d4bSgd78059 	if (len > ETHERVLANMTU) {
236329e83d4bSgd78059 		DBG(DXMIT, "frame too long: %d", len);
236429e83d4bSgd78059 		mxfep->mxfe_macxmt_errors++;
236529e83d4bSgd78059 		freemsg(mp);
236629e83d4bSgd78059 		return (B_TRUE);
236729e83d4bSgd78059 	}
236829e83d4bSgd78059 
236929e83d4bSgd78059 	if (mxfep->mxfe_txavail < MXFE_TXRECLAIM)
237029e83d4bSgd78059 		mxfe_reclaim(mxfep);
237129e83d4bSgd78059 
237229e83d4bSgd78059 	if (mxfep->mxfe_txavail == 0) {
237329e83d4bSgd78059 		/* no more tmds */
237429e83d4bSgd78059 		mxfep->mxfe_wantw = B_TRUE;
237529e83d4bSgd78059 		/* enable TX interrupt */
237629e83d4bSgd78059 		mxfe_enableinterrupts(mxfep);
237729e83d4bSgd78059 		return (B_FALSE);
237829e83d4bSgd78059 	}
237929e83d4bSgd78059 
238029e83d4bSgd78059 	txsend = mxfep->mxfe_txsend;
238129e83d4bSgd78059 
238229e83d4bSgd78059 	/*
238329e83d4bSgd78059 	 * For simplicity, we just do a copy into a preallocated
238429e83d4bSgd78059 	 * DMA buffer.
238529e83d4bSgd78059 	 */
238629e83d4bSgd78059 
238729e83d4bSgd78059 	txb = mxfep->mxfe_txbufs[txsend];
238829e83d4bSgd78059 	mcopymsg(mp, txb->txb_buf);	/* frees mp! */
238929e83d4bSgd78059 
239029e83d4bSgd78059 	/*
239129e83d4bSgd78059 	 * Statistics.
239229e83d4bSgd78059 	 */
239329e83d4bSgd78059 	mxfep->mxfe_opackets++;
239429e83d4bSgd78059 	mxfep->mxfe_obytes += len;
239529e83d4bSgd78059 	if (txb->txb_buf[0] & 0x1) {
239629e83d4bSgd78059 		if (bcmp(txb->txb_buf, mxfe_broadcast, ETHERADDRL) != 0)
239729e83d4bSgd78059 			mxfep->mxfe_multixmt++;
239829e83d4bSgd78059 		else
239929e83d4bSgd78059 			mxfep->mxfe_brdcstxmt++;
240029e83d4bSgd78059 	}
240129e83d4bSgd78059 
240229e83d4bSgd78059 	/* note len is already known to be a small unsigned */
240329e83d4bSgd78059 	control = len | TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE;
240429e83d4bSgd78059 
240529e83d4bSgd78059 	if (txsend == (MXFE_TXRING - 1))
240629e83d4bSgd78059 		control |= TXCTL_ENDRING;
240729e83d4bSgd78059 
240829e83d4bSgd78059 	tmd = &mxfep->mxfe_txdescp[txsend];
240929e83d4bSgd78059 
241029e83d4bSgd78059 	SYNCTXBUF(txb, len, DDI_DMA_SYNC_FORDEV);
241129e83d4bSgd78059 	PUTTXDESC(mxfep, tmd->desc_control, control);
241229e83d4bSgd78059 	PUTTXDESC(mxfep, tmd->desc_buffer1, txb->txb_paddr);
241329e83d4bSgd78059 	PUTTXDESC(mxfep, tmd->desc_buffer2, 0);
241429e83d4bSgd78059 	PUTTXDESC(mxfep, tmd->desc_status, TXSTAT_OWN);
241529e83d4bSgd78059 	/* sync the descriptor out to the device */
241629e83d4bSgd78059 	SYNCTXDESC(mxfep, txsend, DDI_DMA_SYNC_FORDEV);
241729e83d4bSgd78059 
241829e83d4bSgd78059 	/*
241929e83d4bSgd78059 	 * Note the new values of txavail and txsend.
242029e83d4bSgd78059 	 */
242129e83d4bSgd78059 	mxfep->mxfe_txavail--;
242229e83d4bSgd78059 	mxfep->mxfe_txsend = (txsend + 1) % MXFE_TXRING;
242329e83d4bSgd78059 
242429e83d4bSgd78059 	/*
242529e83d4bSgd78059 	 * It should never, ever take more than 5 seconds to drain
242629e83d4bSgd78059 	 * the ring.  If it happens, then we are stuck!
242729e83d4bSgd78059 	 */
242829e83d4bSgd78059 	mxfep->mxfe_txstall_time = gethrtime() + (5 * 1000000000ULL);
242929e83d4bSgd78059 
243029e83d4bSgd78059 	/*
243129e83d4bSgd78059 	 * wake up the chip ... inside the lock to protect against DR suspend,
243229e83d4bSgd78059 	 * etc.
243329e83d4bSgd78059 	 */
243429e83d4bSgd78059 	PUTCSR(mxfep, CSR_TDR, 0);
243529e83d4bSgd78059 
243629e83d4bSgd78059 	return (B_TRUE);
243729e83d4bSgd78059 }
243829e83d4bSgd78059 
243929e83d4bSgd78059 /*
244029e83d4bSgd78059  * Reclaim buffers that have completed transmission.
244129e83d4bSgd78059  */
244229e83d4bSgd78059 void
mxfe_reclaim(mxfe_t * mxfep)244329e83d4bSgd78059 mxfe_reclaim(mxfe_t *mxfep)
244429e83d4bSgd78059 {
244529e83d4bSgd78059 	mxfe_desc_t	*tmdp;
244629e83d4bSgd78059 
244729e83d4bSgd78059 	while (mxfep->mxfe_txavail != MXFE_TXRING) {
244829e83d4bSgd78059 		uint32_t	status;
244929e83d4bSgd78059 		uint32_t	control;
245029e83d4bSgd78059 		int		index = mxfep->mxfe_txreclaim;
245129e83d4bSgd78059 
245229e83d4bSgd78059 		tmdp = &mxfep->mxfe_txdescp[index];
245329e83d4bSgd78059 
245429e83d4bSgd78059 		/* sync it before we read it */
245529e83d4bSgd78059 		SYNCTXDESC(mxfep, index, DDI_DMA_SYNC_FORKERNEL);
245629e83d4bSgd78059 
245729e83d4bSgd78059 		control = GETTXDESC(mxfep, tmdp->desc_control);
245829e83d4bSgd78059 		status = GETTXDESC(mxfep, tmdp->desc_status);
245929e83d4bSgd78059 
246029e83d4bSgd78059 		if (status & TXSTAT_OWN) {
246129e83d4bSgd78059 			/* chip is still working on it, we're done */
246229e83d4bSgd78059 			break;
246329e83d4bSgd78059 		}
246429e83d4bSgd78059 
246529e83d4bSgd78059 		mxfep->mxfe_txavail++;
246629e83d4bSgd78059 		mxfep->mxfe_txreclaim = (index + 1) % MXFE_TXRING;
246729e83d4bSgd78059 
246829e83d4bSgd78059 		/* in the most common successful case, all bits are clear */
246929e83d4bSgd78059 		if (status == 0)
247029e83d4bSgd78059 			continue;
247129e83d4bSgd78059 
247229e83d4bSgd78059 		if (((control & TXCTL_SETUP) != 0) ||
247329e83d4bSgd78059 		    ((control & TXCTL_LAST) == 0)) {
247429e83d4bSgd78059 			/* no interesting statistics here */
247529e83d4bSgd78059 			continue;
247629e83d4bSgd78059 		}
247729e83d4bSgd78059 
247829e83d4bSgd78059 		if (status & TXSTAT_TXERR) {
247929e83d4bSgd78059 			mxfep->mxfe_errxmt++;
248029e83d4bSgd78059 
248129e83d4bSgd78059 			if (status & TXSTAT_JABBER) {
248229e83d4bSgd78059 				/* transmit jabber timeout */
248329e83d4bSgd78059 				mxfep->mxfe_macxmt_errors++;
248429e83d4bSgd78059 			}
248529e83d4bSgd78059 			if (status & (TXSTAT_CARRLOST | TXSTAT_NOCARR)) {
248629e83d4bSgd78059 				mxfep->mxfe_carrier_errors++;
248729e83d4bSgd78059 			}
248829e83d4bSgd78059 			if (status & TXSTAT_UFLOW) {
248929e83d4bSgd78059 				mxfep->mxfe_underflow++;
249029e83d4bSgd78059 			}
249129e83d4bSgd78059 			if (status & TXSTAT_LATECOL) {
249229e83d4bSgd78059 				mxfep->mxfe_tx_late_collisions++;
249329e83d4bSgd78059 			}
249429e83d4bSgd78059 			if (status & TXSTAT_EXCOLL) {
249529e83d4bSgd78059 				mxfep->mxfe_ex_collisions++;
249629e83d4bSgd78059 				mxfep->mxfe_collisions += 16;
249729e83d4bSgd78059 			}
249829e83d4bSgd78059 		}
249929e83d4bSgd78059 
250029e83d4bSgd78059 		if (status & TXSTAT_DEFER) {
250129e83d4bSgd78059 			mxfep->mxfe_defer_xmts++;
250229e83d4bSgd78059 		}
250329e83d4bSgd78059 
250429e83d4bSgd78059 		/* collision counting */
250529e83d4bSgd78059 		if (TXCOLLCNT(status) == 1) {
250629e83d4bSgd78059 			mxfep->mxfe_collisions++;
250729e83d4bSgd78059 			mxfep->mxfe_first_collisions++;
250829e83d4bSgd78059 		} else if (TXCOLLCNT(status)) {
250929e83d4bSgd78059 			mxfep->mxfe_collisions += TXCOLLCNT(status);
251029e83d4bSgd78059 			mxfep->mxfe_multi_collisions += TXCOLLCNT(status);
251129e83d4bSgd78059 		}
251229e83d4bSgd78059 	}
251329e83d4bSgd78059 
251429e83d4bSgd78059 	if (mxfep->mxfe_txavail >= MXFE_TXRESCHED) {
251529e83d4bSgd78059 		if (mxfep->mxfe_wantw) {
251629e83d4bSgd78059 			/*
251729e83d4bSgd78059 			 * we were able to reclaim some packets, so
251829e83d4bSgd78059 			 * disable tx interrupts
251929e83d4bSgd78059 			 */
252029e83d4bSgd78059 			mxfep->mxfe_wantw = B_FALSE;
252129e83d4bSgd78059 			mxfe_enableinterrupts(mxfep);
252229e83d4bSgd78059 			mac_tx_update(mxfep->mxfe_mh);
252329e83d4bSgd78059 		}
252429e83d4bSgd78059 	}
252529e83d4bSgd78059 }
252629e83d4bSgd78059 
252777860f66SGarrett D'Amore boolean_t
mxfe_receive(mxfe_t * mxfep,mblk_t ** rxchain)252877860f66SGarrett D'Amore mxfe_receive(mxfe_t *mxfep, mblk_t **rxchain)
252929e83d4bSgd78059 {
253029e83d4bSgd78059 	unsigned		len;
253129e83d4bSgd78059 	mxfe_rxbuf_t		*rxb;
253229e83d4bSgd78059 	mxfe_desc_t		*rmd;
253329e83d4bSgd78059 	uint32_t		status;
253429e83d4bSgd78059 	mblk_t			*mpchain, **mpp, *mp;
253529e83d4bSgd78059 	int			head, cnt;
253677860f66SGarrett D'Amore 	boolean_t		error = B_FALSE;
253729e83d4bSgd78059 
253829e83d4bSgd78059 	mpchain = NULL;
253929e83d4bSgd78059 	mpp = &mpchain;
254029e83d4bSgd78059 	head = mxfep->mxfe_rxhead;
254129e83d4bSgd78059 
254229e83d4bSgd78059 	/* limit the number of packets we process to a ring size */
254329e83d4bSgd78059 	for (cnt = 0; cnt < MXFE_RXRING; cnt++) {
254429e83d4bSgd78059 
254529e83d4bSgd78059 		DBG(DRECV, "receive at index %d", head);
254629e83d4bSgd78059 
254729e83d4bSgd78059 		rmd = &mxfep->mxfe_rxdescp[head];
254829e83d4bSgd78059 		rxb = mxfep->mxfe_rxbufs[head];
254929e83d4bSgd78059 
255029e83d4bSgd78059 		SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORKERNEL);
255129e83d4bSgd78059 		status = GETRXDESC(mxfep, rmd->desc_status);
255229e83d4bSgd78059 		if (status & RXSTAT_OWN) {
255329e83d4bSgd78059 			/* chip is still chewing on it */
255429e83d4bSgd78059 			break;
255529e83d4bSgd78059 		}
255629e83d4bSgd78059 
255729e83d4bSgd78059 		/* discard the ethernet frame checksum */
255829e83d4bSgd78059 		len = RXLENGTH(status) - ETHERFCSL;
255929e83d4bSgd78059 
256029e83d4bSgd78059 		DBG(DRECV, "recv length %d, status %x", len, status);
256129e83d4bSgd78059 
256229e83d4bSgd78059 		if ((status & (RXSTAT_ERRS | RXSTAT_FIRST | RXSTAT_LAST)) !=
256329e83d4bSgd78059 		    (RXSTAT_FIRST | RXSTAT_LAST)) {
256429e83d4bSgd78059 
256529e83d4bSgd78059 			mxfep->mxfe_errrcv++;
256629e83d4bSgd78059 
256729e83d4bSgd78059 			/*
256829e83d4bSgd78059 			 * Abnormal status bits detected, analyze further.
256929e83d4bSgd78059 			 */
257029e83d4bSgd78059 			if ((status & (RXSTAT_LAST|RXSTAT_FIRST)) !=
257129e83d4bSgd78059 			    (RXSTAT_LAST|RXSTAT_FIRST)) {
257277860f66SGarrett D'Amore 				/* someone trying to send jumbo frames? */
257329e83d4bSgd78059 				DBG(DRECV, "rx packet overspill");
257429e83d4bSgd78059 				if (status & RXSTAT_FIRST) {
257529e83d4bSgd78059 					mxfep->mxfe_toolong_errors++;
257629e83d4bSgd78059 				}
257729e83d4bSgd78059 			} else if (status & RXSTAT_DESCERR) {
257877860f66SGarrett D'Amore 				/* this should never occur! */
257929e83d4bSgd78059 				mxfep->mxfe_macrcv_errors++;
258077860f66SGarrett D'Amore 				error = B_TRUE;
258129e83d4bSgd78059 
258229e83d4bSgd78059 			} else if (status & RXSTAT_RUNT) {
258329e83d4bSgd78059 				mxfep->mxfe_runt++;
258429e83d4bSgd78059 
258529e83d4bSgd78059 			} else if (status & RXSTAT_COLLSEEN) {
258629e83d4bSgd78059 				/* this should really be rx_late_collisions */
258729e83d4bSgd78059 				mxfep->mxfe_macrcv_errors++;
258829e83d4bSgd78059 
258929e83d4bSgd78059 			} else if (status & RXSTAT_DRIBBLE) {
259029e83d4bSgd78059 				mxfep->mxfe_align_errors++;
259129e83d4bSgd78059 
259229e83d4bSgd78059 			} else if (status & RXSTAT_CRCERR) {
259329e83d4bSgd78059 				mxfep->mxfe_fcs_errors++;
259429e83d4bSgd78059 
259529e83d4bSgd78059 			} else if (status & RXSTAT_OFLOW) {
259677860f66SGarrett D'Amore 				/* this is a MAC FIFO error, need to reset */
259729e83d4bSgd78059 				mxfep->mxfe_overflow++;
259877860f66SGarrett D'Amore 				error = B_TRUE;
259929e83d4bSgd78059 			}
260029e83d4bSgd78059 		}
260129e83d4bSgd78059 
260229e83d4bSgd78059 		else if (len > ETHERVLANMTU) {
260329e83d4bSgd78059 			mxfep->mxfe_errrcv++;
260429e83d4bSgd78059 			mxfep->mxfe_toolong_errors++;
260529e83d4bSgd78059 		}
260629e83d4bSgd78059 
260729e83d4bSgd78059 		/*
260829e83d4bSgd78059 		 * At this point, the chip thinks the packet is OK.
260929e83d4bSgd78059 		 */
261029e83d4bSgd78059 		else {
261129e83d4bSgd78059 			mp = allocb(len + MXFE_HEADROOM, 0);
261229e83d4bSgd78059 			if (mp == NULL) {
261329e83d4bSgd78059 				mxfep->mxfe_errrcv++;
261429e83d4bSgd78059 				mxfep->mxfe_norcvbuf++;
261529e83d4bSgd78059 				goto skip;
261629e83d4bSgd78059 			}
261729e83d4bSgd78059 
261829e83d4bSgd78059 			/* sync the buffer before we look at it */
261929e83d4bSgd78059 			SYNCRXBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
262029e83d4bSgd78059 			mp->b_rptr += MXFE_HEADROOM;
262129e83d4bSgd78059 			mp->b_wptr = mp->b_rptr + len;
262229e83d4bSgd78059 			bcopy((char *)rxb->rxb_buf, mp->b_rptr, len);
262329e83d4bSgd78059 
262429e83d4bSgd78059 			mxfep->mxfe_ipackets++;
262529e83d4bSgd78059 			mxfep->mxfe_rbytes += len;
262629e83d4bSgd78059 			if (status & RXSTAT_GROUP) {
262729e83d4bSgd78059 				if (bcmp(mp->b_rptr, mxfe_broadcast,
262829e83d4bSgd78059 				    ETHERADDRL) == 0)
262929e83d4bSgd78059 					mxfep->mxfe_brdcstrcv++;
263029e83d4bSgd78059 				else
263129e83d4bSgd78059 					mxfep->mxfe_multircv++;
263229e83d4bSgd78059 			}
263329e83d4bSgd78059 			*mpp = mp;
263429e83d4bSgd78059 			mpp = &mp->b_next;
263529e83d4bSgd78059 		}
263629e83d4bSgd78059 
263729e83d4bSgd78059 skip:
263829e83d4bSgd78059 		/* return ring entry to the hardware */
263929e83d4bSgd78059 		PUTRXDESC(mxfep, rmd->desc_status, RXSTAT_OWN);
264029e83d4bSgd78059 		SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORDEV);
264129e83d4bSgd78059 
264229e83d4bSgd78059 		/* advance to next RMD */
264329e83d4bSgd78059 		head = (head + 1) % MXFE_RXRING;
264429e83d4bSgd78059 	}
264529e83d4bSgd78059 
264629e83d4bSgd78059 	mxfep->mxfe_rxhead = head;
264729e83d4bSgd78059 
264877860f66SGarrett D'Amore 	*rxchain = mpchain;
264977860f66SGarrett D'Amore 	return (error);
265029e83d4bSgd78059 }
265129e83d4bSgd78059 
265229e83d4bSgd78059 int
mxfe_m_stat(void * arg,uint_t stat,uint64_t * val)265329e83d4bSgd78059 mxfe_m_stat(void *arg, uint_t stat, uint64_t *val)
265429e83d4bSgd78059 {
265529e83d4bSgd78059 	mxfe_t	*mxfep = arg;
265629e83d4bSgd78059 
265729e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
265829e83d4bSgd78059 	if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == MXFE_RUNNING)
265929e83d4bSgd78059 		mxfe_reclaim(mxfep);
266029e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
266129e83d4bSgd78059 
266229e83d4bSgd78059 	switch (stat) {
266329e83d4bSgd78059 	case MAC_STAT_IFSPEED:
266429e83d4bSgd78059 		*val = mxfep->mxfe_ifspeed;
266529e83d4bSgd78059 		break;
266629e83d4bSgd78059 
266729e83d4bSgd78059 	case MAC_STAT_MULTIRCV:
266829e83d4bSgd78059 		*val = mxfep->mxfe_multircv;
266929e83d4bSgd78059 		break;
267029e83d4bSgd78059 
267129e83d4bSgd78059 	case MAC_STAT_BRDCSTRCV:
267229e83d4bSgd78059 		*val = mxfep->mxfe_brdcstrcv;
267329e83d4bSgd78059 		break;
267429e83d4bSgd78059 
267529e83d4bSgd78059 	case MAC_STAT_MULTIXMT:
267629e83d4bSgd78059 		*val = mxfep->mxfe_multixmt;
267729e83d4bSgd78059 		break;
267829e83d4bSgd78059 
267929e83d4bSgd78059 	case MAC_STAT_BRDCSTXMT:
268029e83d4bSgd78059 		*val = mxfep->mxfe_brdcstxmt;
268129e83d4bSgd78059 		break;
268229e83d4bSgd78059 
268329e83d4bSgd78059 	case MAC_STAT_IPACKETS:
268429e83d4bSgd78059 		*val = mxfep->mxfe_ipackets;
268529e83d4bSgd78059 		break;
268629e83d4bSgd78059 
268729e83d4bSgd78059 	case MAC_STAT_RBYTES:
268829e83d4bSgd78059 		*val = mxfep->mxfe_rbytes;
268929e83d4bSgd78059 		break;
269029e83d4bSgd78059 
269129e83d4bSgd78059 	case MAC_STAT_OPACKETS:
269229e83d4bSgd78059 		*val = mxfep->mxfe_opackets;
269329e83d4bSgd78059 		break;
269429e83d4bSgd78059 
269529e83d4bSgd78059 	case MAC_STAT_OBYTES:
269629e83d4bSgd78059 		*val = mxfep->mxfe_obytes;
269729e83d4bSgd78059 		break;
269829e83d4bSgd78059 
269929e83d4bSgd78059 	case MAC_STAT_NORCVBUF:
270029e83d4bSgd78059 		*val = mxfep->mxfe_norcvbuf;
270129e83d4bSgd78059 		break;
270229e83d4bSgd78059 
270329e83d4bSgd78059 	case MAC_STAT_NOXMTBUF:
270429e83d4bSgd78059 		*val = mxfep->mxfe_noxmtbuf;
270529e83d4bSgd78059 		break;
270629e83d4bSgd78059 
270729e83d4bSgd78059 	case MAC_STAT_COLLISIONS:
270829e83d4bSgd78059 		*val = mxfep->mxfe_collisions;
270929e83d4bSgd78059 		break;
271029e83d4bSgd78059 
271129e83d4bSgd78059 	case MAC_STAT_IERRORS:
271229e83d4bSgd78059 		*val = mxfep->mxfe_errrcv;
271329e83d4bSgd78059 		break;
271429e83d4bSgd78059 
271529e83d4bSgd78059 	case MAC_STAT_OERRORS:
271629e83d4bSgd78059 		*val = mxfep->mxfe_errxmt;
271729e83d4bSgd78059 		break;
271829e83d4bSgd78059 
271929e83d4bSgd78059 	case ETHER_STAT_LINK_DUPLEX:
272029e83d4bSgd78059 		*val = mxfep->mxfe_duplex;
272129e83d4bSgd78059 		break;
272229e83d4bSgd78059 
272329e83d4bSgd78059 	case ETHER_STAT_ALIGN_ERRORS:
272429e83d4bSgd78059 		*val = mxfep->mxfe_align_errors;
272529e83d4bSgd78059 		break;
272629e83d4bSgd78059 
272729e83d4bSgd78059 	case ETHER_STAT_FCS_ERRORS:
272829e83d4bSgd78059 		*val = mxfep->mxfe_fcs_errors;
272929e83d4bSgd78059 		break;
273029e83d4bSgd78059 
273129e83d4bSgd78059 	case ETHER_STAT_SQE_ERRORS:
273229e83d4bSgd78059 		*val = mxfep->mxfe_sqe_errors;
273329e83d4bSgd78059 		break;
273429e83d4bSgd78059 
273529e83d4bSgd78059 	case ETHER_STAT_DEFER_XMTS:
273629e83d4bSgd78059 		*val = mxfep->mxfe_defer_xmts;
273729e83d4bSgd78059 		break;
273829e83d4bSgd78059 
273929e83d4bSgd78059 	case ETHER_STAT_FIRST_COLLISIONS:
274029e83d4bSgd78059 		*val  = mxfep->mxfe_first_collisions;
274129e83d4bSgd78059 		break;
274229e83d4bSgd78059 
274329e83d4bSgd78059 	case ETHER_STAT_MULTI_COLLISIONS:
274429e83d4bSgd78059 		*val = mxfep->mxfe_multi_collisions;
274529e83d4bSgd78059 		break;
274629e83d4bSgd78059 
274729e83d4bSgd78059 	case ETHER_STAT_TX_LATE_COLLISIONS:
274829e83d4bSgd78059 		*val = mxfep->mxfe_tx_late_collisions;
274929e83d4bSgd78059 		break;
275029e83d4bSgd78059 
275129e83d4bSgd78059 	case ETHER_STAT_EX_COLLISIONS:
275229e83d4bSgd78059 		*val = mxfep->mxfe_ex_collisions;
275329e83d4bSgd78059 		break;
275429e83d4bSgd78059 
275529e83d4bSgd78059 	case ETHER_STAT_MACXMT_ERRORS:
275629e83d4bSgd78059 		*val = mxfep->mxfe_macxmt_errors;
275729e83d4bSgd78059 		break;
275829e83d4bSgd78059 
275929e83d4bSgd78059 	case ETHER_STAT_CARRIER_ERRORS:
276029e83d4bSgd78059 		*val = mxfep->mxfe_carrier_errors;
276129e83d4bSgd78059 		break;
276229e83d4bSgd78059 
276329e83d4bSgd78059 	case ETHER_STAT_TOOLONG_ERRORS:
276429e83d4bSgd78059 		*val = mxfep->mxfe_toolong_errors;
276529e83d4bSgd78059 		break;
276629e83d4bSgd78059 
276729e83d4bSgd78059 	case ETHER_STAT_MACRCV_ERRORS:
276829e83d4bSgd78059 		*val = mxfep->mxfe_macrcv_errors;
276929e83d4bSgd78059 		break;
277029e83d4bSgd78059 
277129e83d4bSgd78059 	case MAC_STAT_OVERFLOWS:
277229e83d4bSgd78059 		*val = mxfep->mxfe_overflow;
277329e83d4bSgd78059 		break;
277429e83d4bSgd78059 
277529e83d4bSgd78059 	case MAC_STAT_UNDERFLOWS:
277629e83d4bSgd78059 		*val = mxfep->mxfe_underflow;
277729e83d4bSgd78059 		break;
277829e83d4bSgd78059 
277929e83d4bSgd78059 	case ETHER_STAT_TOOSHORT_ERRORS:
278029e83d4bSgd78059 		*val = mxfep->mxfe_runt;
278129e83d4bSgd78059 		break;
278229e83d4bSgd78059 
278329e83d4bSgd78059 	case ETHER_STAT_JABBER_ERRORS:
278429e83d4bSgd78059 		*val = mxfep->mxfe_jabber;
278529e83d4bSgd78059 		break;
278629e83d4bSgd78059 
278729e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_100T4:
278829e83d4bSgd78059 		*val = mxfep->mxfe_adv_100T4;
278929e83d4bSgd78059 		break;
279029e83d4bSgd78059 
279129e83d4bSgd78059 	case ETHER_STAT_LP_CAP_100T4:
279229e83d4bSgd78059 		*val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_T4) ? 1 : 0;
279329e83d4bSgd78059 		break;
279429e83d4bSgd78059 
279596fb08b9Sgd78059 	case ETHER_STAT_CAP_100T4:
279696fb08b9Sgd78059 		*val = mxfep->mxfe_cap_100T4;
279796fb08b9Sgd78059 		break;
279896fb08b9Sgd78059 
279929e83d4bSgd78059 	case ETHER_STAT_CAP_100FDX:
280096fb08b9Sgd78059 		*val = mxfep->mxfe_cap_100fdx;
280129e83d4bSgd78059 		break;
280229e83d4bSgd78059 
280329e83d4bSgd78059 	case ETHER_STAT_CAP_100HDX:
280496fb08b9Sgd78059 		*val = mxfep->mxfe_cap_100hdx;
280529e83d4bSgd78059 		break;
280629e83d4bSgd78059 
280729e83d4bSgd78059 	case ETHER_STAT_CAP_10FDX:
280896fb08b9Sgd78059 		*val = mxfep->mxfe_cap_10fdx;
280929e83d4bSgd78059 		break;
281029e83d4bSgd78059 
281129e83d4bSgd78059 	case ETHER_STAT_CAP_10HDX:
281296fb08b9Sgd78059 		*val = mxfep->mxfe_cap_10hdx;
281329e83d4bSgd78059 		break;
281429e83d4bSgd78059 
281529e83d4bSgd78059 	case ETHER_STAT_CAP_AUTONEG:
281696fb08b9Sgd78059 		*val = mxfep->mxfe_cap_aneg;
281729e83d4bSgd78059 		break;
281829e83d4bSgd78059 
281929e83d4bSgd78059 	case ETHER_STAT_LINK_AUTONEG:
282029e83d4bSgd78059 		*val = ((mxfep->mxfe_adv_aneg != 0) &&
282129e83d4bSgd78059 		    ((mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) != 0));
282229e83d4bSgd78059 		break;
282329e83d4bSgd78059 
282429e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_100FDX:
282529e83d4bSgd78059 		*val = mxfep->mxfe_adv_100fdx;
282629e83d4bSgd78059 		break;
282729e83d4bSgd78059 
282829e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_100HDX:
282929e83d4bSgd78059 		*val = mxfep->mxfe_adv_100hdx;
283029e83d4bSgd78059 		break;
283129e83d4bSgd78059 
283229e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_10FDX:
283329e83d4bSgd78059 		*val = mxfep->mxfe_adv_10fdx;
283429e83d4bSgd78059 		break;
283529e83d4bSgd78059 
283629e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_10HDX:
283729e83d4bSgd78059 		*val = mxfep->mxfe_adv_10hdx;
283829e83d4bSgd78059 		break;
283929e83d4bSgd78059 
284029e83d4bSgd78059 	case ETHER_STAT_ADV_CAP_AUTONEG:
284129e83d4bSgd78059 		*val = mxfep->mxfe_adv_aneg;
284229e83d4bSgd78059 		break;
284329e83d4bSgd78059 
284429e83d4bSgd78059 	case ETHER_STAT_LP_CAP_100FDX:
284529e83d4bSgd78059 		*val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX_FD) ? 1 : 0;
284629e83d4bSgd78059 		break;
284729e83d4bSgd78059 
284829e83d4bSgd78059 	case ETHER_STAT_LP_CAP_100HDX:
284929e83d4bSgd78059 		*val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX) ? 1 : 0;
285029e83d4bSgd78059 		break;
285129e83d4bSgd78059 
285229e83d4bSgd78059 	case ETHER_STAT_LP_CAP_10FDX:
285329e83d4bSgd78059 		*val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T_FD) ? 1 : 0;
285429e83d4bSgd78059 		break;
285529e83d4bSgd78059 
285629e83d4bSgd78059 	case ETHER_STAT_LP_CAP_10HDX:
285729e83d4bSgd78059 		*val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T) ? 1 : 0;
285829e83d4bSgd78059 		break;
285929e83d4bSgd78059 
286029e83d4bSgd78059 	case ETHER_STAT_LP_CAP_AUTONEG:
286129e83d4bSgd78059 		*val = (mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) ? 1 : 0;
286229e83d4bSgd78059 		break;
286329e83d4bSgd78059 
286429e83d4bSgd78059 	case ETHER_STAT_XCVR_ADDR:
286529e83d4bSgd78059 		*val = mxfep->mxfe_phyaddr;
286629e83d4bSgd78059 		break;
286729e83d4bSgd78059 
286829e83d4bSgd78059 	case ETHER_STAT_XCVR_ID:
286929e83d4bSgd78059 		*val = mxfep->mxfe_phyid;
287029e83d4bSgd78059 		break;
287129e83d4bSgd78059 
287229e83d4bSgd78059 	case ETHER_STAT_XCVR_INUSE:
287329e83d4bSgd78059 		*val = mxfep->mxfe_phyinuse;
287429e83d4bSgd78059 		break;
287529e83d4bSgd78059 
287629e83d4bSgd78059 	default:
287729e83d4bSgd78059 		return (ENOTSUP);
287829e83d4bSgd78059 	}
287929e83d4bSgd78059 	return (0);
288029e83d4bSgd78059 }
288129e83d4bSgd78059 
288296fb08b9Sgd78059 /*ARGSUSED*/
288396fb08b9Sgd78059 int
mxfe_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)2884*0dc2366fSVenugopal Iyer mxfe_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
2885*0dc2366fSVenugopal Iyer     void *val)
288629e83d4bSgd78059 {
288796fb08b9Sgd78059 	mxfe_t		*mxfep = arg;
288896fb08b9Sgd78059 	int		err = 0;
288929e83d4bSgd78059 
289096fb08b9Sgd78059 	switch (num) {
28913fd94f8cSam223141 	case MAC_PROP_DUPLEX:
2892*0dc2366fSVenugopal Iyer 		ASSERT(sz >= sizeof (link_duplex_t));
2893*0dc2366fSVenugopal Iyer 		bcopy(&mxfep->mxfe_duplex, val, sizeof (link_duplex_t));
289429e83d4bSgd78059 		break;
289529e83d4bSgd78059 
28963fd94f8cSam223141 	case MAC_PROP_SPEED:
2897*0dc2366fSVenugopal Iyer 		ASSERT(sz >= sizeof (uint64_t));
289896fb08b9Sgd78059 		bcopy(&mxfep->mxfe_ifspeed, val, sizeof (uint64_t));
289929e83d4bSgd78059 		break;
290096fb08b9Sgd78059 
29013fd94f8cSam223141 	case MAC_PROP_AUTONEG:
2902*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_aneg;
290396fb08b9Sgd78059 		break;
290496fb08b9Sgd78059 
29053fd94f8cSam223141 	case MAC_PROP_ADV_100FDX_CAP:
29063fd94f8cSam223141 	case MAC_PROP_EN_100FDX_CAP:
2907*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_100fdx;
290896fb08b9Sgd78059 		break;
290996fb08b9Sgd78059 
29103fd94f8cSam223141 	case MAC_PROP_ADV_100HDX_CAP:
29113fd94f8cSam223141 	case MAC_PROP_EN_100HDX_CAP:
2912*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_100hdx;
291396fb08b9Sgd78059 		break;
291496fb08b9Sgd78059 
29153fd94f8cSam223141 	case MAC_PROP_ADV_10FDX_CAP:
29163fd94f8cSam223141 	case MAC_PROP_EN_10FDX_CAP:
2917*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_10fdx;
291896fb08b9Sgd78059 		break;
291996fb08b9Sgd78059 
29203fd94f8cSam223141 	case MAC_PROP_ADV_10HDX_CAP:
29213fd94f8cSam223141 	case MAC_PROP_EN_10HDX_CAP:
2922*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_10hdx;
292396fb08b9Sgd78059 		break;
292496fb08b9Sgd78059 
29253fd94f8cSam223141 	case MAC_PROP_ADV_100T4_CAP:
29263fd94f8cSam223141 	case MAC_PROP_EN_100T4_CAP:
2927*0dc2366fSVenugopal Iyer 		*(uint8_t *)val = mxfep->mxfe_adv_100T4;
292896fb08b9Sgd78059 		break;
292996fb08b9Sgd78059 
293096fb08b9Sgd78059 	default:
293196fb08b9Sgd78059 		err = ENOTSUP;
293229e83d4bSgd78059 	}
293329e83d4bSgd78059 
293496fb08b9Sgd78059 	return (err);
293529e83d4bSgd78059 }
293629e83d4bSgd78059 
293729e83d4bSgd78059 /*ARGSUSED*/
293829e83d4bSgd78059 int
mxfe_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)293996fb08b9Sgd78059 mxfe_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
294096fb08b9Sgd78059     const void *val)
294129e83d4bSgd78059 {
294296fb08b9Sgd78059 	mxfe_t		*mxfep = arg;
294396fb08b9Sgd78059 	uint8_t		*advp;
294496fb08b9Sgd78059 	uint8_t		*capp;
294529e83d4bSgd78059 
294696fb08b9Sgd78059 	switch (num) {
29473fd94f8cSam223141 	case MAC_PROP_EN_100FDX_CAP:
294896fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_100fdx;
294996fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_100fdx;
295096fb08b9Sgd78059 		break;
295129e83d4bSgd78059 
29523fd94f8cSam223141 	case MAC_PROP_EN_100HDX_CAP:
295396fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_100hdx;
295496fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_100hdx;
295596fb08b9Sgd78059 		break;
295696fb08b9Sgd78059 
29573fd94f8cSam223141 	case MAC_PROP_EN_10FDX_CAP:
295896fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_10fdx;
295996fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_10fdx;
296096fb08b9Sgd78059 		break;
296196fb08b9Sgd78059 
29623fd94f8cSam223141 	case MAC_PROP_EN_10HDX_CAP:
296396fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_10hdx;
296496fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_10hdx;
296596fb08b9Sgd78059 		break;
296696fb08b9Sgd78059 
29673fd94f8cSam223141 	case MAC_PROP_EN_100T4_CAP:
296896fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_100T4;
296996fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_100T4;
297096fb08b9Sgd78059 		break;
297196fb08b9Sgd78059 
29723fd94f8cSam223141 	case MAC_PROP_AUTONEG:
297396fb08b9Sgd78059 		advp = &mxfep->mxfe_adv_aneg;
297496fb08b9Sgd78059 		capp = &mxfep->mxfe_cap_aneg;
297596fb08b9Sgd78059 		break;
297696fb08b9Sgd78059 
297796fb08b9Sgd78059 	default:
297896fb08b9Sgd78059 		return (ENOTSUP);
297929e83d4bSgd78059 	}
298029e83d4bSgd78059 
298196fb08b9Sgd78059 	if (*capp == 0)		/* ensure phy can support value */
298296fb08b9Sgd78059 		return (ENOTSUP);
298329e83d4bSgd78059 
298429e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_intrlock);
298529e83d4bSgd78059 	mutex_enter(&mxfep->mxfe_xmtlock);
298629e83d4bSgd78059 
298796fb08b9Sgd78059 	if (*advp != *(const uint8_t *)val) {
298896fb08b9Sgd78059 		*advp = *(const uint8_t *)val;
298996fb08b9Sgd78059 
299029e83d4bSgd78059 		if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) ==
299129e83d4bSgd78059 		    MXFE_RUNNING) {
299229e83d4bSgd78059 			/*
299329e83d4bSgd78059 			 * This re-initializes the phy, but it also
299429e83d4bSgd78059 			 * restarts transmit and receive rings.
299529e83d4bSgd78059 			 * Needless to say, changing the link
299629e83d4bSgd78059 			 * parameters is destructive to traffic in
299729e83d4bSgd78059 			 * progress.
299829e83d4bSgd78059 			 */
299929e83d4bSgd78059 			mxfe_resetall(mxfep);
300029e83d4bSgd78059 		}
300129e83d4bSgd78059 	}
300229e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_xmtlock);
300329e83d4bSgd78059 	mutex_exit(&mxfep->mxfe_intrlock);
300429e83d4bSgd78059 
300529e83d4bSgd78059 	return (0);
300629e83d4bSgd78059 }
300729e83d4bSgd78059 
3008*0dc2366fSVenugopal Iyer static void
mxfe_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t mph)3009*0dc2366fSVenugopal Iyer mxfe_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
3010*0dc2366fSVenugopal Iyer     mac_prop_info_handle_t mph)
3011*0dc2366fSVenugopal Iyer {
3012*0dc2366fSVenugopal Iyer 	mxfe_t		*mxfep = arg;
3013*0dc2366fSVenugopal Iyer 
3014*0dc2366fSVenugopal Iyer         _NOTE(ARGUNUSED(name));
3015*0dc2366fSVenugopal Iyer 
3016*0dc2366fSVenugopal Iyer 	switch (num) {
3017*0dc2366fSVenugopal Iyer 	case MAC_PROP_DUPLEX:
3018*0dc2366fSVenugopal Iyer 	case MAC_PROP_SPEED:
3019*0dc2366fSVenugopal Iyer 	case MAC_PROP_ADV_100FDX_CAP:
3020*0dc2366fSVenugopal Iyer 	case MAC_PROP_ADV_100HDX_CAP:
3021*0dc2366fSVenugopal Iyer 	case MAC_PROP_ADV_10FDX_CAP:
3022*0dc2366fSVenugopal Iyer 	case MAC_PROP_ADV_10HDX_CAP:
3023*0dc2366fSVenugopal Iyer 	case MAC_PROP_ADV_100T4_CAP:
3024*0dc2366fSVenugopal Iyer 		mac_prop_info_set_perm(mph, MAC_PROP_PERM_READ);
3025*0dc2366fSVenugopal Iyer 		break;
3026*0dc2366fSVenugopal Iyer 
3027*0dc2366fSVenugopal Iyer 	case MAC_PROP_AUTONEG:
3028*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_aneg);
3029*0dc2366fSVenugopal Iyer 		break;
3030*0dc2366fSVenugopal Iyer 
3031*0dc2366fSVenugopal Iyer 	case MAC_PROP_EN_100FDX_CAP:
3032*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100fdx);
3033*0dc2366fSVenugopal Iyer 		break;
3034*0dc2366fSVenugopal Iyer 
3035*0dc2366fSVenugopal Iyer 	case MAC_PROP_EN_100HDX_CAP:
3036*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100hdx);
3037*0dc2366fSVenugopal Iyer 		break;
3038*0dc2366fSVenugopal Iyer 
3039*0dc2366fSVenugopal Iyer 	case MAC_PROP_EN_10FDX_CAP:
3040*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_10fdx);
3041*0dc2366fSVenugopal Iyer 		break;
3042*0dc2366fSVenugopal Iyer 
3043*0dc2366fSVenugopal Iyer 	case MAC_PROP_EN_10HDX_CAP:
3044*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_10hdx);
3045*0dc2366fSVenugopal Iyer 		break;
3046*0dc2366fSVenugopal Iyer 
3047*0dc2366fSVenugopal Iyer 	case MAC_PROP_EN_100T4_CAP:
3048*0dc2366fSVenugopal Iyer 		mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100T4);
3049*0dc2366fSVenugopal Iyer 		break;
3050*0dc2366fSVenugopal Iyer 	}
3051*0dc2366fSVenugopal Iyer }
3052*0dc2366fSVenugopal Iyer 
305329e83d4bSgd78059 /*
305429e83d4bSgd78059  * Debugging and error reporting.
305529e83d4bSgd78059  */
305629e83d4bSgd78059 void
mxfe_error(dev_info_t * dip,char * fmt,...)305729e83d4bSgd78059 mxfe_error(dev_info_t *dip, char *fmt, ...)
305829e83d4bSgd78059 {
305929e83d4bSgd78059 	va_list	ap;
306029e83d4bSgd78059 	char	buf[256];
306129e83d4bSgd78059 
306229e83d4bSgd78059 	va_start(ap, fmt);
306329e83d4bSgd78059 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
306429e83d4bSgd78059 	va_end(ap);
306529e83d4bSgd78059 
306629e83d4bSgd78059 	if (dip) {
306729e83d4bSgd78059 		cmn_err(CE_WARN, "%s%d: %s",
306829e83d4bSgd78059 		    ddi_driver_name(dip), ddi_get_instance(dip), buf);
306929e83d4bSgd78059 	} else {
307029e83d4bSgd78059 		cmn_err(CE_WARN, "mxfe: %s", buf);
307129e83d4bSgd78059 	}
307229e83d4bSgd78059 }
307329e83d4bSgd78059 
307429e83d4bSgd78059 #ifdef DEBUG
307529e83d4bSgd78059 
307629e83d4bSgd78059 void
mxfe_dprintf(mxfe_t * mxfep,const char * func,int level,char * fmt,...)307729e83d4bSgd78059 mxfe_dprintf(mxfe_t *mxfep, const char *func, int level, char *fmt, ...)
307829e83d4bSgd78059 {
307929e83d4bSgd78059 	va_list	ap;
308029e83d4bSgd78059 
308129e83d4bSgd78059 	va_start(ap, fmt);
308229e83d4bSgd78059 	if (mxfe_debug & level) {
308329e83d4bSgd78059 		char	tag[64];
308429e83d4bSgd78059 		char	buf[256];
308529e83d4bSgd78059 
308629e83d4bSgd78059 		if (mxfep && mxfep->mxfe_dip) {
308729e83d4bSgd78059 			(void) snprintf(tag, sizeof (tag),
308829e83d4bSgd78059 			    "%s%d", ddi_driver_name(mxfep->mxfe_dip),
308929e83d4bSgd78059 			    ddi_get_instance(mxfep->mxfe_dip));
309029e83d4bSgd78059 		} else {
309129e83d4bSgd78059 			(void) snprintf(tag, sizeof (tag), "mxfe");
309229e83d4bSgd78059 		}
309329e83d4bSgd78059 
309429e83d4bSgd78059 		(void) snprintf(buf, sizeof (buf), "%s: %s: %s\n", tag,
309529e83d4bSgd78059 		    func, fmt);
309629e83d4bSgd78059 
309729e83d4bSgd78059 		vcmn_err(CE_CONT, buf, ap);
309829e83d4bSgd78059 	}
309929e83d4bSgd78059 	va_end(ap);
310029e83d4bSgd78059 }
310129e83d4bSgd78059 
310229e83d4bSgd78059 #endif
3103