xref: /onnv-gate/usr/src/uts/common/io/elxl/elxl.c (revision 11878:ac93462db6d7)
111717Sgdamore@opensolaris.org /*
211717Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
311717Sgdamore@opensolaris.org  * Use is subject to license terms.
411717Sgdamore@opensolaris.org  */
511717Sgdamore@opensolaris.org 
611717Sgdamore@opensolaris.org /*
711717Sgdamore@opensolaris.org  * Copyright (c) 1998 The NetBSD Foundation, Inc.
811717Sgdamore@opensolaris.org  * All rights reserved.
911717Sgdamore@opensolaris.org  *
1011717Sgdamore@opensolaris.org  * This code is derived from software contributed to The NetBSD Foundation
1111717Sgdamore@opensolaris.org  * by Frank van der Linden.
1211717Sgdamore@opensolaris.org  *
1311717Sgdamore@opensolaris.org  * Redistribution and use in source and binary forms, with or without
1411717Sgdamore@opensolaris.org  * modification, are permitted provided that the following conditions
1511717Sgdamore@opensolaris.org  * are met:
1611717Sgdamore@opensolaris.org  * 1. Redistributions of source code must retain the above copyright
1711717Sgdamore@opensolaris.org  *    notice, this list of conditions and the following disclaimer.
1811717Sgdamore@opensolaris.org  * 2. Redistributions in binary form must reproduce the above copyright
1911717Sgdamore@opensolaris.org  *    notice, this list of conditions and the following disclaimer in the
2011717Sgdamore@opensolaris.org  *    documentation and/or other materials provided with the distribution.
2111717Sgdamore@opensolaris.org  *
2211717Sgdamore@opensolaris.org  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2311717Sgdamore@opensolaris.org  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2411717Sgdamore@opensolaris.org  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2511717Sgdamore@opensolaris.org  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2611717Sgdamore@opensolaris.org  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2711717Sgdamore@opensolaris.org  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2811717Sgdamore@opensolaris.org  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2911717Sgdamore@opensolaris.org  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3011717Sgdamore@opensolaris.org  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3111717Sgdamore@opensolaris.org  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3211717Sgdamore@opensolaris.org  * POSSIBILITY OF SUCH DAMAGE.
3311717Sgdamore@opensolaris.org  */
3411717Sgdamore@opensolaris.org 
3511717Sgdamore@opensolaris.org #include <sys/varargs.h>
3611717Sgdamore@opensolaris.org #include <sys/types.h>
3711717Sgdamore@opensolaris.org #include <sys/modctl.h>
3811717Sgdamore@opensolaris.org #include <sys/conf.h>
3911717Sgdamore@opensolaris.org #include <sys/devops.h>
4011717Sgdamore@opensolaris.org #include <sys/stream.h>
4111717Sgdamore@opensolaris.org #include <sys/strsun.h>
4211717Sgdamore@opensolaris.org #include <sys/cmn_err.h>
4311717Sgdamore@opensolaris.org #include <sys/ethernet.h>
4411717Sgdamore@opensolaris.org #include <sys/pci.h>
4511717Sgdamore@opensolaris.org #include <sys/kmem.h>
4611717Sgdamore@opensolaris.org #include <sys/time.h>
4711717Sgdamore@opensolaris.org #include <sys/mii.h>
4811717Sgdamore@opensolaris.org #include <sys/miiregs.h>
4911717Sgdamore@opensolaris.org #include <sys/mac_ether.h>
5011717Sgdamore@opensolaris.org #include <sys/mac_provider.h>
5111717Sgdamore@opensolaris.org #include <sys/strsubr.h>
5211717Sgdamore@opensolaris.org #include <sys/pattr.h>
5311717Sgdamore@opensolaris.org #include <sys/dlpi.h>
5411717Sgdamore@opensolaris.org #include <sys/ddi.h>
5511717Sgdamore@opensolaris.org #include <sys/sunddi.h>
5611717Sgdamore@opensolaris.org 
5711717Sgdamore@opensolaris.org #include <sys/vlan.h>
5811717Sgdamore@opensolaris.org 
5911717Sgdamore@opensolaris.org #include "elxl.h"
6011717Sgdamore@opensolaris.org 
6111717Sgdamore@opensolaris.org static boolean_t elxl_add_intr(elxl_t *);
6211717Sgdamore@opensolaris.org static void elxl_probe_media(elxl_t *);
6311717Sgdamore@opensolaris.org static void elxl_set_rxfilter(elxl_t *);
6411717Sgdamore@opensolaris.org static void elxl_set_media(elxl_t *);
6511717Sgdamore@opensolaris.org static uint16_t elxl_read_eeprom(elxl_t *, int);
6611717Sgdamore@opensolaris.org static void elxl_init(elxl_t *);
6711717Sgdamore@opensolaris.org static void elxl_stop(elxl_t *);
6811717Sgdamore@opensolaris.org static void elxl_reset(elxl_t *);
6911717Sgdamore@opensolaris.org static void elxl_getstats(elxl_t *);
7011717Sgdamore@opensolaris.org 
7111717Sgdamore@opensolaris.org static int elxl_eeprom_busy(elxl_t *);
7211717Sgdamore@opensolaris.org 
7311717Sgdamore@opensolaris.org static void elxl_setup_tx(elxl_t *);
7411717Sgdamore@opensolaris.org 
7511717Sgdamore@opensolaris.org static uint16_t elxl_mii_read(void *, uint8_t, uint8_t);
7611717Sgdamore@opensolaris.org static void elxl_mii_write(void *, uint8_t, uint8_t, uint16_t);
7711717Sgdamore@opensolaris.org static void elxl_mii_notify(void *, link_state_t);
7811717Sgdamore@opensolaris.org 
7911717Sgdamore@opensolaris.org static int elxl_m_stat(void *, uint_t, uint64_t *);
8011717Sgdamore@opensolaris.org static int elxl_m_start(void *);
8111717Sgdamore@opensolaris.org static void elxl_m_stop(void *);
8211717Sgdamore@opensolaris.org static mblk_t *elxl_m_tx(void *, mblk_t *);
8311717Sgdamore@opensolaris.org static int elxl_m_promisc(void *, boolean_t);
8411717Sgdamore@opensolaris.org static int elxl_m_multicst(void *, boolean_t, const uint8_t *);
8511717Sgdamore@opensolaris.org static int elxl_m_unicst(void *, const uint8_t *);
8611717Sgdamore@opensolaris.org static int elxl_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
87*11878SVenu.Iyer@Sun.COM     void *);
8811717Sgdamore@opensolaris.org static int elxl_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
8911717Sgdamore@opensolaris.org     const void *);
90*11878SVenu.Iyer@Sun.COM static void elxl_m_propinfo(void *, const char *, mac_prop_id_t,
91*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
9211717Sgdamore@opensolaris.org static boolean_t elxl_m_getcapab(void *, mac_capab_t cap, void *);
9311717Sgdamore@opensolaris.org static uint_t elxl_intr(caddr_t, caddr_t);
9411717Sgdamore@opensolaris.org static void elxl_error(elxl_t *, char *, ...);
9511717Sgdamore@opensolaris.org static void elxl_linkcheck(void *);
9611717Sgdamore@opensolaris.org static int elxl_attach(dev_info_t *);
9711717Sgdamore@opensolaris.org static void elxl_detach(elxl_t *);
9811717Sgdamore@opensolaris.org static void elxl_suspend(elxl_t *);
9911717Sgdamore@opensolaris.org static void elxl_resume(dev_info_t *);
10011717Sgdamore@opensolaris.org static int elxl_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
10111717Sgdamore@opensolaris.org static int elxl_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
10211717Sgdamore@opensolaris.org static int elxl_ddi_quiesce(dev_info_t *);
10311717Sgdamore@opensolaris.org 
10411717Sgdamore@opensolaris.org static ddi_device_acc_attr_t ex_dev_acc_attr = {
10511717Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
10611717Sgdamore@opensolaris.org 	DDI_STRUCTURE_LE_ACC,
10711717Sgdamore@opensolaris.org 	DDI_STRICTORDER_ACC
10811717Sgdamore@opensolaris.org };
10911717Sgdamore@opensolaris.org 
11011717Sgdamore@opensolaris.org static ddi_device_acc_attr_t ex_buf_acc_attr = {
11111717Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
11211717Sgdamore@opensolaris.org 	DDI_NEVERSWAP_ACC,
11311717Sgdamore@opensolaris.org 	DDI_STORECACHING_OK_ACC
11411717Sgdamore@opensolaris.org };
11511717Sgdamore@opensolaris.org 
11611717Sgdamore@opensolaris.org /*
11711717Sgdamore@opensolaris.org  * In theory buffers can have more flexible DMA attributes, but since
11811717Sgdamore@opensolaris.org  * we're just using a preallocated region with bcopy, there is little
11911717Sgdamore@opensolaris.org  * reason to allow for rougher alignment.  (Further, the 8-byte
12011717Sgdamore@opensolaris.org  * alignment can allow for more efficient bcopy and similar operations
12111717Sgdamore@opensolaris.org  * from the buffer.)
12211717Sgdamore@opensolaris.org  */
12311717Sgdamore@opensolaris.org static ddi_dma_attr_t ex_dma_attr = {
12411717Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* dma_attr_version */
12511717Sgdamore@opensolaris.org 	0,			/* dma_attr_addr_lo */
12611717Sgdamore@opensolaris.org 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
12711717Sgdamore@opensolaris.org 	0x00FFFFFFU,		/* dma_attr_count_max */
12811717Sgdamore@opensolaris.org 	8,			/* dma_attr_align */
12911717Sgdamore@opensolaris.org 	0x7F,			/* dma_attr_burstsizes */
13011717Sgdamore@opensolaris.org 	1,			/* dma_attr_minxfer */
13111717Sgdamore@opensolaris.org 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
13211717Sgdamore@opensolaris.org 	0xFFFFFFFFU,		/* dma_attr_seg */
13311717Sgdamore@opensolaris.org 	1,			/* dma_attr_sgllen */
13411717Sgdamore@opensolaris.org 	1,			/* dma_attr_granular */
13511717Sgdamore@opensolaris.org 	0			/* dma_attr_flags */
13611717Sgdamore@opensolaris.org };
13711717Sgdamore@opensolaris.org 
13811717Sgdamore@opensolaris.org static uint8_t ex_broadcast[6] = {
13911717Sgdamore@opensolaris.org 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
14011717Sgdamore@opensolaris.org };
14111717Sgdamore@opensolaris.org 
14211717Sgdamore@opensolaris.org /*
14311717Sgdamore@opensolaris.org  * Structure to map media-present bits in boards to ifmedia codes and
14411717Sgdamore@opensolaris.org  * printable media names.  Used for table-driven ifmedia initialization.
14511717Sgdamore@opensolaris.org  */
14611717Sgdamore@opensolaris.org typedef struct ex_media {
14711717Sgdamore@opensolaris.org 	int	exm_mpbit;		/* media present bit */
14811717Sgdamore@opensolaris.org 	int	exm_xcvr;		/* XCVR_SEL_* constant */
14911717Sgdamore@opensolaris.org } ex_media_t;
15011717Sgdamore@opensolaris.org 
15111717Sgdamore@opensolaris.org /*
15211717Sgdamore@opensolaris.org  * Media table for 3c90x chips.  Note that chips with MII have no
15311717Sgdamore@opensolaris.org  * `native' media.  This is sorted in "reverse preference".
15411717Sgdamore@opensolaris.org  */
15511717Sgdamore@opensolaris.org static ex_media_t ex_native_media[] = {
15611717Sgdamore@opensolaris.org 	{ MEDIAOPT_AUI,		XCVR_SEL_AUI },
15711717Sgdamore@opensolaris.org 	{ MEDIAOPT_BNC,		XCVR_SEL_BNC },
15811717Sgdamore@opensolaris.org 	{ MEDIAOPT_10T,		XCVR_SEL_10T },
15911717Sgdamore@opensolaris.org 	{ MEDIAOPT_100TX,	XCVR_SEL_AUTO },	/* only 90XB */
16011717Sgdamore@opensolaris.org 	{ MEDIAOPT_100FX,	XCVR_SEL_100FX },
16111717Sgdamore@opensolaris.org 	{ MEDIAOPT_MII,		XCVR_SEL_MII },
16211717Sgdamore@opensolaris.org 	{ MEDIAOPT_100T4,	XCVR_SEL_MII },
16311717Sgdamore@opensolaris.org 	{ 0,			0 },
16411717Sgdamore@opensolaris.org };
16511717Sgdamore@opensolaris.org 
16611717Sgdamore@opensolaris.org 
16711717Sgdamore@opensolaris.org /*
16811717Sgdamore@opensolaris.org  * NB: There are lots of other models that *could* be supported.
16911717Sgdamore@opensolaris.org  * Specifically there are cardbus and miniPCI variants that could be
17011717Sgdamore@opensolaris.org  * easily added here, but they require special hacks and I have no
17111717Sgdamore@opensolaris.org  * access to the hardware required to verify them.  Especially they
17211717Sgdamore@opensolaris.org  * seem to require some extra work in another register window, and I
17311717Sgdamore@opensolaris.org  * have no supporting documentation.
17411717Sgdamore@opensolaris.org  */
17511717Sgdamore@opensolaris.org static const struct ex_product {
17611717Sgdamore@opensolaris.org 	uint16_t	epp_prodid;	/* PCI product ID */
17711717Sgdamore@opensolaris.org 	const char	*epp_name;	/* device name */
17811717Sgdamore@opensolaris.org 	unsigned	epp_flags;	/* initial softc flags */
17911717Sgdamore@opensolaris.org } ex_products[] = {
18011717Sgdamore@opensolaris.org 	{ 0x4500, "3c450-TX",		0 },
18111717Sgdamore@opensolaris.org 	{ 0x7646, "3cSOHO100-TX",	0 },
18211717Sgdamore@opensolaris.org 	{ 0x9000, "3c900-TPO",		0 },
18311717Sgdamore@opensolaris.org 	{ 0x9001, "3c900-COMBO",	0 },
18411717Sgdamore@opensolaris.org 	{ 0x9004, "3c900B-TPO",		0 },
18511717Sgdamore@opensolaris.org 	{ 0x9005, "3c900B-COMBO",	0 },
18611717Sgdamore@opensolaris.org 	{ 0x9006, "3c900B-TPC",		0 },
18711717Sgdamore@opensolaris.org 	{ 0x900a, "3c900B-FL",		0 },
18811717Sgdamore@opensolaris.org 	{ 0x9050, "3c905-TX",		0 },
18911717Sgdamore@opensolaris.org 	{ 0x9051, "3c905-T4",		0 },
19011717Sgdamore@opensolaris.org 	{ 0x9055, "3c905B-TX",		0 },
19111717Sgdamore@opensolaris.org 	{ 0x9056, "3c905B-T4",		0 },
19211717Sgdamore@opensolaris.org 	{ 0x9058, "3c905B-COMBO",	0 },
19311717Sgdamore@opensolaris.org 	{ 0x905a, "3c905B-FX",		0 },
19411717Sgdamore@opensolaris.org 	{ 0x9200, "3c905C-TX",		0 },
19511717Sgdamore@opensolaris.org 	{ 0x9201, "3c920B-EMB",		0 },
19611717Sgdamore@opensolaris.org 	{ 0x9202, "3c920B-EMB-WNM",	0 },
19711717Sgdamore@opensolaris.org 	{ 0x9800, "3c980",		0 },
19811717Sgdamore@opensolaris.org 	{ 0x9805, "3c980C-TXM",		0 },
19911717Sgdamore@opensolaris.org 
20011717Sgdamore@opensolaris.org 	{ 0, NULL, 0 },
20111717Sgdamore@opensolaris.org };
20211717Sgdamore@opensolaris.org 
203*11878SVenu.Iyer@Sun.COM static char *ex_priv_prop[] = {
204*11878SVenu.Iyer@Sun.COM 	"_media",
205*11878SVenu.Iyer@Sun.COM 	"_available_media",
206*11878SVenu.Iyer@Sun.COM 	NULL
20711717Sgdamore@opensolaris.org };
20811717Sgdamore@opensolaris.org 
20911717Sgdamore@opensolaris.org static mii_ops_t ex_mii_ops = {
21011717Sgdamore@opensolaris.org 	MII_OPS_VERSION,
21111717Sgdamore@opensolaris.org 	elxl_mii_read,
21211717Sgdamore@opensolaris.org 	elxl_mii_write,
21311717Sgdamore@opensolaris.org 	elxl_mii_notify,
21411717Sgdamore@opensolaris.org };
21511717Sgdamore@opensolaris.org 
21611717Sgdamore@opensolaris.org static mac_callbacks_t elxl_m_callbacks = {
217*11878SVenu.Iyer@Sun.COM 	MC_GETCAPAB | MC_PROPERTIES,
21811717Sgdamore@opensolaris.org 	elxl_m_stat,
21911717Sgdamore@opensolaris.org 	elxl_m_start,
22011717Sgdamore@opensolaris.org 	elxl_m_stop,
22111717Sgdamore@opensolaris.org 	elxl_m_promisc,
22211717Sgdamore@opensolaris.org 	elxl_m_multicst,
22311717Sgdamore@opensolaris.org 	elxl_m_unicst,
22411717Sgdamore@opensolaris.org 	elxl_m_tx,
22511717Sgdamore@opensolaris.org 	NULL,
226*11878SVenu.Iyer@Sun.COM 	NULL,
22711717Sgdamore@opensolaris.org 	elxl_m_getcapab,
22811717Sgdamore@opensolaris.org 	NULL,
22911717Sgdamore@opensolaris.org 	NULL,
23011717Sgdamore@opensolaris.org 	elxl_m_setprop,
231*11878SVenu.Iyer@Sun.COM 	elxl_m_getprop,
232*11878SVenu.Iyer@Sun.COM 	elxl_m_propinfo
23311717Sgdamore@opensolaris.org };
23411717Sgdamore@opensolaris.org 
23511717Sgdamore@opensolaris.org /*
23611717Sgdamore@opensolaris.org  * Stream information
23711717Sgdamore@opensolaris.org  */
23811717Sgdamore@opensolaris.org DDI_DEFINE_STREAM_OPS(ex_devops, nulldev, nulldev,
23911717Sgdamore@opensolaris.org     elxl_ddi_attach, elxl_ddi_detach,
24011717Sgdamore@opensolaris.org     nodev, NULL, D_MP, NULL, elxl_ddi_quiesce);
24111717Sgdamore@opensolaris.org 
24211717Sgdamore@opensolaris.org /*
24311717Sgdamore@opensolaris.org  * Module linkage information.
24411717Sgdamore@opensolaris.org  */
24511717Sgdamore@opensolaris.org 
24611717Sgdamore@opensolaris.org static struct modldrv ex_modldrv = {
24711717Sgdamore@opensolaris.org 	&mod_driverops,			/* drv_modops */
24811717Sgdamore@opensolaris.org 	"3Com EtherLink XL",		/* drv_linkinfo */
24911717Sgdamore@opensolaris.org 	&ex_devops			/* drv_dev_ops */
25011717Sgdamore@opensolaris.org };
25111717Sgdamore@opensolaris.org 
25211717Sgdamore@opensolaris.org static struct modlinkage ex_modlinkage = {
25311717Sgdamore@opensolaris.org 	MODREV_1,		/* ml_rev */
25411717Sgdamore@opensolaris.org 	{ &ex_modldrv, NULL }	/* ml_linkage */
25511717Sgdamore@opensolaris.org };
25611717Sgdamore@opensolaris.org 
25711717Sgdamore@opensolaris.org int
_init(void)25811717Sgdamore@opensolaris.org _init(void)
25911717Sgdamore@opensolaris.org {
26011717Sgdamore@opensolaris.org 	int	rv;
26111717Sgdamore@opensolaris.org 	mac_init_ops(&ex_devops, "elxl");
26211717Sgdamore@opensolaris.org 	if ((rv = mod_install(&ex_modlinkage)) != DDI_SUCCESS) {
26311717Sgdamore@opensolaris.org 		mac_fini_ops(&ex_devops);
26411717Sgdamore@opensolaris.org 	}
26511717Sgdamore@opensolaris.org 	return (rv);
26611717Sgdamore@opensolaris.org }
26711717Sgdamore@opensolaris.org 
26811717Sgdamore@opensolaris.org int
_fini(void)26911717Sgdamore@opensolaris.org _fini(void)
27011717Sgdamore@opensolaris.org {
27111717Sgdamore@opensolaris.org 	int	rv;
27211717Sgdamore@opensolaris.org 	if ((rv = mod_remove(&ex_modlinkage)) == DDI_SUCCESS) {
27311717Sgdamore@opensolaris.org 		mac_fini_ops(&ex_devops);
27411717Sgdamore@opensolaris.org 	}
27511717Sgdamore@opensolaris.org 	return (rv);
27611717Sgdamore@opensolaris.org }
27711717Sgdamore@opensolaris.org 
27811717Sgdamore@opensolaris.org int
_info(struct modinfo * modinfop)27911717Sgdamore@opensolaris.org _info(struct modinfo *modinfop)
28011717Sgdamore@opensolaris.org {
28111717Sgdamore@opensolaris.org 	return (mod_info(&ex_modlinkage, modinfop));
28211717Sgdamore@opensolaris.org }
28311717Sgdamore@opensolaris.org 
28411717Sgdamore@opensolaris.org static void
ex_free_ring(ex_ring_t * r)28511717Sgdamore@opensolaris.org ex_free_ring(ex_ring_t *r)
28611717Sgdamore@opensolaris.org {
28711717Sgdamore@opensolaris.org 	for (int i = 0; i < r->r_count; i++) {
28811717Sgdamore@opensolaris.org 		ex_desc_t *ed = &r->r_desc[i];
28911717Sgdamore@opensolaris.org 		if (ed->ed_bufaddr)
29011717Sgdamore@opensolaris.org 			(void) ddi_dma_unbind_handle(ed->ed_dmah);
29111717Sgdamore@opensolaris.org 		if (ed->ed_acch)
29211717Sgdamore@opensolaris.org 			ddi_dma_mem_free(&ed->ed_acch);
29311717Sgdamore@opensolaris.org 		if (ed->ed_dmah)
29411717Sgdamore@opensolaris.org 			ddi_dma_free_handle(&ed->ed_dmah);
29511717Sgdamore@opensolaris.org 	}
29611717Sgdamore@opensolaris.org 
29711717Sgdamore@opensolaris.org 	if (r->r_paddr)
29811717Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(r->r_dmah);
29911717Sgdamore@opensolaris.org 	if (r->r_acch)
30011717Sgdamore@opensolaris.org 		ddi_dma_mem_free(&r->r_acch);
30111717Sgdamore@opensolaris.org 	if (r->r_dmah)
30211717Sgdamore@opensolaris.org 		ddi_dma_free_handle(&r->r_dmah);
30311717Sgdamore@opensolaris.org 
30411717Sgdamore@opensolaris.org 	kmem_free(r->r_desc, sizeof (ex_desc_t) * r->r_count);
30511717Sgdamore@opensolaris.org 	r->r_desc = NULL;
30611717Sgdamore@opensolaris.org }
30711717Sgdamore@opensolaris.org 
30811717Sgdamore@opensolaris.org static void
elxl_reset_ring(ex_ring_t * r,uint_t dir)30911717Sgdamore@opensolaris.org elxl_reset_ring(ex_ring_t *r, uint_t dir)
31011717Sgdamore@opensolaris.org {
31111717Sgdamore@opensolaris.org 	ex_desc_t	*ed;
31211717Sgdamore@opensolaris.org 	ex_pd_t		*pd;
31311717Sgdamore@opensolaris.org 
31411717Sgdamore@opensolaris.org 	if (dir == DDI_DMA_WRITE) {
31511717Sgdamore@opensolaris.org 		/* transmit ring, not linked yet */
31611717Sgdamore@opensolaris.org 		for (int i = 0; i < r->r_count; i++) {
31711717Sgdamore@opensolaris.org 			ed = &r->r_desc[i];
31811717Sgdamore@opensolaris.org 			pd = ed->ed_pd;
31911717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_link, 0);
32011717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_fsh, 0);
32111717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_len, EX_FR_LAST);
32211717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_addr, ed->ed_bufaddr);
32311717Sgdamore@opensolaris.org 		}
32411717Sgdamore@opensolaris.org 		r->r_head = NULL;
32511717Sgdamore@opensolaris.org 		r->r_tail = NULL;
32611717Sgdamore@opensolaris.org 		r->r_avail = r->r_count;
32711717Sgdamore@opensolaris.org 	} else {
32811717Sgdamore@opensolaris.org 		/* receive is linked into a list */
32911717Sgdamore@opensolaris.org 		for (int i = 0; i < r->r_count; i++) {
33011717Sgdamore@opensolaris.org 			ed = &r->r_desc[i];
33111717Sgdamore@opensolaris.org 			pd = ed->ed_pd;
33211717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_link, ed->ed_next->ed_descaddr);
33311717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_status, 0);
33411717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST);
33511717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_addr, ed->ed_bufaddr);
33611717Sgdamore@opensolaris.org 		}
33711717Sgdamore@opensolaris.org 		r->r_head = &r->r_desc[0];
33811717Sgdamore@opensolaris.org 		r->r_tail = NULL;
33911717Sgdamore@opensolaris.org 		r->r_avail = 0;
34011717Sgdamore@opensolaris.org 	}
34111720Sgdamore@opensolaris.org 	(void) ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
34211717Sgdamore@opensolaris.org }
34311717Sgdamore@opensolaris.org 
34411717Sgdamore@opensolaris.org static boolean_t
ex_alloc_ring(elxl_t * sc,int count,ex_ring_t * r,uint_t dir)34511717Sgdamore@opensolaris.org ex_alloc_ring(elxl_t *sc, int count, ex_ring_t *r, uint_t dir)
34611717Sgdamore@opensolaris.org {
34711717Sgdamore@opensolaris.org 	dev_info_t		*dip = sc->ex_dip;
34811717Sgdamore@opensolaris.org 	int			i;
34911717Sgdamore@opensolaris.org 	int			rv;
35011717Sgdamore@opensolaris.org 	size_t			len;
35111717Sgdamore@opensolaris.org 	ddi_dma_cookie_t	dmac;
35211717Sgdamore@opensolaris.org 	unsigned		ndmac;
35311717Sgdamore@opensolaris.org 
35411717Sgdamore@opensolaris.org 	r->r_count = count;
35511717Sgdamore@opensolaris.org 	r->r_desc = kmem_zalloc(sizeof (ex_desc_t) * count, KM_SLEEP);
35611717Sgdamore@opensolaris.org 
35711717Sgdamore@opensolaris.org 	rv = ddi_dma_alloc_handle(dip, &ex_dma_attr, DDI_DMA_DONTWAIT,
35811717Sgdamore@opensolaris.org 	    NULL, &r->r_dmah);
35911717Sgdamore@opensolaris.org 	if (rv != DDI_SUCCESS) {
36011717Sgdamore@opensolaris.org 		elxl_error(sc, "unable to allocate descriptor dma handle");
36111717Sgdamore@opensolaris.org 		return (B_FALSE);
36211717Sgdamore@opensolaris.org 	}
36311717Sgdamore@opensolaris.org 
36411717Sgdamore@opensolaris.org 	rv = ddi_dma_mem_alloc(r->r_dmah, count * sizeof (struct ex_pd),
36511717Sgdamore@opensolaris.org 	    &ex_dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
36611717Sgdamore@opensolaris.org 	    (caddr_t *)&r->r_pd, &len, &r->r_acch);
36711717Sgdamore@opensolaris.org 	if (rv != DDI_SUCCESS) {
36811717Sgdamore@opensolaris.org 		elxl_error(sc, "unable to allocate descriptor memory");
36911717Sgdamore@opensolaris.org 		return (B_FALSE);
37011717Sgdamore@opensolaris.org 	}
37111717Sgdamore@opensolaris.org 	bzero(r->r_pd, len);
37211717Sgdamore@opensolaris.org 
37311717Sgdamore@opensolaris.org 	rv = ddi_dma_addr_bind_handle(r->r_dmah, NULL,
37411717Sgdamore@opensolaris.org 	    (caddr_t)r->r_pd, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
37511717Sgdamore@opensolaris.org 	    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
37611717Sgdamore@opensolaris.org 	if (rv != DDI_DMA_MAPPED) {
37711717Sgdamore@opensolaris.org 		elxl_error(sc, "unable to map descriptor memory");
37811717Sgdamore@opensolaris.org 		return (B_FALSE);
37911717Sgdamore@opensolaris.org 	}
38011717Sgdamore@opensolaris.org 	r->r_paddr = dmac.dmac_address;
38111717Sgdamore@opensolaris.org 
38211717Sgdamore@opensolaris.org 	for (i = 0; i < count; i++) {
38311717Sgdamore@opensolaris.org 		ex_desc_t	*ed = &r->r_desc[i];
38411717Sgdamore@opensolaris.org 		ex_pd_t		*pd = &r->r_pd[i];
38511717Sgdamore@opensolaris.org 
38611717Sgdamore@opensolaris.org 		ed->ed_pd = pd;
38711717Sgdamore@opensolaris.org 		ed->ed_off = (i * sizeof (ex_pd_t));
38811717Sgdamore@opensolaris.org 		ed->ed_descaddr = r->r_paddr + (i * sizeof (ex_pd_t));
38911717Sgdamore@opensolaris.org 
39011717Sgdamore@opensolaris.org 		/* Link the high level descriptors into a ring. */
39111717Sgdamore@opensolaris.org 		ed->ed_next = &r->r_desc[(i + 1) % count];
39211717Sgdamore@opensolaris.org 		ed->ed_next->ed_prev = ed;
39311717Sgdamore@opensolaris.org 
39411717Sgdamore@opensolaris.org 		rv = ddi_dma_alloc_handle(dip, &ex_dma_attr,
39511717Sgdamore@opensolaris.org 		    DDI_DMA_DONTWAIT, NULL, &ed->ed_dmah);
39611717Sgdamore@opensolaris.org 		if (rv != 0) {
39711717Sgdamore@opensolaris.org 			elxl_error(sc, "can't allocate buf dma handle");
39811717Sgdamore@opensolaris.org 			return (B_FALSE);
39911717Sgdamore@opensolaris.org 		}
40011717Sgdamore@opensolaris.org 		rv = ddi_dma_mem_alloc(ed->ed_dmah, EX_BUFSZ, &ex_buf_acc_attr,
40111717Sgdamore@opensolaris.org 		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &ed->ed_buf,
40211717Sgdamore@opensolaris.org 		    &len, &ed->ed_acch);
40311717Sgdamore@opensolaris.org 		if (rv != DDI_SUCCESS) {
40411717Sgdamore@opensolaris.org 			elxl_error(sc, "unable to allocate buf memory");
40511717Sgdamore@opensolaris.org 			return (B_FALSE);
40611717Sgdamore@opensolaris.org 		}
40711717Sgdamore@opensolaris.org 		bzero(ed->ed_buf, len);
40811717Sgdamore@opensolaris.org 
40911717Sgdamore@opensolaris.org 		rv = ddi_dma_addr_bind_handle(ed->ed_dmah, NULL,
41011717Sgdamore@opensolaris.org 		    ed->ed_buf, len, dir | DDI_DMA_STREAMING,
41111717Sgdamore@opensolaris.org 		    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
41211717Sgdamore@opensolaris.org 		if (rv != DDI_DMA_MAPPED) {
41311717Sgdamore@opensolaris.org 			elxl_error(sc, "unable to map buf memory");
41411717Sgdamore@opensolaris.org 			return (B_FALSE);
41511717Sgdamore@opensolaris.org 		}
41611717Sgdamore@opensolaris.org 		ed->ed_bufaddr = dmac.dmac_address;
41711717Sgdamore@opensolaris.org 	}
41811717Sgdamore@opensolaris.org 
41911717Sgdamore@opensolaris.org 	elxl_reset_ring(r, dir);
42011717Sgdamore@opensolaris.org 
42111717Sgdamore@opensolaris.org 	return (B_TRUE);
42211717Sgdamore@opensolaris.org }
42311717Sgdamore@opensolaris.org 
42411717Sgdamore@opensolaris.org static boolean_t
elxl_add_intr(elxl_t * sc)42511717Sgdamore@opensolaris.org elxl_add_intr(elxl_t *sc)
42611717Sgdamore@opensolaris.org {
42711717Sgdamore@opensolaris.org 	dev_info_t		*dip;
42811717Sgdamore@opensolaris.org 	int			actual;
42911717Sgdamore@opensolaris.org 	uint_t			ipri;
43011717Sgdamore@opensolaris.org 
43111717Sgdamore@opensolaris.org 	int			rv;
43211717Sgdamore@opensolaris.org 
43311717Sgdamore@opensolaris.org 	dip = sc->ex_dip;
43411717Sgdamore@opensolaris.org 
43511717Sgdamore@opensolaris.org 	rv = ddi_intr_alloc(dip, &sc->ex_intrh, DDI_INTR_TYPE_FIXED,
43611717Sgdamore@opensolaris.org 	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
43711717Sgdamore@opensolaris.org 	if ((rv != DDI_SUCCESS) || (actual != 1)) {
43811717Sgdamore@opensolaris.org 		elxl_error(sc, "Unable to allocate interrupt, %d, count %d",
43911717Sgdamore@opensolaris.org 		    rv, actual);
44011717Sgdamore@opensolaris.org 		return (B_FALSE);
44111717Sgdamore@opensolaris.org 	}
44211717Sgdamore@opensolaris.org 
44311717Sgdamore@opensolaris.org 	if (ddi_intr_get_pri(sc->ex_intrh, &ipri) != DDI_SUCCESS) {
44411717Sgdamore@opensolaris.org 		elxl_error(sc, "Unable to get interrupt priority");
44511717Sgdamore@opensolaris.org 		return (B_FALSE);
44611717Sgdamore@opensolaris.org 	}
44711717Sgdamore@opensolaris.org 
44811717Sgdamore@opensolaris.org 	if (ddi_intr_add_handler(sc->ex_intrh, elxl_intr, sc, NULL) !=
44911717Sgdamore@opensolaris.org 	    DDI_SUCCESS) {
45011717Sgdamore@opensolaris.org 		elxl_error(sc, "Can't add interrupt handler");
45111717Sgdamore@opensolaris.org 		(void) ddi_intr_free(sc->ex_intrh);
45211717Sgdamore@opensolaris.org 		sc->ex_intrh = NULL;
45311717Sgdamore@opensolaris.org 		return (B_FALSE);
45411717Sgdamore@opensolaris.org 	}
45511717Sgdamore@opensolaris.org 	mutex_init(&sc->ex_intrlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
45611717Sgdamore@opensolaris.org 	mutex_init(&sc->ex_txlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
45711717Sgdamore@opensolaris.org 
45811717Sgdamore@opensolaris.org 	return (B_TRUE);
45911717Sgdamore@opensolaris.org }
46011717Sgdamore@opensolaris.org 
46111717Sgdamore@opensolaris.org static int
elxl_attach(dev_info_t * dip)46211717Sgdamore@opensolaris.org elxl_attach(dev_info_t *dip)
46311717Sgdamore@opensolaris.org {
46411717Sgdamore@opensolaris.org 	elxl_t		*sc;
46511717Sgdamore@opensolaris.org 	mac_register_t	*macp;
46611717Sgdamore@opensolaris.org 	uint16_t	val;
46711717Sgdamore@opensolaris.org 	uint16_t	venid;
46811717Sgdamore@opensolaris.org 	uint16_t	devid;
46911717Sgdamore@opensolaris.org 	int		i;
47011717Sgdamore@opensolaris.org 
47111717Sgdamore@opensolaris.org 	sc = kmem_zalloc(sizeof (*sc), KM_SLEEP);
47211717Sgdamore@opensolaris.org 	ddi_set_driver_private(dip, sc);
47311717Sgdamore@opensolaris.org 	sc->ex_dip = dip;
47411717Sgdamore@opensolaris.org 
47511717Sgdamore@opensolaris.org 	if (pci_config_setup(dip, &sc->ex_pcih) != DDI_SUCCESS) {
47611717Sgdamore@opensolaris.org 		elxl_error(sc, "unable to setup PCI config handle");
47711717Sgdamore@opensolaris.org 		goto fail;
47811717Sgdamore@opensolaris.org 	}
47911717Sgdamore@opensolaris.org 	venid = pci_config_get16(sc->ex_pcih, PCI_CONF_VENID);
48011717Sgdamore@opensolaris.org 	devid = pci_config_get16(sc->ex_pcih, PCI_CONF_DEVID);
48111717Sgdamore@opensolaris.org 
48211717Sgdamore@opensolaris.org 	if (venid != 0x10b7) {
48311717Sgdamore@opensolaris.org 		/* Not a 3Com part! */
48411717Sgdamore@opensolaris.org 		elxl_error(sc, "Unsupported vendor id (0x%x)", venid);
48511717Sgdamore@opensolaris.org 		goto fail;
48611717Sgdamore@opensolaris.org 	}
48711717Sgdamore@opensolaris.org 	for (i = 0; ex_products[i].epp_name; i++) {
48811717Sgdamore@opensolaris.org 		if (devid == ex_products[i].epp_prodid) {
48911717Sgdamore@opensolaris.org 			cmn_err(CE_CONT, "?%s%d: 3Com %s",
49011717Sgdamore@opensolaris.org 			    ddi_driver_name(dip),
49111717Sgdamore@opensolaris.org 			    ddi_get_instance(dip),
49211717Sgdamore@opensolaris.org 			    ex_products[i].epp_name);
49311717Sgdamore@opensolaris.org 			sc->ex_conf = ex_products[i].epp_flags;
49411717Sgdamore@opensolaris.org 			break;
49511717Sgdamore@opensolaris.org 		}
49611717Sgdamore@opensolaris.org 	}
49711717Sgdamore@opensolaris.org 	if (ex_products[i].epp_name == NULL) {
49811717Sgdamore@opensolaris.org 		/* Not a produce we know how to support */
49911717Sgdamore@opensolaris.org 		elxl_error(sc, "Unsupported device id (0x%x)", devid);
50011717Sgdamore@opensolaris.org 		elxl_error(sc, "Driver may or may not function.");
50111717Sgdamore@opensolaris.org 	}
50211717Sgdamore@opensolaris.org 
50311717Sgdamore@opensolaris.org 	pci_config_put16(sc->ex_pcih, PCI_CONF_COMM,
50411717Sgdamore@opensolaris.org 	    pci_config_get16(sc->ex_pcih, PCI_CONF_COMM) |
50511717Sgdamore@opensolaris.org 	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
50611717Sgdamore@opensolaris.org 
50711717Sgdamore@opensolaris.org 	if (ddi_regs_map_setup(dip, 1, &sc->ex_regsva, 0, 0, &ex_dev_acc_attr,
50811717Sgdamore@opensolaris.org 	    &sc->ex_regsh) != DDI_SUCCESS) {
50911717Sgdamore@opensolaris.org 		elxl_error(sc, "Unable to map device registers");
51011717Sgdamore@opensolaris.org 		goto fail;
51111717Sgdamore@opensolaris.org 	}
51211717Sgdamore@opensolaris.org 
51311717Sgdamore@opensolaris.org 	if (!elxl_add_intr(sc)) {
51411717Sgdamore@opensolaris.org 		goto fail;
51511717Sgdamore@opensolaris.org 	}
51611717Sgdamore@opensolaris.org 
51711717Sgdamore@opensolaris.org 	elxl_reset(sc);
51811717Sgdamore@opensolaris.org 
51911717Sgdamore@opensolaris.org 	val = elxl_read_eeprom(sc, EE_OEM_ADDR_0);
52011717Sgdamore@opensolaris.org 	sc->ex_factaddr[0] = val >> 8;
52111717Sgdamore@opensolaris.org 	sc->ex_factaddr[1] = val & 0xff;
52211717Sgdamore@opensolaris.org 	val = elxl_read_eeprom(sc, EE_OEM_ADDR_1);
52311717Sgdamore@opensolaris.org 	sc->ex_factaddr[2] = val >> 8;
52411717Sgdamore@opensolaris.org 	sc->ex_factaddr[3] = val & 0xff;
52511717Sgdamore@opensolaris.org 	val = elxl_read_eeprom(sc, EE_OEM_ADDR_2);
52611717Sgdamore@opensolaris.org 	sc->ex_factaddr[4] = val >> 8;
52711717Sgdamore@opensolaris.org 	sc->ex_factaddr[5] = val & 0xff;
52811717Sgdamore@opensolaris.org 	bcopy(sc->ex_factaddr, sc->ex_curraddr, 6);
52911717Sgdamore@opensolaris.org 
53011717Sgdamore@opensolaris.org 	sc->ex_capab = elxl_read_eeprom(sc, EE_CAPABILITIES);
53111717Sgdamore@opensolaris.org 
53211717Sgdamore@opensolaris.org 	/*
53311717Sgdamore@opensolaris.org 	 * Is this a 90XB?  If bit 2 (supportsLargePackets) is set, or
53411717Sgdamore@opensolaris.org 	 * bit (supportsNoTxLength) is clear, then its a 90X.
53511717Sgdamore@opensolaris.org 	 * Otherwise its a 90XB.
53611717Sgdamore@opensolaris.org 	 */
53711717Sgdamore@opensolaris.org 	if ((sc->ex_capab & (1 << 2)) || !(sc->ex_capab & (1 << 9))) {
53811717Sgdamore@opensolaris.org 		sc->ex_conf &= ~CONF_90XB;
53911717Sgdamore@opensolaris.org 	} else {
54011717Sgdamore@opensolaris.org 		sc->ex_conf |= CONF_90XB;
54111717Sgdamore@opensolaris.org 	}
54211717Sgdamore@opensolaris.org 
54311717Sgdamore@opensolaris.org 	if (!ex_alloc_ring(sc, EX_NRX, &sc->ex_rxring, DDI_DMA_READ)) {
54411717Sgdamore@opensolaris.org 		goto fail;
54511717Sgdamore@opensolaris.org 	}
54611717Sgdamore@opensolaris.org 
54711717Sgdamore@opensolaris.org 	if (!ex_alloc_ring(sc, EX_NTX, &sc->ex_txring, DDI_DMA_WRITE)) {
54811717Sgdamore@opensolaris.org 		goto fail;
54911717Sgdamore@opensolaris.org 	}
55011717Sgdamore@opensolaris.org 
55111717Sgdamore@opensolaris.org 	elxl_probe_media(sc);
55211717Sgdamore@opensolaris.org 
55311717Sgdamore@opensolaris.org 	/*
55411717Sgdamore@opensolaris.org 	 * The probe may have indicated MII!
55511717Sgdamore@opensolaris.org 	 */
55611717Sgdamore@opensolaris.org 	if (sc->ex_mediaopt & (MEDIAOPT_MII | MEDIAOPT_100TX)) {
55711717Sgdamore@opensolaris.org 		sc->ex_miih = mii_alloc(sc, sc->ex_dip, &ex_mii_ops);
55811717Sgdamore@opensolaris.org 		if (sc->ex_miih == NULL) {
55911717Sgdamore@opensolaris.org 			goto fail;
56011717Sgdamore@opensolaris.org 		}
56111717Sgdamore@opensolaris.org 		/*
56211717Sgdamore@opensolaris.org 		 * Note: The 90XB models can in theory support pause,
56311717Sgdamore@opensolaris.org 		 * but we're not enabling now due to lack of units for
56411717Sgdamore@opensolaris.org 		 * testing with.  If this is changed, make sure to
56511717Sgdamore@opensolaris.org 		 * update the code in elxl_mii_notify to set the flow
56611717Sgdamore@opensolaris.org 		 * control field in the W3_MAC_CONTROL register.
56711717Sgdamore@opensolaris.org 		 */
56811717Sgdamore@opensolaris.org 		mii_set_pauseable(sc->ex_miih, B_FALSE, B_FALSE);
56911717Sgdamore@opensolaris.org 	}
57011717Sgdamore@opensolaris.org 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
57111717Sgdamore@opensolaris.org 		elxl_error(sc, "MAC register allocation failed");
57211717Sgdamore@opensolaris.org 		goto fail;
57311717Sgdamore@opensolaris.org 	}
57411717Sgdamore@opensolaris.org 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
57511717Sgdamore@opensolaris.org 	macp->m_driver = sc;
57611717Sgdamore@opensolaris.org 	macp->m_dip = dip;
57711717Sgdamore@opensolaris.org 	macp->m_src_addr = sc->ex_curraddr;
57811717Sgdamore@opensolaris.org 	macp->m_callbacks = &elxl_m_callbacks;
57911717Sgdamore@opensolaris.org 	macp->m_min_sdu = 0;
58011717Sgdamore@opensolaris.org 	macp->m_max_sdu = ETHERMTU;
58111717Sgdamore@opensolaris.org 	macp->m_margin = VLAN_TAGSZ;
58211717Sgdamore@opensolaris.org 	macp->m_priv_props = ex_priv_prop;
58311717Sgdamore@opensolaris.org 
58411717Sgdamore@opensolaris.org 	(void) ddi_intr_enable(sc->ex_intrh);
58511717Sgdamore@opensolaris.org 
58611717Sgdamore@opensolaris.org 	if (mac_register(macp, &sc->ex_mach) == DDI_SUCCESS) {
58711717Sgdamore@opensolaris.org 
58811717Sgdamore@opensolaris.org 			/*
58911717Sgdamore@opensolaris.org 			 * Note: we don't want to start link checking
59011717Sgdamore@opensolaris.org 			 * until *after* we have added the MAC handle.
59111717Sgdamore@opensolaris.org 			 */
59211717Sgdamore@opensolaris.org 		if (sc->ex_mediaopt &
59311717Sgdamore@opensolaris.org 		    (MEDIAOPT_MASK & ~(MEDIAOPT_MII | MEDIAOPT_100TX))) {
59411717Sgdamore@opensolaris.org 
59511717Sgdamore@opensolaris.org 			/* Check non-MII link state once per second. */
59611717Sgdamore@opensolaris.org 			sc->ex_linkcheck =
59711717Sgdamore@opensolaris.org 			    ddi_periodic_add(elxl_linkcheck, sc, 10000000, 0);
59811717Sgdamore@opensolaris.org 		}
59911717Sgdamore@opensolaris.org 
60011717Sgdamore@opensolaris.org 		mac_free(macp);
60111717Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
60211717Sgdamore@opensolaris.org 	}
60311717Sgdamore@opensolaris.org 
60411717Sgdamore@opensolaris.org 	mac_free(macp);
60511717Sgdamore@opensolaris.org 
60611717Sgdamore@opensolaris.org fail:
60711717Sgdamore@opensolaris.org 	elxl_detach(sc);
60811717Sgdamore@opensolaris.org 	return (DDI_FAILURE);
60911717Sgdamore@opensolaris.org }
61011717Sgdamore@opensolaris.org 
61111717Sgdamore@opensolaris.org /*
61211717Sgdamore@opensolaris.org  * Find the media present on non-MII chips, and select the one to use.
61311717Sgdamore@opensolaris.org  */
61411717Sgdamore@opensolaris.org static void
elxl_probe_media(elxl_t * sc)61511717Sgdamore@opensolaris.org elxl_probe_media(elxl_t *sc)
61611717Sgdamore@opensolaris.org {
61711717Sgdamore@opensolaris.org 	ex_media_t	*exm;
61811717Sgdamore@opensolaris.org 	uint32_t	config;
61911717Sgdamore@opensolaris.org 	uint32_t	default_media;
62011717Sgdamore@opensolaris.org 	uint16_t	media_options;
62111717Sgdamore@opensolaris.org 
62211717Sgdamore@opensolaris.org 	SET_WIN(3);
62311717Sgdamore@opensolaris.org 	config = GET32(W3_INTERNAL_CONFIG);
62411717Sgdamore@opensolaris.org 	media_options = GET16(W3_MEDIAOPT);
62511717Sgdamore@opensolaris.org 
62611717Sgdamore@opensolaris.org 	/*
62711717Sgdamore@opensolaris.org 	 * We modify the media_options field so that we have a
62811717Sgdamore@opensolaris.org 	 * consistent view of the media available, without worrying
62911717Sgdamore@opensolaris.org 	 * about the version of ASIC, etc.
63011717Sgdamore@opensolaris.org 	 */
63111717Sgdamore@opensolaris.org 
63211717Sgdamore@opensolaris.org 	/*
63311717Sgdamore@opensolaris.org 	 * 100BASE-TX is handled differently on 90XB from 90X.  Older
63411717Sgdamore@opensolaris.org 	 * parts use the external MII to provide this support.
63511717Sgdamore@opensolaris.org 	 */
63611717Sgdamore@opensolaris.org 	if (sc->ex_conf & CONF_90XB) {
63711717Sgdamore@opensolaris.org 		if (media_options & MEDIAOPT_100TX) {
63811717Sgdamore@opensolaris.org 			/*
63911717Sgdamore@opensolaris.org 			 * 3Com advises that we should only ever use the
64011717Sgdamore@opensolaris.org 			 * auto mode.  Notably, it seems that there should
64111717Sgdamore@opensolaris.org 			 * never be a 90XB board with the MEDIAOPT_10T bit set
64211717Sgdamore@opensolaris.org 			 * without this bit.  If it happens, the driver will
64311717Sgdamore@opensolaris.org 			 * run in compatible 10BASE-T only mode.
64411717Sgdamore@opensolaris.org 			 */
64511717Sgdamore@opensolaris.org 			media_options &= ~MEDIAOPT_10T;
64611717Sgdamore@opensolaris.org 		}
64711717Sgdamore@opensolaris.org 	} else {
64811717Sgdamore@opensolaris.org 		if (media_options & MEDIAOPT_100TX) {
64911717Sgdamore@opensolaris.org 			/*
65011717Sgdamore@opensolaris.org 			 * If this occurs, we really want to use it like
65111717Sgdamore@opensolaris.org 			 * an MII device.  Generally in this situation we
65211717Sgdamore@opensolaris.org 			 * want to use the MII exclusively, and there ought
65311717Sgdamore@opensolaris.org 			 * not be a 10bT transceiver.
65411717Sgdamore@opensolaris.org 			 */
65511717Sgdamore@opensolaris.org 			media_options |= MEDIAOPT_MII;
65611717Sgdamore@opensolaris.org 			media_options &= ~MEDIAOPT_100TX;
65711717Sgdamore@opensolaris.org 			media_options &= ~MEDIAOPT_10T;
65811717Sgdamore@opensolaris.org 
65911717Sgdamore@opensolaris.org 			/*
66011717Sgdamore@opensolaris.org 			 * Additionally, some of these devices map all
66111717Sgdamore@opensolaris.org 			 * internal PHY register at *every* address, not
66211717Sgdamore@opensolaris.org 			 * just the "allowed" address 24.
66311717Sgdamore@opensolaris.org 			 */
66411717Sgdamore@opensolaris.org 			sc->ex_conf |= CONF_INTPHY;
66511717Sgdamore@opensolaris.org 		}
66611717Sgdamore@opensolaris.org 		/*
66711717Sgdamore@opensolaris.org 		 * Early versions didn't have 10FL models, and used this
66811717Sgdamore@opensolaris.org 		 * bit for something else (VCO).
66911717Sgdamore@opensolaris.org 		 */
67011717Sgdamore@opensolaris.org 		media_options &= ~MEDIAOPT_10FL;
67111717Sgdamore@opensolaris.org 	}
67211717Sgdamore@opensolaris.org 	if (media_options & MEDIAOPT_100T4) {
67311717Sgdamore@opensolaris.org 		/* 100BASE-T4 units all use the MII bus. */
67411717Sgdamore@opensolaris.org 		media_options |= MEDIAOPT_MII;
67511717Sgdamore@opensolaris.org 		media_options &= ~MEDIAOPT_100T4;
67611717Sgdamore@opensolaris.org 	}
67711717Sgdamore@opensolaris.org 
67811717Sgdamore@opensolaris.org 	/* Save our media options. */
67911717Sgdamore@opensolaris.org 	sc->ex_mediaopt = media_options;
68011717Sgdamore@opensolaris.org 
68111717Sgdamore@opensolaris.org #define	APPEND_MEDIA(str, bit, name)					\
68211717Sgdamore@opensolaris.org 	if (media_options & (bit)) {					\
68311717Sgdamore@opensolaris.org 		(void) strlcat(str, *str ? "," : "", sizeof (str));	\
68411717Sgdamore@opensolaris.org 		(void) strlcat(str, name, sizeof (str));		\
68511717Sgdamore@opensolaris.org 	}
68611717Sgdamore@opensolaris.org 
68711717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, (MEDIAOPT_MII|MEDIAOPT_100TX), "mii");
68811717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10T, "tp-hdx,tp-fdx");
68911717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_100FX, "fx-hdx,fx-fdx");
69011717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_BNC, "bnc");
69111717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_AUI, "aui");
69211717Sgdamore@opensolaris.org 	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10FL, "fl-hdx,fl-fdx");
69311717Sgdamore@opensolaris.org 
69411717Sgdamore@opensolaris.org 	if (config & XCVR_SEL_100TX) {
69511717Sgdamore@opensolaris.org 		/* Only found on 90XB.  Don't use this, use AUTO instead! */
69611717Sgdamore@opensolaris.org 		config |= XCVR_SEL_AUTO;
69711717Sgdamore@opensolaris.org 		config &= ~XCVR_SEL_100TX;
69811717Sgdamore@opensolaris.org 	}
69911717Sgdamore@opensolaris.org 
70011717Sgdamore@opensolaris.org 	default_media = (config & XCVR_SEL_MASK);
70111717Sgdamore@opensolaris.org 
70211717Sgdamore@opensolaris.org 	/* Sanity check that there are any media! */
70311717Sgdamore@opensolaris.org 	if ((media_options & MEDIAOPT_MASK) == 0) {
70411717Sgdamore@opensolaris.org 		elxl_error(sc,
70511717Sgdamore@opensolaris.org 		    "No media present?  Attempting to use default.");
70611717Sgdamore@opensolaris.org 		/*
70711717Sgdamore@opensolaris.org 		 * This "default" may be non-sensical.  At worst it should
70811717Sgdamore@opensolaris.org 		 * cause a busted link.
70911717Sgdamore@opensolaris.org 		 */
71011717Sgdamore@opensolaris.org 		sc->ex_xcvr = default_media;
71111717Sgdamore@opensolaris.org 	}
71211717Sgdamore@opensolaris.org 
71311717Sgdamore@opensolaris.org 	for (exm = ex_native_media; exm->exm_mpbit != 0; exm++) {
71411717Sgdamore@opensolaris.org 		if (media_options & exm->exm_mpbit) {
71511717Sgdamore@opensolaris.org 			if (exm->exm_xcvr == default_media) {
71611717Sgdamore@opensolaris.org 				/* preferred default is present, just use it */
71711717Sgdamore@opensolaris.org 				sc->ex_xcvr = default_media;
71811717Sgdamore@opensolaris.org 				return;
71911717Sgdamore@opensolaris.org 			}
72011717Sgdamore@opensolaris.org 
72111717Sgdamore@opensolaris.org 			sc->ex_xcvr = exm->exm_xcvr;
72211717Sgdamore@opensolaris.org 			/* but keep trying for other more preferred options */
72311717Sgdamore@opensolaris.org 		}
72411717Sgdamore@opensolaris.org 	}
72511717Sgdamore@opensolaris.org }
72611717Sgdamore@opensolaris.org 
72711717Sgdamore@opensolaris.org /*
72811717Sgdamore@opensolaris.org  * Setup transmitter parameters.
72911717Sgdamore@opensolaris.org  */
73011717Sgdamore@opensolaris.org static void
elxl_setup_tx(elxl_t * sc)73111717Sgdamore@opensolaris.org elxl_setup_tx(elxl_t *sc)
73211717Sgdamore@opensolaris.org {
73311717Sgdamore@opensolaris.org 	/*
73411717Sgdamore@opensolaris.org 	 * Disable reclaim threshold for 90xB, set free threshold to
73511717Sgdamore@opensolaris.org 	 * 6 * 256 = 1536 for 90x.
73611717Sgdamore@opensolaris.org 	 */
73711717Sgdamore@opensolaris.org 	if (sc->ex_conf & CONF_90XB)
73811717Sgdamore@opensolaris.org 		PUT_CMD(CMD_SET_TXRECLAIM | 255);
73911717Sgdamore@opensolaris.org 	else
74011717Sgdamore@opensolaris.org 		PUT8(REG_TXFREETHRESH, 6);
74111717Sgdamore@opensolaris.org 
74211717Sgdamore@opensolaris.org 	/*
74311717Sgdamore@opensolaris.org 	 * We've seen underflows at the root cause of NIC hangs on
74411717Sgdamore@opensolaris.org 	 * older cards.  Use a store-and-forward model to prevent that.
74511717Sgdamore@opensolaris.org 	 */
74611717Sgdamore@opensolaris.org 	PUT_CMD(CMD_SET_TXSTART | EX_BUFSZ >> 2);
74711717Sgdamore@opensolaris.org }
74811717Sgdamore@opensolaris.org 
74911717Sgdamore@opensolaris.org /*
75011717Sgdamore@opensolaris.org  * Bring device up.
75111717Sgdamore@opensolaris.org  */
75211717Sgdamore@opensolaris.org static void
elxl_init(elxl_t * sc)75311717Sgdamore@opensolaris.org elxl_init(elxl_t *sc)
75411717Sgdamore@opensolaris.org {
75511717Sgdamore@opensolaris.org 	if (sc->ex_suspended)
75611717Sgdamore@opensolaris.org 		return;
75711717Sgdamore@opensolaris.org 
75811717Sgdamore@opensolaris.org 	WAIT_CMD(sc);
75911717Sgdamore@opensolaris.org 	elxl_stop(sc);
76011717Sgdamore@opensolaris.org 
76111717Sgdamore@opensolaris.org 	PUT_CMD(CMD_RX_RESET);
76211717Sgdamore@opensolaris.org 	WAIT_CMD(sc);
76311717Sgdamore@opensolaris.org 	PUT_CMD(CMD_TX_RESET);
76411717Sgdamore@opensolaris.org 	WAIT_CMD(sc);
76511717Sgdamore@opensolaris.org 
76611717Sgdamore@opensolaris.org 	/* Load Tx parameters. */
76711717Sgdamore@opensolaris.org 	elxl_setup_tx(sc);
76811717Sgdamore@opensolaris.org 
76911717Sgdamore@opensolaris.org 	PUT32(REG_DMACTRL, GET32(REG_DMACTRL) | DMACTRL_UPRXEAREN);
77011717Sgdamore@opensolaris.org 
77111717Sgdamore@opensolaris.org 	PUT_CMD(CMD_IND_ENABLE | INT_WATCHED);
77211717Sgdamore@opensolaris.org 	PUT_CMD(CMD_INT_ENABLE | INT_WATCHED);
77311717Sgdamore@opensolaris.org 
77411717Sgdamore@opensolaris.org 	PUT_CMD(CMD_INT_ACK | 0xff);
77511717Sgdamore@opensolaris.org 
77611717Sgdamore@opensolaris.org 	elxl_set_media(sc);
77711717Sgdamore@opensolaris.org 	elxl_set_rxfilter(sc);
77811717Sgdamore@opensolaris.org 
77911717Sgdamore@opensolaris.org 	/* Configure for VLAN tag sizing. */
78011717Sgdamore@opensolaris.org 	SET_WIN(3);
78111717Sgdamore@opensolaris.org 	if (sc->ex_conf & CONF_90XB) {
78211717Sgdamore@opensolaris.org 		PUT16(W3_MAX_PKT_SIZE, EX_BUFSZ);
78311717Sgdamore@opensolaris.org 	} else {
78411717Sgdamore@opensolaris.org 		PUT16(W3_MAC_CONTROL, GET16(W3_MAC_CONTROL) |
78511717Sgdamore@opensolaris.org 		    MAC_CONTROL_ALLOW_LARGE);
78611717Sgdamore@opensolaris.org 	}
78711717Sgdamore@opensolaris.org 
78811717Sgdamore@opensolaris.org 	PUT_CMD(CMD_SET_RXEARLY | (EX_BUFSZ >> 2));
78911717Sgdamore@opensolaris.org 
79011717Sgdamore@opensolaris.org 	PUT_CMD(CMD_STATS_ENABLE);
79111717Sgdamore@opensolaris.org 	PUT_CMD(CMD_TX_ENABLE);
79211717Sgdamore@opensolaris.org 	PUT32(REG_UPLISTPTR, sc->ex_rxring.r_paddr);
79311717Sgdamore@opensolaris.org 	PUT_CMD(CMD_RX_ENABLE);
79411717Sgdamore@opensolaris.org 	PUT_CMD(CMD_UP_UNSTALL);
79511717Sgdamore@opensolaris.org }
79611717Sgdamore@opensolaris.org 
79711717Sgdamore@opensolaris.org /*
79811717Sgdamore@opensolaris.org  * Set multicast receive filter. Also take care of promiscuous mode.
79911717Sgdamore@opensolaris.org  * Note that *some* of this hardware is fully capable of either a 256
80011717Sgdamore@opensolaris.org  * or 64 bit multicast hash.  However, we can't determine what the
80111717Sgdamore@opensolaris.org  * size of the hash table is easily, and so we are expected to be able
80211717Sgdamore@opensolaris.org  * to resubmit the entire list of addresses each time.  This puts an
80311717Sgdamore@opensolaris.org  * onerous burden on the driver to maintain its list of multicast
80411717Sgdamore@opensolaris.org  * addresses.  Since multicast stuff is usually not that performance
80511717Sgdamore@opensolaris.org  * sensitive, and since we don't usually have much of it, we are just
80611717Sgdamore@opensolaris.org  * going to skip it.  We allow the upper layers to filter it, as
80711717Sgdamore@opensolaris.org  * needed, by setting the all-multicast bit if the hardware can do it.
80811717Sgdamore@opensolaris.org  * This also reduces our test burden.
80911717Sgdamore@opensolaris.org  */
81011717Sgdamore@opensolaris.org static void
elxl_set_rxfilter(elxl_t * sc)81111717Sgdamore@opensolaris.org elxl_set_rxfilter(elxl_t *sc)
81211717Sgdamore@opensolaris.org {
81311717Sgdamore@opensolaris.org 	uint16_t mask = FILTER_UNICAST | FILTER_ALLBCAST;
81411717Sgdamore@opensolaris.org 
81511717Sgdamore@opensolaris.org 	if (sc->ex_suspended)
81611717Sgdamore@opensolaris.org 		return;
81711717Sgdamore@opensolaris.org 
81811717Sgdamore@opensolaris.org 	/*
81911717Sgdamore@opensolaris.org 	 * Set the station address and clear the station mask. The latter
82011717Sgdamore@opensolaris.org 	 * is needed for 90x cards, 0 is the default for 90xB cards.
82111717Sgdamore@opensolaris.org 	 */
82211717Sgdamore@opensolaris.org 	SET_WIN(2);
82311717Sgdamore@opensolaris.org 	for (int i = 0; i < ETHERADDRL; i++) {
82411717Sgdamore@opensolaris.org 		PUT8(W2_STATION_ADDRESS + i, sc->ex_curraddr[i]);
82511717Sgdamore@opensolaris.org 		PUT8(W2_STATION_MASK + i, 0);
82611717Sgdamore@opensolaris.org 	}
82711717Sgdamore@opensolaris.org 
82811717Sgdamore@opensolaris.org 	if (sc->ex_mccount) {
82911717Sgdamore@opensolaris.org 		mask |= FILTER_ALLMULTI;
83011717Sgdamore@opensolaris.org 	}
83111717Sgdamore@opensolaris.org 	if (sc->ex_promisc) {
83211717Sgdamore@opensolaris.org 		mask |= FILTER_PROMISC;
83311717Sgdamore@opensolaris.org 	}
83411717Sgdamore@opensolaris.org 	PUT_CMD(CMD_SET_FILTER | mask);
83511717Sgdamore@opensolaris.org }
83611717Sgdamore@opensolaris.org 
83711717Sgdamore@opensolaris.org static void
elxl_set_media(elxl_t * sc)83811717Sgdamore@opensolaris.org elxl_set_media(elxl_t *sc)
83911717Sgdamore@opensolaris.org {
84011717Sgdamore@opensolaris.org 	uint32_t configreg;
84111717Sgdamore@opensolaris.org 
84211717Sgdamore@opensolaris.org 	SET_WIN(4);
84311717Sgdamore@opensolaris.org 	PUT16(W4_MEDIASTAT, 0);
84411717Sgdamore@opensolaris.org 	PUT_CMD(CMD_BNC_DISABLE);
84511717Sgdamore@opensolaris.org 	drv_usecwait(800);
84611717Sgdamore@opensolaris.org 
84711717Sgdamore@opensolaris.org 	/*
84811717Sgdamore@opensolaris.org 	 * Now turn on the selected media/transceiver.
84911717Sgdamore@opensolaris.org 	 */
85011717Sgdamore@opensolaris.org 	switch (sc->ex_xcvr) {
85111717Sgdamore@opensolaris.org 	case XCVR_SEL_10T:
85211717Sgdamore@opensolaris.org 		sc->ex_mii_active = B_FALSE;
85311717Sgdamore@opensolaris.org 		PUT16(W4_MEDIASTAT,
85411717Sgdamore@opensolaris.org 		    MEDIASTAT_JABGUARD_EN | MEDIASTAT_LINKBEAT_EN);
85511717Sgdamore@opensolaris.org 		drv_usecwait(800);
85611717Sgdamore@opensolaris.org 		break;
85711717Sgdamore@opensolaris.org 
85811717Sgdamore@opensolaris.org 	case XCVR_SEL_BNC:
85911717Sgdamore@opensolaris.org 		sc->ex_mii_active = B_FALSE;
86011717Sgdamore@opensolaris.org 		PUT_CMD(CMD_BNC_ENABLE);
86111717Sgdamore@opensolaris.org 		drv_usecwait(800);
86211717Sgdamore@opensolaris.org 		break;
86311717Sgdamore@opensolaris.org 
86411717Sgdamore@opensolaris.org 	case XCVR_SEL_100FX:
86511717Sgdamore@opensolaris.org 		sc->ex_mii_active = B_FALSE;	/* Is this really true? */
86611717Sgdamore@opensolaris.org 		PUT16(W4_MEDIASTAT, MEDIASTAT_LINKBEAT_EN);
86711717Sgdamore@opensolaris.org 		drv_usecwait(800);
86811717Sgdamore@opensolaris.org 		break;
86911717Sgdamore@opensolaris.org 
87011717Sgdamore@opensolaris.org 	case XCVR_SEL_AUI:
87111717Sgdamore@opensolaris.org 		sc->ex_mii_active = B_FALSE;
87211717Sgdamore@opensolaris.org 		PUT16(W4_MEDIASTAT, MEDIASTAT_SQE_EN);
87311717Sgdamore@opensolaris.org 		drv_usecwait(800);
87411717Sgdamore@opensolaris.org 		break;
87511717Sgdamore@opensolaris.org 
87611717Sgdamore@opensolaris.org 	case XCVR_SEL_AUTO:
87711717Sgdamore@opensolaris.org 	case XCVR_SEL_MII:
87811717Sgdamore@opensolaris.org 		/*
87911717Sgdamore@opensolaris.org 		 * This is due to paranoia.  If a card claims
88011717Sgdamore@opensolaris.org 		 * to default to MII, but doesn't have it set in
88111717Sgdamore@opensolaris.org 		 * media options, then we don't want to leave
88211717Sgdamore@opensolaris.org 		 * the MII active or we'll have problems derferencing
88311717Sgdamore@opensolaris.org 		 * the "mii handle".
88411717Sgdamore@opensolaris.org 		 */
88511717Sgdamore@opensolaris.org 		if (sc->ex_miih) {
88611717Sgdamore@opensolaris.org 			sc->ex_mii_active = B_TRUE;
88711717Sgdamore@opensolaris.org 		} else {
88811717Sgdamore@opensolaris.org 			sc->ex_mii_active = B_FALSE;
88911717Sgdamore@opensolaris.org 		}
89011717Sgdamore@opensolaris.org 		break;
89111717Sgdamore@opensolaris.org 
89211717Sgdamore@opensolaris.org 	default:
89311717Sgdamore@opensolaris.org 		sc->ex_mii_active = B_FALSE;
89411717Sgdamore@opensolaris.org 		elxl_error(sc, "Impossible media setting!");
89511717Sgdamore@opensolaris.org 		break;
89611717Sgdamore@opensolaris.org 	}
89711717Sgdamore@opensolaris.org 
89811717Sgdamore@opensolaris.org 	SET_WIN(3);
89911717Sgdamore@opensolaris.org 	configreg = GET32(W3_INTERNAL_CONFIG);
90011717Sgdamore@opensolaris.org 
90111717Sgdamore@opensolaris.org 	configreg &= ~(XCVR_SEL_MASK);
90211717Sgdamore@opensolaris.org 	configreg |= (sc->ex_xcvr);
90311717Sgdamore@opensolaris.org 
90411717Sgdamore@opensolaris.org 	PUT32(W3_INTERNAL_CONFIG, configreg);
90511717Sgdamore@opensolaris.org 
90611717Sgdamore@opensolaris.org 	/*
90711717Sgdamore@opensolaris.org 	 * If we're not using MII, force the full-duplex setting.  MII
90811717Sgdamore@opensolaris.org 	 * based modes handle the full-duplex setting via the MII
90911717Sgdamore@opensolaris.org 	 * notify callback.
91011717Sgdamore@opensolaris.org 	 */
91111717Sgdamore@opensolaris.org 	if (!sc->ex_mii_active) {
91211717Sgdamore@opensolaris.org 		uint16_t mctl;
91311717Sgdamore@opensolaris.org 		mctl = GET16(W3_MAC_CONTROL);
91411717Sgdamore@opensolaris.org 		if (sc->ex_fdx) {
91511717Sgdamore@opensolaris.org 			mctl |= MAC_CONTROL_FDX;
91611717Sgdamore@opensolaris.org 		} else {
91711717Sgdamore@opensolaris.org 			mctl &= ~MAC_CONTROL_FDX;
91811717Sgdamore@opensolaris.org 		}
91911717Sgdamore@opensolaris.org 		PUT16(W3_MAC_CONTROL, mctl);
92011717Sgdamore@opensolaris.org 	}
92111717Sgdamore@opensolaris.org }
92211717Sgdamore@opensolaris.org 
92311717Sgdamore@opensolaris.org /*
92411717Sgdamore@opensolaris.org  * Get currently-selected media from card.
92511717Sgdamore@opensolaris.org  * (if_media callback, may be called before interface is brought up).
92611717Sgdamore@opensolaris.org  */
92711717Sgdamore@opensolaris.org static void
elxl_linkcheck(void * arg)92811717Sgdamore@opensolaris.org elxl_linkcheck(void *arg)
92911717Sgdamore@opensolaris.org {
93011717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
93111717Sgdamore@opensolaris.org 	uint16_t	stat;
93211717Sgdamore@opensolaris.org 	link_state_t	link;
93311717Sgdamore@opensolaris.org 
93411717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
93511717Sgdamore@opensolaris.org 	if (sc->ex_mii_active) {
93611717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_txlock);
93711717Sgdamore@opensolaris.org 		return;
93811717Sgdamore@opensolaris.org 	}
93911717Sgdamore@opensolaris.org 	if (sc->ex_running && !sc->ex_suspended) {
94011717Sgdamore@opensolaris.org 		switch (sc->ex_xcvr) {
94111717Sgdamore@opensolaris.org 		case XCVR_SEL_100FX:
94211717Sgdamore@opensolaris.org 			/* these media we can detect link on */
94311717Sgdamore@opensolaris.org 			SET_WIN(4);
94411717Sgdamore@opensolaris.org 			stat = GET16(W4_MEDIASTAT);
94511717Sgdamore@opensolaris.org 			if (stat & MEDIASTAT_LINKDETECT) {
94611717Sgdamore@opensolaris.org 				sc->ex_link = LINK_STATE_UP;
94711717Sgdamore@opensolaris.org 				sc->ex_speed = 100000000;
94811717Sgdamore@opensolaris.org 			} else {
94911717Sgdamore@opensolaris.org 				sc->ex_link = LINK_STATE_DOWN;
95011717Sgdamore@opensolaris.org 				sc->ex_speed = 0;
95111717Sgdamore@opensolaris.org 			}
95211717Sgdamore@opensolaris.org 			break;
95311717Sgdamore@opensolaris.org 
95411717Sgdamore@opensolaris.org 		case XCVR_SEL_10T:
95511717Sgdamore@opensolaris.org 			/* these media we can detect link on */
95611717Sgdamore@opensolaris.org 			SET_WIN(4);
95711717Sgdamore@opensolaris.org 			stat = GET16(W4_MEDIASTAT);
95811717Sgdamore@opensolaris.org 			if (stat & MEDIASTAT_LINKDETECT) {
95911717Sgdamore@opensolaris.org 				sc->ex_link = LINK_STATE_UP;
96011717Sgdamore@opensolaris.org 				sc->ex_speed = 10000000;
96111717Sgdamore@opensolaris.org 			} else {
96211717Sgdamore@opensolaris.org 				sc->ex_link = LINK_STATE_DOWN;
96311717Sgdamore@opensolaris.org 				sc->ex_speed = 0;
96411717Sgdamore@opensolaris.org 			}
96511717Sgdamore@opensolaris.org 			break;
96611717Sgdamore@opensolaris.org 
96711717Sgdamore@opensolaris.org 		case XCVR_SEL_BNC:
96811717Sgdamore@opensolaris.org 		case XCVR_SEL_AUI:
96911717Sgdamore@opensolaris.org 		default:
97011717Sgdamore@opensolaris.org 			/*
97111717Sgdamore@opensolaris.org 			 * For these we don't really know the answer,
97211717Sgdamore@opensolaris.org 			 * but if we lie then at least it won't cause
97311717Sgdamore@opensolaris.org 			 * ifconfig to turn off the RUNNING flag.
97411717Sgdamore@opensolaris.org 			 * This is necessary because we might
97511717Sgdamore@opensolaris.org 			 * transition from LINK_STATE_DOWN when
97611717Sgdamore@opensolaris.org 			 * switching media.
97711717Sgdamore@opensolaris.org 			 */
97811717Sgdamore@opensolaris.org 			sc->ex_speed = 10000000;
97911717Sgdamore@opensolaris.org 			sc->ex_link = LINK_STATE_UP;
98011717Sgdamore@opensolaris.org 			break;
98111717Sgdamore@opensolaris.org 		}
98211717Sgdamore@opensolaris.org 		SET_WIN(3);
98311717Sgdamore@opensolaris.org 		sc->ex_duplex = GET16(W3_MAC_CONTROL) & MAC_CONTROL_FDX ?
98411717Sgdamore@opensolaris.org 		    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
98511717Sgdamore@opensolaris.org 	} else {
98611717Sgdamore@opensolaris.org 		sc->ex_speed = 0;
98711717Sgdamore@opensolaris.org 		sc->ex_duplex = LINK_DUPLEX_UNKNOWN;
98811717Sgdamore@opensolaris.org 		sc->ex_link = LINK_STATE_UNKNOWN;
98911717Sgdamore@opensolaris.org 	}
99011717Sgdamore@opensolaris.org 	link = sc->ex_link;
99111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
99211717Sgdamore@opensolaris.org 
99311717Sgdamore@opensolaris.org 	mac_link_update(sc->ex_mach, link);
99411717Sgdamore@opensolaris.org }
99511717Sgdamore@opensolaris.org 
99611717Sgdamore@opensolaris.org static int
elxl_m_promisc(void * arg,boolean_t on)99711717Sgdamore@opensolaris.org elxl_m_promisc(void *arg, boolean_t on)
99811717Sgdamore@opensolaris.org {
99911717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
100011717Sgdamore@opensolaris.org 
100111717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
100211717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
100311717Sgdamore@opensolaris.org 	sc->ex_promisc = on;
100411717Sgdamore@opensolaris.org 	elxl_set_rxfilter(sc);
100511717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
100611717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
100711717Sgdamore@opensolaris.org 	return (0);
100811717Sgdamore@opensolaris.org }
100911717Sgdamore@opensolaris.org 
101011717Sgdamore@opensolaris.org static int
elxl_m_multicst(void * arg,boolean_t add,const uint8_t * addr)101111717Sgdamore@opensolaris.org elxl_m_multicst(void *arg, boolean_t add, const uint8_t *addr)
101211717Sgdamore@opensolaris.org {
101311717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
101411717Sgdamore@opensolaris.org 
101511717Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(addr));
101611717Sgdamore@opensolaris.org 
101711717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
101811717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
101911717Sgdamore@opensolaris.org 	if (add) {
102011717Sgdamore@opensolaris.org 		sc->ex_mccount++;
102111717Sgdamore@opensolaris.org 		if (sc->ex_mccount == 1) {
102211717Sgdamore@opensolaris.org 			elxl_set_rxfilter(sc);
102311717Sgdamore@opensolaris.org 		}
102411717Sgdamore@opensolaris.org 	} else {
102511717Sgdamore@opensolaris.org 		sc->ex_mccount--;
102611717Sgdamore@opensolaris.org 		if (sc->ex_mccount == 0) {
102711717Sgdamore@opensolaris.org 			elxl_set_rxfilter(sc);
102811717Sgdamore@opensolaris.org 		}
102911717Sgdamore@opensolaris.org 	}
103011717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
103111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
103211717Sgdamore@opensolaris.org 	return (0);
103311717Sgdamore@opensolaris.org }
103411717Sgdamore@opensolaris.org 
103511717Sgdamore@opensolaris.org static int
elxl_m_unicst(void * arg,const uint8_t * addr)103611717Sgdamore@opensolaris.org elxl_m_unicst(void *arg, const uint8_t *addr)
103711717Sgdamore@opensolaris.org {
103811717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
103911717Sgdamore@opensolaris.org 
104011717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
104111717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
104211717Sgdamore@opensolaris.org 	bcopy(addr, sc->ex_curraddr, ETHERADDRL);
104311717Sgdamore@opensolaris.org 	elxl_set_rxfilter(sc);
104411717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
104511717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
104611717Sgdamore@opensolaris.org 
104711717Sgdamore@opensolaris.org 	return (0);
104811717Sgdamore@opensolaris.org }
104911717Sgdamore@opensolaris.org 
105011717Sgdamore@opensolaris.org static mblk_t *
elxl_m_tx(void * arg,mblk_t * mp)105111717Sgdamore@opensolaris.org elxl_m_tx(void *arg, mblk_t *mp)
105211717Sgdamore@opensolaris.org {
105311717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
105411717Sgdamore@opensolaris.org 	ex_desc_t	*txd;
105511717Sgdamore@opensolaris.org 	ex_desc_t	*first;
105611717Sgdamore@opensolaris.org 	ex_desc_t	*tail;
105711717Sgdamore@opensolaris.org 	size_t		len;
105811717Sgdamore@opensolaris.org 	ex_ring_t	*r;
105911717Sgdamore@opensolaris.org 	ex_pd_t		*pd;
106011717Sgdamore@opensolaris.org 	uint32_t	cflags;
106111717Sgdamore@opensolaris.org 	mblk_t		*nmp;
106211717Sgdamore@opensolaris.org 	boolean_t	reenable = B_FALSE;
106311717Sgdamore@opensolaris.org 	boolean_t	reset = B_FALSE;
106411717Sgdamore@opensolaris.org 	uint32_t	paddr;
106511717Sgdamore@opensolaris.org 
106611717Sgdamore@opensolaris.org 	r = &sc->ex_txring;
106711717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
106811717Sgdamore@opensolaris.org 	if (sc->ex_suspended) {
106911717Sgdamore@opensolaris.org 		while (mp != NULL) {
107011717Sgdamore@opensolaris.org 			sc->ex_nocarrier++;
107111717Sgdamore@opensolaris.org 			nmp = mp->b_next;
107211717Sgdamore@opensolaris.org 			freemsg(mp);
107311717Sgdamore@opensolaris.org 			mp = nmp;
107411717Sgdamore@opensolaris.org 		}
107511717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_txlock);
107611717Sgdamore@opensolaris.org 		return (NULL);
107711717Sgdamore@opensolaris.org 	}
107811717Sgdamore@opensolaris.org 
107911717Sgdamore@opensolaris.org 	for (int limit = (EX_NTX * 2); limit; limit--) {
108011717Sgdamore@opensolaris.org 		uint8_t stat = GET8(REG_TXSTATUS);
108111717Sgdamore@opensolaris.org 		if ((stat & TXSTATUS_COMPLETE) == 0) {
108211717Sgdamore@opensolaris.org 			break;
108311717Sgdamore@opensolaris.org 		}
108411717Sgdamore@opensolaris.org 		if (stat & TXSTATUS_MAXCOLLISIONS) {
108511717Sgdamore@opensolaris.org 			reenable = B_TRUE;
108611717Sgdamore@opensolaris.org 			sc->ex_excoll++;
108711717Sgdamore@opensolaris.org 		}
108811717Sgdamore@opensolaris.org 		if ((stat & TXSTATUS_ERRS) != 0) {
108911717Sgdamore@opensolaris.org 			reset = B_TRUE;
109011717Sgdamore@opensolaris.org 			if (stat & TXSTATUS_JABBER) {
109111717Sgdamore@opensolaris.org 				sc->ex_jabber++;
109211717Sgdamore@opensolaris.org 			}
109311717Sgdamore@opensolaris.org 			if (stat & TXSTATUS_RECLAIM_ERR) {
109411717Sgdamore@opensolaris.org 				sc->ex_txerr++;
109511717Sgdamore@opensolaris.org 			}
109611717Sgdamore@opensolaris.org 			if (stat & TXSTATUS_UNDERRUN) {
109711717Sgdamore@opensolaris.org 				sc->ex_uflo++;
109811717Sgdamore@opensolaris.org 			}
109911717Sgdamore@opensolaris.org 		}
110011717Sgdamore@opensolaris.org 		PUT8(REG_TXSTATUS, 0);
110111717Sgdamore@opensolaris.org 	}
110211717Sgdamore@opensolaris.org 
110311717Sgdamore@opensolaris.org 	if (reset || reenable) {
110411717Sgdamore@opensolaris.org 		paddr = GET32(REG_DNLISTPTR);
110511717Sgdamore@opensolaris.org 		if (reset) {
110611717Sgdamore@opensolaris.org 			WAIT_CMD(sc);
110711717Sgdamore@opensolaris.org 			PUT_CMD(CMD_TX_RESET);
110811717Sgdamore@opensolaris.org 			WAIT_CMD(sc);
110911717Sgdamore@opensolaris.org 			elxl_setup_tx(sc);
111011717Sgdamore@opensolaris.org 		}
111111717Sgdamore@opensolaris.org 		PUT_CMD(CMD_TX_ENABLE);
111211717Sgdamore@opensolaris.org 		if (paddr) {
111311717Sgdamore@opensolaris.org 			PUT32(REG_DNLISTPTR, paddr);
111411717Sgdamore@opensolaris.org 		}
111511717Sgdamore@opensolaris.org 	}
111611717Sgdamore@opensolaris.org 
111711717Sgdamore@opensolaris.org 	/* first reclaim any free descriptors */
111811717Sgdamore@opensolaris.org 	while (r->r_avail < r->r_count) {
111911717Sgdamore@opensolaris.org 
112011717Sgdamore@opensolaris.org 		paddr = GET32(REG_DNLISTPTR);
112111717Sgdamore@opensolaris.org 		txd = r->r_head;
112211717Sgdamore@opensolaris.org 		if (paddr == txd->ed_descaddr) {
112311717Sgdamore@opensolaris.org 			/* still processing this one, we're done */
112411717Sgdamore@opensolaris.org 			break;
112511717Sgdamore@opensolaris.org 		}
112611717Sgdamore@opensolaris.org 		if (paddr == 0) {
112711717Sgdamore@opensolaris.org 			/* done processing the entire list! */
112811717Sgdamore@opensolaris.org 			r->r_head = NULL;
112911717Sgdamore@opensolaris.org 			r->r_tail = NULL;
113011717Sgdamore@opensolaris.org 			r->r_avail = r->r_count;
113111717Sgdamore@opensolaris.org 			break;
113211717Sgdamore@opensolaris.org 		}
113311717Sgdamore@opensolaris.org 		r->r_avail++;
113411717Sgdamore@opensolaris.org 		r->r_head = txd->ed_next;
113511717Sgdamore@opensolaris.org 	}
113611717Sgdamore@opensolaris.org 
113711717Sgdamore@opensolaris.org 	if ((r->r_avail < r->r_count) && (GET32(REG_DNLISTPTR) != 0)) {
113811717Sgdamore@opensolaris.org 		PUT_CMD(CMD_DN_STALL);
113911717Sgdamore@opensolaris.org 		WAIT_CMD(sc);
114011717Sgdamore@opensolaris.org 	}
114111717Sgdamore@opensolaris.org 
114211717Sgdamore@opensolaris.org 	first = NULL;
114311717Sgdamore@opensolaris.org 	tail = r->r_tail;
114411717Sgdamore@opensolaris.org 
114511717Sgdamore@opensolaris.org 	/*
114611717Sgdamore@opensolaris.org 	 * If there is already a tx list, select the next desc on the list.
114711717Sgdamore@opensolaris.org 	 * Otherwise, just pick the first descriptor.
114811717Sgdamore@opensolaris.org 	 */
114911717Sgdamore@opensolaris.org 	txd = tail ? tail->ed_next : &r->r_desc[0];
115011717Sgdamore@opensolaris.org 
115111717Sgdamore@opensolaris.org 	while ((mp != NULL) && (r->r_avail)) {
115211717Sgdamore@opensolaris.org 
115311717Sgdamore@opensolaris.org 		nmp = mp->b_next;
115411717Sgdamore@opensolaris.org 
115511717Sgdamore@opensolaris.org 		len = msgsize(mp);
115611717Sgdamore@opensolaris.org 		if (len > (ETHERMAX + VLAN_TAGSZ)) {
115711717Sgdamore@opensolaris.org 			sc->ex_txerr++;
115811717Sgdamore@opensolaris.org 			freemsg(mp);
115911717Sgdamore@opensolaris.org 			mp = nmp;
116011717Sgdamore@opensolaris.org 			continue;
116111717Sgdamore@opensolaris.org 		}
116211717Sgdamore@opensolaris.org 
116311717Sgdamore@opensolaris.org 		cflags = 0;
116411717Sgdamore@opensolaris.org 		if ((sc->ex_conf & CONF_90XB) != 0) {
116511717Sgdamore@opensolaris.org 			uint32_t	pflags;
116611717Sgdamore@opensolaris.org 			hcksum_retrieve(mp, NULL, NULL, NULL, NULL, NULL, NULL,
116711717Sgdamore@opensolaris.org 			    &pflags);
116811717Sgdamore@opensolaris.org 			if (pflags & HCK_IPV4_HDRCKSUM) {
116911717Sgdamore@opensolaris.org 				cflags |= EX_DPD_IPCKSUM;
117011717Sgdamore@opensolaris.org 			}
117111717Sgdamore@opensolaris.org 			if (pflags & HCK_FULLCKSUM) {
117211717Sgdamore@opensolaris.org 				cflags |= (EX_DPD_TCPCKSUM | EX_DPD_UDPCKSUM);
117311717Sgdamore@opensolaris.org 			}
117411717Sgdamore@opensolaris.org 		}
117511717Sgdamore@opensolaris.org 
117611717Sgdamore@opensolaris.org 		/* Mark this descriptor is in use.  We're committed now. */
117711717Sgdamore@opensolaris.org 		mcopymsg(mp, txd->ed_buf);	/* frees the mblk! */
117811717Sgdamore@opensolaris.org 		r->r_avail--;
117911717Sgdamore@opensolaris.org 		mp = nmp;
118011717Sgdamore@opensolaris.org 
118111717Sgdamore@opensolaris.org 		/* Accounting stuff. */
118211717Sgdamore@opensolaris.org 		sc->ex_opackets++;
118311717Sgdamore@opensolaris.org 		sc->ex_obytes += len;
118411717Sgdamore@opensolaris.org 		if (txd->ed_buf[0] & 0x1) {
118511717Sgdamore@opensolaris.org 			if (bcmp(txd->ed_buf, ex_broadcast, ETHERADDRL) != 0) {
118611717Sgdamore@opensolaris.org 				sc->ex_multixmt++;
118711717Sgdamore@opensolaris.org 			} else {
118811717Sgdamore@opensolaris.org 				sc->ex_brdcstxmt++;
118911717Sgdamore@opensolaris.org 			}
119011717Sgdamore@opensolaris.org 		}
119111717Sgdamore@opensolaris.org 
119211717Sgdamore@opensolaris.org 		pd = txd->ed_pd;
119311717Sgdamore@opensolaris.org 
119411717Sgdamore@opensolaris.org 
119511717Sgdamore@opensolaris.org 		/*
119611717Sgdamore@opensolaris.org 		 * Zero pad the frame if its too short.  This
119711717Sgdamore@opensolaris.org 		 * also avoids a checksum offload bug.
119811717Sgdamore@opensolaris.org 		 */
119911717Sgdamore@opensolaris.org 		if (len < 30) {
120011717Sgdamore@opensolaris.org 			bzero(txd->ed_buf + len, ETHERMIN - len);
120111717Sgdamore@opensolaris.org 			len = ETHERMIN;
120211717Sgdamore@opensolaris.org 		}
120311717Sgdamore@opensolaris.org 
120411717Sgdamore@opensolaris.org 		/*
120511717Sgdamore@opensolaris.org 		 * If this our first packet so far, record the head
120611717Sgdamore@opensolaris.org 		 * of the list.
120711717Sgdamore@opensolaris.org 		 */
120811717Sgdamore@opensolaris.org 		if (first == NULL) {
120911717Sgdamore@opensolaris.org 			first = txd;
121011717Sgdamore@opensolaris.org 		}
121111717Sgdamore@opensolaris.org 
121211717Sgdamore@opensolaris.org 		(void) ddi_dma_sync(txd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
121311717Sgdamore@opensolaris.org 
121411717Sgdamore@opensolaris.org 		PUT_PD(r, pd->pd_link, 0);
121511717Sgdamore@opensolaris.org 		PUT_PD(r, pd->pd_fsh, len | cflags);
121611717Sgdamore@opensolaris.org 		PUT_PD(r, pd->pd_addr, txd->ed_bufaddr);
121711717Sgdamore@opensolaris.org 		PUT_PD(r, pd->pd_len, len | EX_FR_LAST);
121811717Sgdamore@opensolaris.org 
121911717Sgdamore@opensolaris.org 		/*
122011717Sgdamore@opensolaris.org 		 * Write the link into the previous descriptor.  Note that
122111717Sgdamore@opensolaris.org 		 * if this is the first packet (so no previous queued), this
122211717Sgdamore@opensolaris.org 		 * will be benign because the previous descriptor won't be
122311717Sgdamore@opensolaris.org 		 * on any tx list.  (Furthermore, we'll clear its link field
122411717Sgdamore@opensolaris.org 		 * when we do later use it.)
122511717Sgdamore@opensolaris.org 		 */
122611717Sgdamore@opensolaris.org 		PUT_PD(r, txd->ed_prev->ed_pd->pd_link, txd->ed_descaddr);
122711717Sgdamore@opensolaris.org 	}
122811717Sgdamore@opensolaris.org 
122911717Sgdamore@opensolaris.org 	/*
123011717Sgdamore@opensolaris.org 	 * Are we submitting any packets?
123111717Sgdamore@opensolaris.org 	 */
123211717Sgdamore@opensolaris.org 	if (first != NULL) {
123311717Sgdamore@opensolaris.org 		/* Interrupt on the last packet. */
123411717Sgdamore@opensolaris.org 		PUT_PD(r, pd->pd_fsh, len | cflags | EX_DPD_DNIND);
123511717Sgdamore@opensolaris.org 
123611717Sgdamore@opensolaris.org 		if (tail == NULL) {
123711717Sgdamore@opensolaris.org 			/* No packets pending, so its a new list head! */
123811717Sgdamore@opensolaris.org 			r->r_head = first;
123911717Sgdamore@opensolaris.org 		} else {
124011717Sgdamore@opensolaris.org 			pd = tail->ed_pd;
124111717Sgdamore@opensolaris.org 			/* We've added frames, so don't interrupt mid-list. */
124211717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_fsh,
124311717Sgdamore@opensolaris.org 			    GET_PD(r, pd->pd_fsh) & ~(EX_DPD_DNIND));
124411717Sgdamore@opensolaris.org 		}
124511717Sgdamore@opensolaris.org 		/* Record the last descriptor. */
124611717Sgdamore@opensolaris.org 		r->r_tail = txd;
124711717Sgdamore@opensolaris.org 
124811717Sgdamore@opensolaris.org 		/* flush the entire ring - we're stopped so its safe */
124911717Sgdamore@opensolaris.org 		(void) ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
125011717Sgdamore@opensolaris.org 	}
125111717Sgdamore@opensolaris.org 
125211717Sgdamore@opensolaris.org 	/* Restart transmitter. */
125311717Sgdamore@opensolaris.org 	if (sc->ex_txring.r_head) {
125411717Sgdamore@opensolaris.org 		PUT32(REG_DNLISTPTR, sc->ex_txring.r_head->ed_descaddr);
125511717Sgdamore@opensolaris.org 	}
125611717Sgdamore@opensolaris.org 	PUT_CMD(CMD_DN_UNSTALL);
125711717Sgdamore@opensolaris.org 
125811717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
125911717Sgdamore@opensolaris.org 
126011717Sgdamore@opensolaris.org 	return (mp);
126111717Sgdamore@opensolaris.org }
126211717Sgdamore@opensolaris.org 
126311717Sgdamore@opensolaris.org static mblk_t *
elxl_recv(elxl_t * sc,ex_desc_t * rxd,uint32_t stat)126411717Sgdamore@opensolaris.org elxl_recv(elxl_t *sc, ex_desc_t *rxd, uint32_t stat)
126511717Sgdamore@opensolaris.org {
126611717Sgdamore@opensolaris.org 	mblk_t		*mp = NULL;
126711717Sgdamore@opensolaris.org 	uint32_t	len;
126811717Sgdamore@opensolaris.org 
126911717Sgdamore@opensolaris.org 	len = stat & EX_UPD_PKTLENMASK;
127011717Sgdamore@opensolaris.org 	if (stat & (EX_UPD_ERR_VLAN | EX_UPD_OVERFLOW)) {
127111717Sgdamore@opensolaris.org 		if (stat & EX_UPD_RUNT) {
127211717Sgdamore@opensolaris.org 			sc->ex_runt++;
127311717Sgdamore@opensolaris.org 		}
127411717Sgdamore@opensolaris.org 		if (stat & EX_UPD_OVERRUN) {
127511717Sgdamore@opensolaris.org 			sc->ex_oflo++;
127611717Sgdamore@opensolaris.org 		}
127711717Sgdamore@opensolaris.org 		if (stat & EX_UPD_CRCERR) {
127811717Sgdamore@opensolaris.org 			sc->ex_fcs++;
127911717Sgdamore@opensolaris.org 		}
128011717Sgdamore@opensolaris.org 		if (stat & EX_UPD_ALIGNERR) {
128111717Sgdamore@opensolaris.org 			sc->ex_align++;
128211717Sgdamore@opensolaris.org 		}
128311717Sgdamore@opensolaris.org 		if (stat & EX_UPD_OVERFLOW) {
128411717Sgdamore@opensolaris.org 			sc->ex_toolong++;
128511717Sgdamore@opensolaris.org 		}
128611717Sgdamore@opensolaris.org 		return (NULL);
128711717Sgdamore@opensolaris.org 	}
128811717Sgdamore@opensolaris.org 	if (len < sizeof (struct ether_header)) {
128911717Sgdamore@opensolaris.org 		sc->ex_runt++;
129011717Sgdamore@opensolaris.org 		return (NULL);
129111717Sgdamore@opensolaris.org 	}
129211717Sgdamore@opensolaris.org 	if (len > (ETHERMAX + VLAN_TAGSZ)) {
129311717Sgdamore@opensolaris.org 		/* Allow four bytes for the VLAN header */
129411717Sgdamore@opensolaris.org 		sc->ex_toolong++;
129511717Sgdamore@opensolaris.org 		return (NULL);
129611717Sgdamore@opensolaris.org 	}
129711717Sgdamore@opensolaris.org 	if ((mp = allocb(len + 14, BPRI_HI)) == NULL) {
129811717Sgdamore@opensolaris.org 		sc->ex_allocbfail++;
129911717Sgdamore@opensolaris.org 		return (NULL);
130011717Sgdamore@opensolaris.org 	}
130111717Sgdamore@opensolaris.org 
130211720Sgdamore@opensolaris.org 	(void) ddi_dma_sync(rxd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
130311717Sgdamore@opensolaris.org 	mp->b_rptr += 14;
130411717Sgdamore@opensolaris.org 	mp->b_wptr = mp->b_rptr + len;
130511717Sgdamore@opensolaris.org 	bcopy(rxd->ed_buf, mp->b_rptr, len);
130611717Sgdamore@opensolaris.org 
130711717Sgdamore@opensolaris.org 	sc->ex_ipackets++;
130811717Sgdamore@opensolaris.org 	sc->ex_ibytes += len;
130911717Sgdamore@opensolaris.org 	if (rxd->ed_buf[0] & 0x1) {
131011717Sgdamore@opensolaris.org 		if (bcmp(rxd->ed_buf, ex_broadcast, ETHERADDRL) != 0) {
131111717Sgdamore@opensolaris.org 			sc->ex_multircv++;
131211717Sgdamore@opensolaris.org 		} else {
131311717Sgdamore@opensolaris.org 			sc->ex_brdcstrcv++;
131411717Sgdamore@opensolaris.org 		}
131511717Sgdamore@opensolaris.org 	}
131611717Sgdamore@opensolaris.org 
131711717Sgdamore@opensolaris.org 	/*
131811717Sgdamore@opensolaris.org 	 * Set the incoming checksum information for the packet.
131911717Sgdamore@opensolaris.org 	 */
132011717Sgdamore@opensolaris.org 	if (((sc->ex_conf & CONF_90XB) != 0) &&
132111717Sgdamore@opensolaris.org 	    ((stat & EX_UPD_IPCHECKED) != 0) &&
132211717Sgdamore@opensolaris.org 	    ((stat & (EX_UPD_CKSUMERR)) == 0)) {
132311717Sgdamore@opensolaris.org 		uint32_t	pflags = 0;
132411717Sgdamore@opensolaris.org 		if (stat & EX_UPD_IPCHECKED) {
132511717Sgdamore@opensolaris.org 			pflags |= HCK_IPV4_HDRCKSUM;
132611717Sgdamore@opensolaris.org 		}
132711717Sgdamore@opensolaris.org 		if (stat & (EX_UPD_TCPCHECKED | EX_UPD_UDPCHECKED)) {
132811717Sgdamore@opensolaris.org 			pflags |= (HCK_FULLCKSUM | HCK_FULLCKSUM_OK);
132911717Sgdamore@opensolaris.org 		}
133011717Sgdamore@opensolaris.org 		(void) hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0, pflags, 0);
133111717Sgdamore@opensolaris.org 	}
133211717Sgdamore@opensolaris.org 
133311717Sgdamore@opensolaris.org 	return (mp);
133411717Sgdamore@opensolaris.org }
133511717Sgdamore@opensolaris.org 
133611717Sgdamore@opensolaris.org static int
elxl_m_start(void * arg)133711717Sgdamore@opensolaris.org elxl_m_start(void *arg)
133811717Sgdamore@opensolaris.org {
133911717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
134011717Sgdamore@opensolaris.org 
134111717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
134211717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
134311717Sgdamore@opensolaris.org 
134411717Sgdamore@opensolaris.org 	elxl_init(sc);
134511717Sgdamore@opensolaris.org 	sc->ex_running = B_TRUE;
134611717Sgdamore@opensolaris.org 
134711717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
134811717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
134911717Sgdamore@opensolaris.org 
135011717Sgdamore@opensolaris.org 	if (sc->ex_miih) {
135111717Sgdamore@opensolaris.org 		mii_start(sc->ex_miih);
135211717Sgdamore@opensolaris.org 	}
135311717Sgdamore@opensolaris.org 	return (0);
135411717Sgdamore@opensolaris.org }
135511717Sgdamore@opensolaris.org 
135611717Sgdamore@opensolaris.org static void
elxl_m_stop(void * arg)135711717Sgdamore@opensolaris.org elxl_m_stop(void *arg)
135811717Sgdamore@opensolaris.org {
135911717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
136011717Sgdamore@opensolaris.org 
136111717Sgdamore@opensolaris.org 	if (sc->ex_miih) {
136211717Sgdamore@opensolaris.org 		mii_stop(sc->ex_miih);
136311717Sgdamore@opensolaris.org 	}
136411717Sgdamore@opensolaris.org 
136511717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
136611717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
136711717Sgdamore@opensolaris.org 
136811717Sgdamore@opensolaris.org 	elxl_stop(sc);
136911717Sgdamore@opensolaris.org 	sc->ex_running = B_FALSE;
137011717Sgdamore@opensolaris.org 
137111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
137211717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
137311717Sgdamore@opensolaris.org }
137411717Sgdamore@opensolaris.org 
137511717Sgdamore@opensolaris.org static boolean_t
elxl_m_getcapab(void * arg,mac_capab_t cap,void * data)137611717Sgdamore@opensolaris.org elxl_m_getcapab(void *arg, mac_capab_t cap, void *data)
137711717Sgdamore@opensolaris.org {
137811717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
137911717Sgdamore@opensolaris.org 	switch (cap) {
138011717Sgdamore@opensolaris.org 	case MAC_CAPAB_HCKSUM: {
138111717Sgdamore@opensolaris.org 		uint32_t	*flags = data;
138211717Sgdamore@opensolaris.org 		if (sc->ex_conf & CONF_90XB) {
138311717Sgdamore@opensolaris.org 			*flags = HCKSUM_IPHDRCKSUM | HCKSUM_INET_FULL_V4;
138411717Sgdamore@opensolaris.org 			return (B_TRUE);
138511717Sgdamore@opensolaris.org 		}
138611717Sgdamore@opensolaris.org 		return (B_FALSE);
138711717Sgdamore@opensolaris.org 	}
138811717Sgdamore@opensolaris.org 	default:
138911717Sgdamore@opensolaris.org 		return (B_FALSE);
139011717Sgdamore@opensolaris.org 	}
139111717Sgdamore@opensolaris.org }
139211717Sgdamore@opensolaris.org 
139311717Sgdamore@opensolaris.org static int
elxl_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)1394*11878SVenu.Iyer@Sun.COM elxl_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
1395*11878SVenu.Iyer@Sun.COM     void *val)
139611717Sgdamore@opensolaris.org {
139711717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
139811717Sgdamore@opensolaris.org 	int		rv;
139911717Sgdamore@opensolaris.org 
140011717Sgdamore@opensolaris.org 	if (sc->ex_mii_active) {
1401*11878SVenu.Iyer@Sun.COM 		rv = mii_m_getprop(sc->ex_miih, name, num, sz, val);
140211717Sgdamore@opensolaris.org 		if (rv != ENOTSUP)
140311717Sgdamore@opensolaris.org 			return (rv);
140411717Sgdamore@opensolaris.org 	}
140511717Sgdamore@opensolaris.org 
140611717Sgdamore@opensolaris.org 	switch (num) {
140711717Sgdamore@opensolaris.org 	case MAC_PROP_DUPLEX:
1408*11878SVenu.Iyer@Sun.COM 		*(uint8_t *)val = sc->ex_duplex;
140911717Sgdamore@opensolaris.org 		break;
141011717Sgdamore@opensolaris.org 	case MAC_PROP_SPEED:
141111717Sgdamore@opensolaris.org 		*(uint8_t *)val = sc->ex_speed;
141211717Sgdamore@opensolaris.org 		break;
141311717Sgdamore@opensolaris.org 	case MAC_PROP_STATUS:
141411717Sgdamore@opensolaris.org 		bcopy(&sc->ex_link, val, sizeof (link_state_t));
141511717Sgdamore@opensolaris.org 		break;
141611717Sgdamore@opensolaris.org 
141711717Sgdamore@opensolaris.org 	case MAC_PROP_PRIVATE:
141811717Sgdamore@opensolaris.org 		if (strcmp(name, "_media") == 0) {
141911717Sgdamore@opensolaris.org 			char *str;
142011717Sgdamore@opensolaris.org 
142111717Sgdamore@opensolaris.org 			switch (sc->ex_xcvr) {
142211717Sgdamore@opensolaris.org 			case XCVR_SEL_AUTO:
142311717Sgdamore@opensolaris.org 			case XCVR_SEL_MII:
142411717Sgdamore@opensolaris.org 				str = "mii";
142511717Sgdamore@opensolaris.org 				break;
142611717Sgdamore@opensolaris.org 			case XCVR_SEL_10T:
142711717Sgdamore@opensolaris.org 				str = sc->ex_fdx ? "tp-fdx" : "tp-hdx";
142811717Sgdamore@opensolaris.org 				break;
142911717Sgdamore@opensolaris.org 			case XCVR_SEL_BNC:
143011717Sgdamore@opensolaris.org 				str = "bnc";
143111717Sgdamore@opensolaris.org 				break;
143211717Sgdamore@opensolaris.org 			case XCVR_SEL_AUI:
143311717Sgdamore@opensolaris.org 				if (sc->ex_mediaopt & MEDIAOPT_10FL) {
143411717Sgdamore@opensolaris.org 					str = sc->ex_fdx ? "fl-fdx" : "fl-hdx";
143511717Sgdamore@opensolaris.org 				} else {
143611717Sgdamore@opensolaris.org 					str = "aui";
143711717Sgdamore@opensolaris.org 				}
143811717Sgdamore@opensolaris.org 				break;
143911717Sgdamore@opensolaris.org 			case XCVR_SEL_100FX:
144011717Sgdamore@opensolaris.org 				str = sc->ex_fdx ? "fx-fdx" : "fx-hdx";
144111717Sgdamore@opensolaris.org 				break;
144211717Sgdamore@opensolaris.org 			default:
144311717Sgdamore@opensolaris.org 				str = "unknown";
144411717Sgdamore@opensolaris.org 				break;
144511717Sgdamore@opensolaris.org 			}
144611717Sgdamore@opensolaris.org 			(void) snprintf(val, sz, "%s", str);
144711717Sgdamore@opensolaris.org 			return (0);
144811717Sgdamore@opensolaris.org 		}
144911717Sgdamore@opensolaris.org 		/*
145011717Sgdamore@opensolaris.org 		 * This available media property is a hack, and should
145111717Sgdamore@opensolaris.org 		 * be removed when we can provide proper support for
145211717Sgdamore@opensolaris.org 		 * querying it as proposed in PSARC 2009/235.  (At the
145311717Sgdamore@opensolaris.org 		 * moment the implementation lacks support for using
145411717Sgdamore@opensolaris.org 		 * MAC_PROP_POSSIBLE with private properties.)
145511717Sgdamore@opensolaris.org 		 */
145611717Sgdamore@opensolaris.org 		if (strcmp(name, "_available_media") == 0) {
145711717Sgdamore@opensolaris.org 			(void) snprintf(val, sz, "%s", sc->ex_medias);
145811717Sgdamore@opensolaris.org 			return (0);
145911717Sgdamore@opensolaris.org 		}
146011717Sgdamore@opensolaris.org 		break;
146111717Sgdamore@opensolaris.org 	}
146211717Sgdamore@opensolaris.org 	return (ENOTSUP);
146311717Sgdamore@opensolaris.org }
146411717Sgdamore@opensolaris.org 
146511717Sgdamore@opensolaris.org static int
elxl_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)146611717Sgdamore@opensolaris.org elxl_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
146711717Sgdamore@opensolaris.org     const void *val)
146811717Sgdamore@opensolaris.org {
146911717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
147011717Sgdamore@opensolaris.org 	int		rv;
147111717Sgdamore@opensolaris.org 
147211717Sgdamore@opensolaris.org 	if (sc->ex_mii_active) {
147311717Sgdamore@opensolaris.org 		rv = mii_m_setprop(sc->ex_miih, name, num, sz, val);
147411717Sgdamore@opensolaris.org 		if (rv != ENOTSUP) {
147511717Sgdamore@opensolaris.org 			return (rv);
147611717Sgdamore@opensolaris.org 		}
147711717Sgdamore@opensolaris.org 	}
147811717Sgdamore@opensolaris.org 	switch (num) {
147911717Sgdamore@opensolaris.org 
148011717Sgdamore@opensolaris.org 	case MAC_PROP_PRIVATE:
148111717Sgdamore@opensolaris.org 		if (strcmp(name, "_media") == 0) {
148211717Sgdamore@opensolaris.org 			uint32_t mopt = sc->ex_mediaopt;
148311717Sgdamore@opensolaris.org 
148411717Sgdamore@opensolaris.org 			if (strcmp(val, "mii") == 0) {
148511717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_100TX) {
148611717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_AUTO;
148711717Sgdamore@opensolaris.org 				} else if (mopt & MEDIAOPT_MII)  {
148811717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_MII;
148911717Sgdamore@opensolaris.org 				} else {
149011717Sgdamore@opensolaris.org 					return (EINVAL);
149111717Sgdamore@opensolaris.org 				}
149211717Sgdamore@opensolaris.org 			} else if (strcmp(val, "tp-fdx") == 0) {
149311717Sgdamore@opensolaris.org 				/* select media option */
149411717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_10T) {
149511717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_10T;
149611717Sgdamore@opensolaris.org 					sc->ex_fdx = B_TRUE;
149711717Sgdamore@opensolaris.org 				} else {
149811717Sgdamore@opensolaris.org 					return (EINVAL);
149911717Sgdamore@opensolaris.org 				}
150011717Sgdamore@opensolaris.org 			} else if (strcmp(val, "tp-hdx") == 0) {
150111717Sgdamore@opensolaris.org 				/* select media option */
150211717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_10T) {
150311717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_10T;
150411717Sgdamore@opensolaris.org 					sc->ex_fdx = B_FALSE;
150511717Sgdamore@opensolaris.org 				} else {
150611717Sgdamore@opensolaris.org 					return (EINVAL);
150711717Sgdamore@opensolaris.org 				}
150811717Sgdamore@opensolaris.org 			} else if (strcmp(val, "fx-fdx") == 0) {
150911717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_100FX) {
151011717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_100FX;
151111717Sgdamore@opensolaris.org 					sc->ex_fdx = B_TRUE;
151211717Sgdamore@opensolaris.org 				} else {
151311717Sgdamore@opensolaris.org 					return (EINVAL);
151411717Sgdamore@opensolaris.org 				}
151511717Sgdamore@opensolaris.org 			} else if (strcmp(val, "fx-hdx") == 0) {
151611717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_100FX) {
151711717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_100FX;
151811717Sgdamore@opensolaris.org 					sc->ex_fdx = B_FALSE;
151911717Sgdamore@opensolaris.org 				} else {
152011717Sgdamore@opensolaris.org 					return (EINVAL);
152111717Sgdamore@opensolaris.org 				}
152211717Sgdamore@opensolaris.org 			} else if (strcmp(val, "bnc") == 0) {
152311717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_BNC) {
152411717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_BNC;
152511717Sgdamore@opensolaris.org 					sc->ex_fdx = B_FALSE;
152611717Sgdamore@opensolaris.org 				} else {
152711717Sgdamore@opensolaris.org 					return (EINVAL);
152811717Sgdamore@opensolaris.org 				}
152911717Sgdamore@opensolaris.org 			} else if (strcmp(val, "aui") == 0) {
153011717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_AUI) {
153111717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_AUI;
153211717Sgdamore@opensolaris.org 					sc->ex_fdx = B_FALSE;
153311717Sgdamore@opensolaris.org 				} else {
153411717Sgdamore@opensolaris.org 					return (EINVAL);
153511717Sgdamore@opensolaris.org 				}
153611717Sgdamore@opensolaris.org 			} else if (strcmp(val, "fl-fdx") == 0) {
153711717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_10FL) {
153811717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_AUI;
153911717Sgdamore@opensolaris.org 					sc->ex_fdx = B_TRUE;
154011717Sgdamore@opensolaris.org 				} else {
154111717Sgdamore@opensolaris.org 					return (EINVAL);
154211717Sgdamore@opensolaris.org 				}
154311717Sgdamore@opensolaris.org 			} else if (strcmp(val, "fl-hdx") == 0) {
154411717Sgdamore@opensolaris.org 				if (mopt & MEDIAOPT_10FL) {
154511717Sgdamore@opensolaris.org 					sc->ex_xcvr = XCVR_SEL_AUI;
154611717Sgdamore@opensolaris.org 					sc->ex_fdx = B_FALSE;
154711717Sgdamore@opensolaris.org 				} else {
154811717Sgdamore@opensolaris.org 					return (EINVAL);
154911717Sgdamore@opensolaris.org 				}
155011717Sgdamore@opensolaris.org 
155111717Sgdamore@opensolaris.org 			} else {
155211717Sgdamore@opensolaris.org 				return (EINVAL);
155311717Sgdamore@opensolaris.org 			}
155411717Sgdamore@opensolaris.org 			goto reset;
155511717Sgdamore@opensolaris.org 		}
155611717Sgdamore@opensolaris.org 		break;
155711717Sgdamore@opensolaris.org 	default:
155811717Sgdamore@opensolaris.org 		break;
155911717Sgdamore@opensolaris.org 	}
156011717Sgdamore@opensolaris.org 
156111717Sgdamore@opensolaris.org 	return (ENOTSUP);
156211717Sgdamore@opensolaris.org 
156311717Sgdamore@opensolaris.org reset:
156411717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
156511717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
156611717Sgdamore@opensolaris.org 	if (!sc->ex_suspended) {
156711717Sgdamore@opensolaris.org 		elxl_reset(sc);
156811717Sgdamore@opensolaris.org 		if (sc->ex_running) {
156911717Sgdamore@opensolaris.org 			elxl_init(sc);
157011717Sgdamore@opensolaris.org 		}
157111717Sgdamore@opensolaris.org 	}
157211717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
157311717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
157411717Sgdamore@opensolaris.org 	return (0);
157511717Sgdamore@opensolaris.org }
157611717Sgdamore@opensolaris.org 
1577*11878SVenu.Iyer@Sun.COM static void
elxl_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t prh)1578*11878SVenu.Iyer@Sun.COM elxl_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
1579*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
1580*11878SVenu.Iyer@Sun.COM {
1581*11878SVenu.Iyer@Sun.COM 	elxl_t		*sc = arg;
1582*11878SVenu.Iyer@Sun.COM 
1583*11878SVenu.Iyer@Sun.COM 	if (sc->ex_mii_active)
1584*11878SVenu.Iyer@Sun.COM 		mii_m_propinfo(sc->ex_miih, name, num, prh);
1585*11878SVenu.Iyer@Sun.COM 
1586*11878SVenu.Iyer@Sun.COM 	switch (num) {
1587*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_DUPLEX:
1588*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_SPEED:
1589*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_STATUS:
1590*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1591*11878SVenu.Iyer@Sun.COM 		break;
1592*11878SVenu.Iyer@Sun.COM 
1593*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_PRIVATE:
1594*11878SVenu.Iyer@Sun.COM 		if (strcmp(name, "_available_media") == 0)
1595*11878SVenu.Iyer@Sun.COM 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1596*11878SVenu.Iyer@Sun.COM 		break;
1597*11878SVenu.Iyer@Sun.COM 	}
1598*11878SVenu.Iyer@Sun.COM }
1599*11878SVenu.Iyer@Sun.COM 
160011717Sgdamore@opensolaris.org static int
elxl_m_stat(void * arg,uint_t stat,uint64_t * val)160111717Sgdamore@opensolaris.org elxl_m_stat(void *arg, uint_t stat, uint64_t *val)
160211717Sgdamore@opensolaris.org {
160311717Sgdamore@opensolaris.org 	elxl_t	*sc = arg;
160411717Sgdamore@opensolaris.org 
160511717Sgdamore@opensolaris.org 	if (stat == MAC_STAT_IFSPEED) {
160611717Sgdamore@opensolaris.org 		elxl_getstats(sc);
160711717Sgdamore@opensolaris.org 	}
160811717Sgdamore@opensolaris.org 
160911717Sgdamore@opensolaris.org 	if ((sc->ex_mii_active) &&
161011717Sgdamore@opensolaris.org 	    (mii_m_getstat(sc->ex_miih, stat, val) == 0)) {
161111717Sgdamore@opensolaris.org 		return (0);
161211717Sgdamore@opensolaris.org 	}
161311717Sgdamore@opensolaris.org 
161411717Sgdamore@opensolaris.org 	switch (stat) {
161511717Sgdamore@opensolaris.org 	case MAC_STAT_IFSPEED:
161611717Sgdamore@opensolaris.org 		*val = sc->ex_speed;
161711717Sgdamore@opensolaris.org 		break;
161811717Sgdamore@opensolaris.org 
161911717Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_DUPLEX:
162011717Sgdamore@opensolaris.org 		*val = sc->ex_duplex;
162111717Sgdamore@opensolaris.org 		break;
162211717Sgdamore@opensolaris.org 
162311717Sgdamore@opensolaris.org 	case MAC_STAT_MULTIRCV:
162411717Sgdamore@opensolaris.org 		*val = sc->ex_multircv;
162511717Sgdamore@opensolaris.org 		break;
162611717Sgdamore@opensolaris.org 
162711717Sgdamore@opensolaris.org 	case MAC_STAT_BRDCSTRCV:
162811717Sgdamore@opensolaris.org 		*val = sc->ex_brdcstrcv;
162911717Sgdamore@opensolaris.org 		break;
163011717Sgdamore@opensolaris.org 
163111717Sgdamore@opensolaris.org 	case MAC_STAT_MULTIXMT:
163211717Sgdamore@opensolaris.org 		*val = sc->ex_multixmt;
163311717Sgdamore@opensolaris.org 		break;
163411717Sgdamore@opensolaris.org 
163511717Sgdamore@opensolaris.org 	case MAC_STAT_BRDCSTXMT:
163611717Sgdamore@opensolaris.org 		*val = sc->ex_brdcstxmt;
163711717Sgdamore@opensolaris.org 		break;
163811717Sgdamore@opensolaris.org 
163911717Sgdamore@opensolaris.org 	case MAC_STAT_IPACKETS:
164011717Sgdamore@opensolaris.org 		*val = sc->ex_ipackets;
164111717Sgdamore@opensolaris.org 		break;
164211717Sgdamore@opensolaris.org 
164311717Sgdamore@opensolaris.org 	case MAC_STAT_OPACKETS:
164411717Sgdamore@opensolaris.org 		*val = sc->ex_opackets;
164511717Sgdamore@opensolaris.org 		break;
164611717Sgdamore@opensolaris.org 
164711717Sgdamore@opensolaris.org 	case MAC_STAT_RBYTES:
164811717Sgdamore@opensolaris.org 		*val = sc->ex_ibytes;
164911717Sgdamore@opensolaris.org 		break;
165011717Sgdamore@opensolaris.org 	case MAC_STAT_OBYTES:
165111717Sgdamore@opensolaris.org 		*val = sc->ex_obytes;
165211717Sgdamore@opensolaris.org 		break;
165311717Sgdamore@opensolaris.org 
165411717Sgdamore@opensolaris.org 	case MAC_STAT_COLLISIONS:
165511717Sgdamore@opensolaris.org 	case ETHER_STAT_FIRST_COLLISIONS:
165611717Sgdamore@opensolaris.org 		*val = sc->ex_singlecol + sc->ex_multcol;
165711717Sgdamore@opensolaris.org 		break;
165811717Sgdamore@opensolaris.org 
165911717Sgdamore@opensolaris.org 	case ETHER_STAT_MULTI_COLLISIONS:
166011717Sgdamore@opensolaris.org 		*val = sc->ex_multcol;
166111717Sgdamore@opensolaris.org 		break;
166211717Sgdamore@opensolaris.org 
166311717Sgdamore@opensolaris.org 	case ETHER_STAT_TX_LATE_COLLISIONS:
166411717Sgdamore@opensolaris.org 		*val = sc->ex_latecol;
166511717Sgdamore@opensolaris.org 		break;
166611717Sgdamore@opensolaris.org 
166711717Sgdamore@opensolaris.org 	case ETHER_STAT_ALIGN_ERRORS:
166811717Sgdamore@opensolaris.org 		*val = sc->ex_align;
166911717Sgdamore@opensolaris.org 		break;
167011717Sgdamore@opensolaris.org 
167111717Sgdamore@opensolaris.org 	case ETHER_STAT_FCS_ERRORS:
167211717Sgdamore@opensolaris.org 		*val = sc->ex_fcs;
167311717Sgdamore@opensolaris.org 		break;
167411717Sgdamore@opensolaris.org 
167511717Sgdamore@opensolaris.org 	case ETHER_STAT_SQE_ERRORS:
167611717Sgdamore@opensolaris.org 		*val = sc->ex_sqe;
167711717Sgdamore@opensolaris.org 		break;
167811717Sgdamore@opensolaris.org 
167911717Sgdamore@opensolaris.org 	case ETHER_STAT_DEFER_XMTS:
168011717Sgdamore@opensolaris.org 		*val = sc->ex_defer;
168111717Sgdamore@opensolaris.org 		break;
168211717Sgdamore@opensolaris.org 
168311717Sgdamore@opensolaris.org 	case ETHER_STAT_CARRIER_ERRORS:
168411717Sgdamore@opensolaris.org 		*val = sc->ex_nocarrier;
168511717Sgdamore@opensolaris.org 		break;
168611717Sgdamore@opensolaris.org 
168711717Sgdamore@opensolaris.org 	case ETHER_STAT_TOOLONG_ERRORS:
168811717Sgdamore@opensolaris.org 		*val = sc->ex_toolong;
168911717Sgdamore@opensolaris.org 		break;
169011717Sgdamore@opensolaris.org 
169111717Sgdamore@opensolaris.org 	case ETHER_STAT_EX_COLLISIONS:
169211717Sgdamore@opensolaris.org 		*val = sc->ex_excoll;
169311717Sgdamore@opensolaris.org 		break;
169411717Sgdamore@opensolaris.org 
169511717Sgdamore@opensolaris.org 	case MAC_STAT_OVERFLOWS:
169611717Sgdamore@opensolaris.org 		*val = sc->ex_oflo;
169711717Sgdamore@opensolaris.org 		break;
169811717Sgdamore@opensolaris.org 
169911717Sgdamore@opensolaris.org 	case MAC_STAT_UNDERFLOWS:
170011717Sgdamore@opensolaris.org 		*val = sc->ex_uflo;
170111717Sgdamore@opensolaris.org 		break;
170211717Sgdamore@opensolaris.org 
170311717Sgdamore@opensolaris.org 	case ETHER_STAT_TOOSHORT_ERRORS:
170411717Sgdamore@opensolaris.org 		*val = sc->ex_runt;
170511717Sgdamore@opensolaris.org 		break;
170611717Sgdamore@opensolaris.org 
170711717Sgdamore@opensolaris.org 	case ETHER_STAT_JABBER_ERRORS:
170811717Sgdamore@opensolaris.org 		*val = sc->ex_jabber;
170911717Sgdamore@opensolaris.org 		break;
171011717Sgdamore@opensolaris.org 
171111717Sgdamore@opensolaris.org 	case MAC_STAT_NORCVBUF:
171211717Sgdamore@opensolaris.org 		*val = sc->ex_allocbfail;
171311717Sgdamore@opensolaris.org 		break;
171411717Sgdamore@opensolaris.org 
171511717Sgdamore@opensolaris.org 	case MAC_STAT_OERRORS:
171611717Sgdamore@opensolaris.org 		*val = sc->ex_jabber + sc->ex_latecol + sc->ex_uflo;
171711717Sgdamore@opensolaris.org 		break;
171811717Sgdamore@opensolaris.org 
171911717Sgdamore@opensolaris.org 	case MAC_STAT_IERRORS:
172011717Sgdamore@opensolaris.org 		*val = sc->ex_align + sc->ex_fcs + sc->ex_runt +
172111717Sgdamore@opensolaris.org 		    sc->ex_toolong + sc->ex_oflo + sc->ex_allocbfail;
172211717Sgdamore@opensolaris.org 		break;
172311717Sgdamore@opensolaris.org 
172411717Sgdamore@opensolaris.org 	default:
172511717Sgdamore@opensolaris.org 		return (ENOTSUP);
172611717Sgdamore@opensolaris.org 	}
172711717Sgdamore@opensolaris.org 	return (0);
172811717Sgdamore@opensolaris.org }
172911717Sgdamore@opensolaris.org 
173011717Sgdamore@opensolaris.org static uint_t
elxl_intr(caddr_t arg,caddr_t dontcare)173111717Sgdamore@opensolaris.org elxl_intr(caddr_t arg, caddr_t dontcare)
173211717Sgdamore@opensolaris.org {
173311717Sgdamore@opensolaris.org 	elxl_t		*sc = (void *)arg;
173411717Sgdamore@opensolaris.org 	uint16_t	stat;
173511717Sgdamore@opensolaris.org 	mblk_t		*mphead = NULL;
173611717Sgdamore@opensolaris.org 	mblk_t		**mpp = &mphead;
173711717Sgdamore@opensolaris.org 
173811717Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(dontcare));
173911717Sgdamore@opensolaris.org 
174011717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
174111717Sgdamore@opensolaris.org 	if (sc->ex_suspended) {
174211717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_intrlock);
174311717Sgdamore@opensolaris.org 		return (DDI_INTR_UNCLAIMED);
174411717Sgdamore@opensolaris.org 	}
174511717Sgdamore@opensolaris.org 
174611717Sgdamore@opensolaris.org 	stat = GET16(REG_CMD_STAT);
174711717Sgdamore@opensolaris.org 
174811717Sgdamore@opensolaris.org 	if ((stat & INT_LATCH) == 0)  {
174911717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_intrlock);
175011717Sgdamore@opensolaris.org 		return (DDI_INTR_UNCLAIMED);
175111717Sgdamore@opensolaris.org 	}
175211717Sgdamore@opensolaris.org 
175311717Sgdamore@opensolaris.org 	/*
175411717Sgdamore@opensolaris.org 	 * Acknowledge interrupts.
175511717Sgdamore@opensolaris.org 	 */
175611717Sgdamore@opensolaris.org 	PUT_CMD(CMD_INT_ACK | (stat & INT_WATCHED) | INT_LATCH);
175711717Sgdamore@opensolaris.org 
175811717Sgdamore@opensolaris.org 	if (stat & INT_HOST_ERROR) {
175911717Sgdamore@opensolaris.org 		/* XXX: Potentially a good spot for FMA */
176011717Sgdamore@opensolaris.org 		elxl_error(sc, "Adapter failure (%x)", stat);
176111717Sgdamore@opensolaris.org 		mutex_enter(&sc->ex_txlock);
176211717Sgdamore@opensolaris.org 		elxl_reset(sc);
176311717Sgdamore@opensolaris.org 		if (sc->ex_running)
176411717Sgdamore@opensolaris.org 			elxl_init(sc);
176511717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_txlock);
176611717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_intrlock);
176711717Sgdamore@opensolaris.org 		return (DDI_INTR_CLAIMED);
176811717Sgdamore@opensolaris.org 	}
176911717Sgdamore@opensolaris.org 	if (stat & INT_UP_COMPLETE) {
177011717Sgdamore@opensolaris.org 		ex_ring_t		*r;
177111717Sgdamore@opensolaris.org 		ex_desc_t		*rxd;
177211717Sgdamore@opensolaris.org 		ex_pd_t			*pd;
177311717Sgdamore@opensolaris.org 		mblk_t			*mp;
177411717Sgdamore@opensolaris.org 		uint32_t		pktstat;
177511717Sgdamore@opensolaris.org 
177611717Sgdamore@opensolaris.org 		r = &sc->ex_rxring;
177711717Sgdamore@opensolaris.org 
177811717Sgdamore@opensolaris.org 		for (;;) {
177911717Sgdamore@opensolaris.org 			rxd = r->r_head;
178011717Sgdamore@opensolaris.org 			pd = rxd->ed_pd;
178111717Sgdamore@opensolaris.org 
178211720Sgdamore@opensolaris.org 			(void) ddi_dma_sync(r->r_dmah, rxd->ed_off,
178311717Sgdamore@opensolaris.org 			    sizeof (ex_pd_t), DDI_DMA_SYNC_FORKERNEL);
178411717Sgdamore@opensolaris.org 
178511717Sgdamore@opensolaris.org 			pktstat = GET_PD(r, pd->pd_status);
178611717Sgdamore@opensolaris.org 
178711717Sgdamore@opensolaris.org 			if ((pktstat & EX_UPD_COMPLETE) == 0) {
178811717Sgdamore@opensolaris.org 				break;
178911717Sgdamore@opensolaris.org 			}
179011717Sgdamore@opensolaris.org 
179111717Sgdamore@opensolaris.org 			/* Advance head to next packet. */
179211717Sgdamore@opensolaris.org 			r->r_head = r->r_head->ed_next;
179311717Sgdamore@opensolaris.org 
179411717Sgdamore@opensolaris.org 			if ((mp = elxl_recv(sc, rxd, pktstat)) != NULL) {
179511717Sgdamore@opensolaris.org 				*mpp = mp;
179611717Sgdamore@opensolaris.org 				mpp = &mp->b_next;
179711717Sgdamore@opensolaris.org 			}
179811717Sgdamore@opensolaris.org 
179911717Sgdamore@opensolaris.org 			/* clear the upComplete status, reset other fields */
180011717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_status, 0);
180111717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST);
180211717Sgdamore@opensolaris.org 			PUT_PD(r, pd->pd_addr, rxd->ed_bufaddr);
180311720Sgdamore@opensolaris.org 			(void) ddi_dma_sync(r->r_dmah, rxd->ed_off,
180411717Sgdamore@opensolaris.org 			    sizeof (ex_pd_t), DDI_DMA_SYNC_FORDEV);
180511717Sgdamore@opensolaris.org 		}
180611717Sgdamore@opensolaris.org 
180711717Sgdamore@opensolaris.org 		/*
180811717Sgdamore@opensolaris.org 		 * If the engine stalled processing (due to
180911717Sgdamore@opensolaris.org 		 * insufficient UPDs usually), restart it.
181011717Sgdamore@opensolaris.org 		 */
181111717Sgdamore@opensolaris.org 		if (GET32(REG_UPLISTPTR) == 0) {
181211717Sgdamore@opensolaris.org 			/*
181311717Sgdamore@opensolaris.org 			 * This seems that it can happen in an RX overrun
181411717Sgdamore@opensolaris.org 			 * situation.
181511717Sgdamore@opensolaris.org 			 */
181611717Sgdamore@opensolaris.org 			mutex_enter(&sc->ex_txlock);
181711717Sgdamore@opensolaris.org 			if (sc->ex_running)
181811717Sgdamore@opensolaris.org 				elxl_init(sc);
181911717Sgdamore@opensolaris.org 			mutex_exit(&sc->ex_txlock);
182011717Sgdamore@opensolaris.org 		}
182111717Sgdamore@opensolaris.org 		PUT_CMD(CMD_UP_UNSTALL);
182211717Sgdamore@opensolaris.org 	}
182311717Sgdamore@opensolaris.org 
182411717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
182511717Sgdamore@opensolaris.org 
182611717Sgdamore@opensolaris.org 	if (mphead) {
182711717Sgdamore@opensolaris.org 		mac_rx(sc->ex_mach, NULL, mphead);
182811717Sgdamore@opensolaris.org 	}
182911717Sgdamore@opensolaris.org 	if (stat & INT_STATS) {
183011717Sgdamore@opensolaris.org 		elxl_getstats(sc);
183111717Sgdamore@opensolaris.org 	}
183211717Sgdamore@opensolaris.org 	if (stat & INT_DN_COMPLETE) {
183311717Sgdamore@opensolaris.org 		mac_tx_update(sc->ex_mach);
183411717Sgdamore@opensolaris.org 	}
183511717Sgdamore@opensolaris.org 
183611717Sgdamore@opensolaris.org 	return (DDI_INTR_CLAIMED);
183711717Sgdamore@opensolaris.org }
183811717Sgdamore@opensolaris.org 
183911717Sgdamore@opensolaris.org static void
elxl_getstats(elxl_t * sc)184011717Sgdamore@opensolaris.org elxl_getstats(elxl_t *sc)
184111717Sgdamore@opensolaris.org {
184211717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
184311717Sgdamore@opensolaris.org 	if (sc->ex_suspended) {
184411717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_txlock);
184511717Sgdamore@opensolaris.org 		return;
184611717Sgdamore@opensolaris.org 	}
184711717Sgdamore@opensolaris.org 
184811717Sgdamore@opensolaris.org 	SET_WIN(6);
184911717Sgdamore@opensolaris.org 	/*
185011717Sgdamore@opensolaris.org 	 * We count the packets and bytes elsewhere, but we need to
185111717Sgdamore@opensolaris.org 	 * read the registers to clear them.
185211717Sgdamore@opensolaris.org 	 */
185311717Sgdamore@opensolaris.org 	(void) GET8(W6_RX_FRAMES);
185411717Sgdamore@opensolaris.org 	(void) GET8(W6_TX_FRAMES);
185511717Sgdamore@opensolaris.org 	(void) GET8(W6_UPPER_FRAMES);
185611717Sgdamore@opensolaris.org 	(void) GET8(W6_RX_OVERRUNS);	/* counted by elxl_recv */
185711717Sgdamore@opensolaris.org 	(void) GET16(W6_RX_BYTES);
185811717Sgdamore@opensolaris.org 	(void) GET16(W6_TX_BYTES);
185911717Sgdamore@opensolaris.org 
186011717Sgdamore@opensolaris.org 	sc->ex_defer += GET8(W6_DEFER);
186111717Sgdamore@opensolaris.org 	sc->ex_latecol += GET8(W6_TX_LATE_COL);
186211717Sgdamore@opensolaris.org 	sc->ex_singlecol += GET8(W6_SINGLE_COL);
186311717Sgdamore@opensolaris.org 	sc->ex_multcol += GET8(W6_MULT_COL);
186411717Sgdamore@opensolaris.org 	sc->ex_sqe += GET8(W6_SQE_ERRORS);
186511717Sgdamore@opensolaris.org 	sc->ex_nocarrier += GET8(W6_NO_CARRIER);
186611717Sgdamore@opensolaris.org 
186711717Sgdamore@opensolaris.org 	SET_WIN(4);
186811717Sgdamore@opensolaris.org 	/* Note: we ought to report this somewhere... */
186911717Sgdamore@opensolaris.org 	(void) GET8(W4_BADSSD);
187011717Sgdamore@opensolaris.org 
187111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
187211717Sgdamore@opensolaris.org }
187311717Sgdamore@opensolaris.org 
187411717Sgdamore@opensolaris.org static void
elxl_reset(elxl_t * sc)187511717Sgdamore@opensolaris.org elxl_reset(elxl_t *sc)
187611717Sgdamore@opensolaris.org {
187711717Sgdamore@opensolaris.org 	PUT_CMD(CMD_GLOBAL_RESET);
187811717Sgdamore@opensolaris.org 	/*
187911717Sgdamore@opensolaris.org 	 * Some ASICs need a longer time (20 ms) to come properly out
188011717Sgdamore@opensolaris.org 	 * of reset.  Do not reduce this value.
188111717Sgdamore@opensolaris.org 	 *
188211717Sgdamore@opensolaris.org 	 * Note that this occurs only during attach and failure recovery,
188311717Sgdamore@opensolaris.org 	 * so it should be mostly harmless.
188411717Sgdamore@opensolaris.org 	 */
188511717Sgdamore@opensolaris.org 	drv_usecwait(20000);
188611717Sgdamore@opensolaris.org 	WAIT_CMD(sc);
188711717Sgdamore@opensolaris.org }
188811717Sgdamore@opensolaris.org 
188911717Sgdamore@opensolaris.org static void
elxl_stop(elxl_t * sc)189011717Sgdamore@opensolaris.org elxl_stop(elxl_t *sc)
189111717Sgdamore@opensolaris.org {
189211717Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&sc->ex_intrlock));
189311717Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&sc->ex_txlock));
189411717Sgdamore@opensolaris.org 
189511717Sgdamore@opensolaris.org 	if (sc->ex_suspended)
189611717Sgdamore@opensolaris.org 		return;
189711717Sgdamore@opensolaris.org 
189811717Sgdamore@opensolaris.org 	PUT_CMD(CMD_RX_DISABLE);
189911717Sgdamore@opensolaris.org 	PUT_CMD(CMD_TX_DISABLE);
190011717Sgdamore@opensolaris.org 	PUT_CMD(CMD_BNC_DISABLE);
190111717Sgdamore@opensolaris.org 
190211717Sgdamore@opensolaris.org 	elxl_reset_ring(&sc->ex_rxring, DDI_DMA_READ);
190311717Sgdamore@opensolaris.org 	elxl_reset_ring(&sc->ex_txring, DDI_DMA_WRITE);
190411717Sgdamore@opensolaris.org 
190511717Sgdamore@opensolaris.org 	PUT_CMD(CMD_INT_ACK | INT_LATCH);
190611717Sgdamore@opensolaris.org 	/* Disable all interrupts. (0 means "none".) */
190711717Sgdamore@opensolaris.org 	PUT_CMD(CMD_INT_ENABLE | 0);
190811717Sgdamore@opensolaris.org }
190911717Sgdamore@opensolaris.org 
191011717Sgdamore@opensolaris.org static void
elxl_suspend(elxl_t * sc)191111717Sgdamore@opensolaris.org elxl_suspend(elxl_t *sc)
191211717Sgdamore@opensolaris.org {
191311717Sgdamore@opensolaris.org 	if (sc->ex_miih) {
191411717Sgdamore@opensolaris.org 		mii_suspend(sc->ex_miih);
191511717Sgdamore@opensolaris.org 	}
191611717Sgdamore@opensolaris.org 
191711717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
191811717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
191911717Sgdamore@opensolaris.org 	elxl_stop(sc);
192011717Sgdamore@opensolaris.org 	sc->ex_suspended = B_TRUE;
192111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
192211717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
192311717Sgdamore@opensolaris.org }
192411717Sgdamore@opensolaris.org 
192511717Sgdamore@opensolaris.org static void
elxl_resume(dev_info_t * dip)192611717Sgdamore@opensolaris.org elxl_resume(dev_info_t *dip)
192711717Sgdamore@opensolaris.org {
192811717Sgdamore@opensolaris.org 	elxl_t	*sc;
192911717Sgdamore@opensolaris.org 
193011717Sgdamore@opensolaris.org 	/* This should always succeed. */
193111717Sgdamore@opensolaris.org 	sc = ddi_get_driver_private(dip);
193211717Sgdamore@opensolaris.org 	ASSERT(sc);
193311717Sgdamore@opensolaris.org 
193411717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_intrlock);
193511717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
193611717Sgdamore@opensolaris.org 	sc->ex_suspended = B_FALSE;
193711717Sgdamore@opensolaris.org 	elxl_reset(sc);
193811717Sgdamore@opensolaris.org 	if (sc->ex_running)
193911717Sgdamore@opensolaris.org 		elxl_init(sc);
194011717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
194111717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_intrlock);
194211717Sgdamore@opensolaris.org 
194311717Sgdamore@opensolaris.org 	if (sc->ex_miih) {
194411717Sgdamore@opensolaris.org 		mii_resume(sc->ex_miih);
194511717Sgdamore@opensolaris.org 	}
194611717Sgdamore@opensolaris.org }
194711717Sgdamore@opensolaris.org 
194811717Sgdamore@opensolaris.org static void
elxl_detach(elxl_t * sc)194911717Sgdamore@opensolaris.org elxl_detach(elxl_t *sc)
195011717Sgdamore@opensolaris.org {
195111717Sgdamore@opensolaris.org 	if (sc->ex_miih) {
195211717Sgdamore@opensolaris.org 		/* Detach all PHYs */
195311717Sgdamore@opensolaris.org 		mii_free(sc->ex_miih);
195411717Sgdamore@opensolaris.org 	}
195511717Sgdamore@opensolaris.org 	if (sc->ex_linkcheck) {
195611717Sgdamore@opensolaris.org 		ddi_periodic_delete(sc->ex_linkcheck);
195711717Sgdamore@opensolaris.org 	}
195811717Sgdamore@opensolaris.org 
195911717Sgdamore@opensolaris.org 	if (sc->ex_intrh != NULL) {
196011717Sgdamore@opensolaris.org 		(void) ddi_intr_disable(sc->ex_intrh);
196111717Sgdamore@opensolaris.org 		(void) ddi_intr_remove_handler(sc->ex_intrh);
196211717Sgdamore@opensolaris.org 		(void) ddi_intr_free(sc->ex_intrh);
196311717Sgdamore@opensolaris.org 		mutex_destroy(&sc->ex_intrlock);
196411717Sgdamore@opensolaris.org 		mutex_destroy(&sc->ex_txlock);
196511717Sgdamore@opensolaris.org 	}
196611717Sgdamore@opensolaris.org 
196711717Sgdamore@opensolaris.org 	if (sc->ex_pcih) {
196811717Sgdamore@opensolaris.org 		pci_config_teardown(&sc->ex_pcih);
196911717Sgdamore@opensolaris.org 	}
197011717Sgdamore@opensolaris.org 	if (sc->ex_regsh) {
197111717Sgdamore@opensolaris.org 		ddi_regs_map_free(&sc->ex_regsh);
197211717Sgdamore@opensolaris.org 	}
197311717Sgdamore@opensolaris.org 	ex_free_ring(&sc->ex_txring);
197411717Sgdamore@opensolaris.org 	ex_free_ring(&sc->ex_rxring);
197511717Sgdamore@opensolaris.org 
197611717Sgdamore@opensolaris.org 	kmem_free(sc, sizeof (*sc));
197711717Sgdamore@opensolaris.org }
197811717Sgdamore@opensolaris.org 
197911717Sgdamore@opensolaris.org /*
198011717Sgdamore@opensolaris.org  * Read EEPROM data.  If we can't unbusy the EEPROM, then zero will be
198111717Sgdamore@opensolaris.org  * returned.  This will probably result in a bogus node address.
198211717Sgdamore@opensolaris.org  */
198311717Sgdamore@opensolaris.org static uint16_t
elxl_read_eeprom(elxl_t * sc,int offset)198411717Sgdamore@opensolaris.org elxl_read_eeprom(elxl_t *sc, int offset)
198511717Sgdamore@opensolaris.org {
198611717Sgdamore@opensolaris.org 	uint16_t data = 0;
198711717Sgdamore@opensolaris.org 
198811717Sgdamore@opensolaris.org 	SET_WIN(0);
198911717Sgdamore@opensolaris.org 	if (elxl_eeprom_busy(sc))
199011717Sgdamore@opensolaris.org 		goto out;
199111717Sgdamore@opensolaris.org 
199211717Sgdamore@opensolaris.org 	PUT16(W0_EE_CMD, EE_CMD_READ | (offset & 0x3f));
199311717Sgdamore@opensolaris.org 	if (elxl_eeprom_busy(sc))
199411717Sgdamore@opensolaris.org 		goto out;
199511717Sgdamore@opensolaris.org 	data = GET16(W0_EE_DATA);
199611717Sgdamore@opensolaris.org out:
199711717Sgdamore@opensolaris.org 	return (data);
199811717Sgdamore@opensolaris.org }
199911717Sgdamore@opensolaris.org 
200011717Sgdamore@opensolaris.org static int
elxl_eeprom_busy(elxl_t * sc)200111717Sgdamore@opensolaris.org elxl_eeprom_busy(elxl_t *sc)
200211717Sgdamore@opensolaris.org {
200311717Sgdamore@opensolaris.org 	int i = 2000;
200411717Sgdamore@opensolaris.org 
200511717Sgdamore@opensolaris.org 	while (i--) {
200611717Sgdamore@opensolaris.org 		if (!(GET16(W0_EE_CMD) & EE_CMD_BUSY))
200711717Sgdamore@opensolaris.org 			return (0);
200811717Sgdamore@opensolaris.org 		drv_usecwait(100);
200911717Sgdamore@opensolaris.org 	}
201011717Sgdamore@opensolaris.org 	elxl_error(sc, "Eeprom stays busy.");
201111717Sgdamore@opensolaris.org 	return (1);
201211717Sgdamore@opensolaris.org }
201311717Sgdamore@opensolaris.org 
201411717Sgdamore@opensolaris.org static void
ex_mii_send_bits(struct ex_softc * sc,uint16_t bits,int cnt)201511717Sgdamore@opensolaris.org ex_mii_send_bits(struct ex_softc *sc, uint16_t bits, int cnt)
201611717Sgdamore@opensolaris.org {
201711717Sgdamore@opensolaris.org 	uint16_t val;
201811717Sgdamore@opensolaris.org 	ASSERT(cnt > 0);
201911717Sgdamore@opensolaris.org 
202011717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
202111717Sgdamore@opensolaris.org 	drv_usecwait(1);
202211717Sgdamore@opensolaris.org 
202311717Sgdamore@opensolaris.org 	for (int i = (1 << (cnt - 1)); i; i >>= 1) {
202411717Sgdamore@opensolaris.org 		if (bits & i) {
202511717Sgdamore@opensolaris.org 			val = PHYSMGMT_DIR | PHYSMGMT_DATA;
202611717Sgdamore@opensolaris.org 		} else {
202711717Sgdamore@opensolaris.org 			val = PHYSMGMT_DIR;
202811717Sgdamore@opensolaris.org 		}
202911717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, val);
203011717Sgdamore@opensolaris.org 		drv_usecwait(1);
203111717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, val | PHYSMGMT_CLK);
203211717Sgdamore@opensolaris.org 		drv_usecwait(1);
203311717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, val);
203411717Sgdamore@opensolaris.org 		drv_usecwait(1);
203511717Sgdamore@opensolaris.org 	}
203611717Sgdamore@opensolaris.org }
203711717Sgdamore@opensolaris.org 
203811717Sgdamore@opensolaris.org static void
ex_mii_sync(struct ex_softc * sc)203911717Sgdamore@opensolaris.org ex_mii_sync(struct ex_softc *sc)
204011717Sgdamore@opensolaris.org {
204111717Sgdamore@opensolaris.org 	/*
204211717Sgdamore@opensolaris.org 	 * We set the data bit output, and strobe the clock 32 times.
204311717Sgdamore@opensolaris.org 	 */
204411717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR);
204511717Sgdamore@opensolaris.org 	drv_usecwait(1);
204611717Sgdamore@opensolaris.org 
204711717Sgdamore@opensolaris.org 	for (int i = 0; i < 32; i++) {
204811717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR | PHYSMGMT_CLK);
204911717Sgdamore@opensolaris.org 		drv_usecwait(1);
205011717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR);
205111717Sgdamore@opensolaris.org 		drv_usecwait(1);
205211717Sgdamore@opensolaris.org 	}
205311717Sgdamore@opensolaris.org }
205411717Sgdamore@opensolaris.org 
205511717Sgdamore@opensolaris.org static uint16_t
elxl_mii_read(void * arg,uint8_t phy,uint8_t reg)205611717Sgdamore@opensolaris.org elxl_mii_read(void *arg, uint8_t phy, uint8_t reg)
205711717Sgdamore@opensolaris.org {
205811717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
205911717Sgdamore@opensolaris.org 	uint16_t	data;
206011717Sgdamore@opensolaris.org 	int		val;
206111717Sgdamore@opensolaris.org 
206211717Sgdamore@opensolaris.org 	if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID)
206311717Sgdamore@opensolaris.org 		return (0xffff);
206411717Sgdamore@opensolaris.org 
206511717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
206611717Sgdamore@opensolaris.org 	SET_WIN(4);
206711717Sgdamore@opensolaris.org 
206811717Sgdamore@opensolaris.org 	ex_mii_sync(sc);
206911717Sgdamore@opensolaris.org 
207011717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, 1, 2);	/* start */
207111717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, 2, 2);	/* read command */
207211717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, phy, 5);
207311717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, reg, 5);
207411717Sgdamore@opensolaris.org 
207511717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, 0);			/* switch to input */
207611717Sgdamore@opensolaris.org 	drv_usecwait(1);
207711717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);	/* turnaround time */
207811717Sgdamore@opensolaris.org 	drv_usecwait(1);
207911717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, 0);
208011717Sgdamore@opensolaris.org 	drv_usecwait(1);
208111717Sgdamore@opensolaris.org 
208211717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);	/* idle time */
208311717Sgdamore@opensolaris.org 	drv_usecwait(1);
208411717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, 0);
208511717Sgdamore@opensolaris.org 	drv_usecwait(1);
208611717Sgdamore@opensolaris.org 
208711717Sgdamore@opensolaris.org 	for (data = 0, val = 0x8000; val; val >>= 1) {
208811717Sgdamore@opensolaris.org 		if (GET16(W4_PHYSMGMT) & PHYSMGMT_DATA) {
208911717Sgdamore@opensolaris.org 			data |= val;
209011717Sgdamore@opensolaris.org 		}
209111717Sgdamore@opensolaris.org 		/* strobe the clock */
209211717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);
209311717Sgdamore@opensolaris.org 		drv_usecwait(1);
209411717Sgdamore@opensolaris.org 		PUT16(W4_PHYSMGMT, 0);
209511717Sgdamore@opensolaris.org 		drv_usecwait(1);
209611717Sgdamore@opensolaris.org 	}
209711717Sgdamore@opensolaris.org 
209811717Sgdamore@opensolaris.org 	/* return to output mode */
209911717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
210011717Sgdamore@opensolaris.org 	drv_usecwait(1);
210111717Sgdamore@opensolaris.org 
210211717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
210311717Sgdamore@opensolaris.org 
210411717Sgdamore@opensolaris.org 	return (data);
210511717Sgdamore@opensolaris.org }
210611717Sgdamore@opensolaris.org 
210711717Sgdamore@opensolaris.org static void
elxl_mii_write(void * arg,uint8_t phy,uint8_t reg,uint16_t data)210811717Sgdamore@opensolaris.org elxl_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data)
210911717Sgdamore@opensolaris.org {
211011717Sgdamore@opensolaris.org 	elxl_t *sc = arg;
211111717Sgdamore@opensolaris.org 
211211717Sgdamore@opensolaris.org 	if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID)
211311717Sgdamore@opensolaris.org 		return;
211411717Sgdamore@opensolaris.org 
211511717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
211611717Sgdamore@opensolaris.org 	SET_WIN(4);
211711717Sgdamore@opensolaris.org 
211811717Sgdamore@opensolaris.org 	ex_mii_sync(sc);
211911717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, 1, 2);	/* start */
212011717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, 1, 2);	/* write */
212111717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, phy, 5);
212211717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, reg, 5);
212311717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, 2, 2);	/* ack/turnaround */
212411717Sgdamore@opensolaris.org 	ex_mii_send_bits(sc, data, 16);
212511717Sgdamore@opensolaris.org 
212611717Sgdamore@opensolaris.org 	/* return to output mode */
212711717Sgdamore@opensolaris.org 	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
212811717Sgdamore@opensolaris.org 	drv_usecwait(1);
212911717Sgdamore@opensolaris.org 
213011717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
213111717Sgdamore@opensolaris.org }
213211717Sgdamore@opensolaris.org 
213311717Sgdamore@opensolaris.org static void
elxl_mii_notify(void * arg,link_state_t link)213411717Sgdamore@opensolaris.org elxl_mii_notify(void *arg, link_state_t link)
213511717Sgdamore@opensolaris.org {
213611717Sgdamore@opensolaris.org 	elxl_t		*sc = arg;
213711717Sgdamore@opensolaris.org 	int		mctl;
213811717Sgdamore@opensolaris.org 	link_duplex_t	duplex;
213911717Sgdamore@opensolaris.org 
214011717Sgdamore@opensolaris.org 	duplex = mii_get_duplex(sc->ex_miih);
214111717Sgdamore@opensolaris.org 
214211717Sgdamore@opensolaris.org 	mutex_enter(&sc->ex_txlock);
214311717Sgdamore@opensolaris.org 	if (!sc->ex_mii_active) {
214411717Sgdamore@opensolaris.org 		/* If we're using some other legacy media, bail out now */
214511717Sgdamore@opensolaris.org 		mutex_exit(&sc->ex_txlock);
214611717Sgdamore@opensolaris.org 		return;
214711717Sgdamore@opensolaris.org 	}
214811717Sgdamore@opensolaris.org 	if (!sc->ex_suspended) {
214911717Sgdamore@opensolaris.org 		SET_WIN(3);
215011717Sgdamore@opensolaris.org 		mctl = GET16(W3_MAC_CONTROL);
215111717Sgdamore@opensolaris.org 		if (duplex == LINK_DUPLEX_FULL)
215211717Sgdamore@opensolaris.org 			mctl |= MAC_CONTROL_FDX;
215311717Sgdamore@opensolaris.org 		else
215411717Sgdamore@opensolaris.org 			mctl &= ~MAC_CONTROL_FDX;
215511717Sgdamore@opensolaris.org 		PUT16(W3_MAC_CONTROL, mctl);
215611717Sgdamore@opensolaris.org 	}
215711717Sgdamore@opensolaris.org 	mutex_exit(&sc->ex_txlock);
215811717Sgdamore@opensolaris.org 
215911717Sgdamore@opensolaris.org 	mac_link_update(sc->ex_mach, link);
216011717Sgdamore@opensolaris.org }
216111717Sgdamore@opensolaris.org 
216211717Sgdamore@opensolaris.org static int
elxl_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)216311717Sgdamore@opensolaris.org elxl_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
216411717Sgdamore@opensolaris.org {
216511717Sgdamore@opensolaris.org 	switch (cmd) {
216611717Sgdamore@opensolaris.org 	case DDI_ATTACH:
216711717Sgdamore@opensolaris.org 		return (elxl_attach(dip));
216811717Sgdamore@opensolaris.org 
216911717Sgdamore@opensolaris.org 	case DDI_RESUME:
217011717Sgdamore@opensolaris.org 		elxl_resume(dip);
217111717Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
217211717Sgdamore@opensolaris.org 
217311717Sgdamore@opensolaris.org 	default:
217411717Sgdamore@opensolaris.org 		return (DDI_FAILURE);
217511717Sgdamore@opensolaris.org 	}
217611717Sgdamore@opensolaris.org }
217711717Sgdamore@opensolaris.org 
217811717Sgdamore@opensolaris.org static int
elxl_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)217911717Sgdamore@opensolaris.org elxl_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
218011717Sgdamore@opensolaris.org {
218111717Sgdamore@opensolaris.org 	elxl_t	*sc;
218211717Sgdamore@opensolaris.org 
218311717Sgdamore@opensolaris.org 	sc = ddi_get_driver_private(dip);
218411717Sgdamore@opensolaris.org 	ASSERT(sc);
218511717Sgdamore@opensolaris.org 
218611717Sgdamore@opensolaris.org 	switch (cmd) {
218711717Sgdamore@opensolaris.org 	case DDI_DETACH:
218811717Sgdamore@opensolaris.org 		if (mac_disable(sc->ex_mach) != 0) {
218911717Sgdamore@opensolaris.org 			return (DDI_FAILURE);
219011717Sgdamore@opensolaris.org 		}
219111788Sgdamore@opensolaris.org 		(void) mac_unregister(sc->ex_mach);
219211717Sgdamore@opensolaris.org 		elxl_detach(sc);
219311717Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
219411717Sgdamore@opensolaris.org 
219511717Sgdamore@opensolaris.org 	case DDI_SUSPEND:
219611717Sgdamore@opensolaris.org 		elxl_suspend(sc);
219711717Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
219811717Sgdamore@opensolaris.org 
219911717Sgdamore@opensolaris.org 	default:
220011717Sgdamore@opensolaris.org 		return (DDI_FAILURE);
220111717Sgdamore@opensolaris.org 	}
220211717Sgdamore@opensolaris.org }
220311717Sgdamore@opensolaris.org 
220411717Sgdamore@opensolaris.org static int
elxl_ddi_quiesce(dev_info_t * dip)220511717Sgdamore@opensolaris.org elxl_ddi_quiesce(dev_info_t *dip)
220611717Sgdamore@opensolaris.org {
220711717Sgdamore@opensolaris.org 	elxl_t	*sc;
220811717Sgdamore@opensolaris.org 
220911717Sgdamore@opensolaris.org 	sc = ddi_get_driver_private(dip);
221011717Sgdamore@opensolaris.org 	ASSERT(sc);
221111717Sgdamore@opensolaris.org 
221211717Sgdamore@opensolaris.org 	if (!sc->ex_suspended)
221311717Sgdamore@opensolaris.org 		elxl_reset(sc);
221411717Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
221511717Sgdamore@opensolaris.org }
221611717Sgdamore@opensolaris.org 
221711717Sgdamore@opensolaris.org static void
elxl_error(elxl_t * sc,char * fmt,...)221811717Sgdamore@opensolaris.org elxl_error(elxl_t *sc, char *fmt, ...)
221911717Sgdamore@opensolaris.org {
222011717Sgdamore@opensolaris.org 	va_list	ap;
222111717Sgdamore@opensolaris.org 	char	buf[256];
222211717Sgdamore@opensolaris.org 
222311717Sgdamore@opensolaris.org 	va_start(ap, fmt);
222411717Sgdamore@opensolaris.org 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
222511717Sgdamore@opensolaris.org 	va_end(ap);
222611717Sgdamore@opensolaris.org 
222711717Sgdamore@opensolaris.org 	cmn_err(CE_WARN, "%s%d: %s",
222811717Sgdamore@opensolaris.org 	    ddi_driver_name(sc->ex_dip), ddi_get_instance(sc->ex_dip), buf);
222911717Sgdamore@opensolaris.org }
2230