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