169b3e104SGarrett D'Amore /* 269b3e104SGarrett D'Amore * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 369b3e104SGarrett D'Amore * Use is subject to license terms. 469b3e104SGarrett D'Amore */ 569b3e104SGarrett D'Amore 669b3e104SGarrett D'Amore /* 769b3e104SGarrett D'Amore * Copyright (c) 1998 The NetBSD Foundation, Inc. 869b3e104SGarrett D'Amore * All rights reserved. 969b3e104SGarrett D'Amore * 1069b3e104SGarrett D'Amore * This code is derived from software contributed to The NetBSD Foundation 1169b3e104SGarrett D'Amore * by Frank van der Linden. 1269b3e104SGarrett D'Amore * 1369b3e104SGarrett D'Amore * Redistribution and use in source and binary forms, with or without 1469b3e104SGarrett D'Amore * modification, are permitted provided that the following conditions 1569b3e104SGarrett D'Amore * are met: 1669b3e104SGarrett D'Amore * 1. Redistributions of source code must retain the above copyright 1769b3e104SGarrett D'Amore * notice, this list of conditions and the following disclaimer. 1869b3e104SGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright 1969b3e104SGarrett D'Amore * notice, this list of conditions and the following disclaimer in the 2069b3e104SGarrett D'Amore * documentation and/or other materials provided with the distribution. 2169b3e104SGarrett D'Amore * 2269b3e104SGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2369b3e104SGarrett D'Amore * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2469b3e104SGarrett D'Amore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2569b3e104SGarrett D'Amore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2669b3e104SGarrett D'Amore * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2769b3e104SGarrett D'Amore * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2869b3e104SGarrett D'Amore * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2969b3e104SGarrett D'Amore * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3069b3e104SGarrett D'Amore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3169b3e104SGarrett D'Amore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3269b3e104SGarrett D'Amore * POSSIBILITY OF SUCH DAMAGE. 3369b3e104SGarrett D'Amore */ 3469b3e104SGarrett D'Amore 3569b3e104SGarrett D'Amore #include <sys/varargs.h> 3669b3e104SGarrett D'Amore #include <sys/types.h> 3769b3e104SGarrett D'Amore #include <sys/modctl.h> 3869b3e104SGarrett D'Amore #include <sys/conf.h> 3969b3e104SGarrett D'Amore #include <sys/devops.h> 4069b3e104SGarrett D'Amore #include <sys/stream.h> 4169b3e104SGarrett D'Amore #include <sys/strsun.h> 4269b3e104SGarrett D'Amore #include <sys/cmn_err.h> 4369b3e104SGarrett D'Amore #include <sys/ethernet.h> 4469b3e104SGarrett D'Amore #include <sys/pci.h> 4569b3e104SGarrett D'Amore #include <sys/kmem.h> 4669b3e104SGarrett D'Amore #include <sys/time.h> 4769b3e104SGarrett D'Amore #include <sys/mii.h> 4869b3e104SGarrett D'Amore #include <sys/miiregs.h> 4969b3e104SGarrett D'Amore #include <sys/mac_ether.h> 5069b3e104SGarrett D'Amore #include <sys/mac_provider.h> 5169b3e104SGarrett D'Amore #include <sys/strsubr.h> 5269b3e104SGarrett D'Amore #include <sys/pattr.h> 5369b3e104SGarrett D'Amore #include <sys/dlpi.h> 5469b3e104SGarrett D'Amore #include <sys/ddi.h> 5569b3e104SGarrett D'Amore #include <sys/sunddi.h> 5669b3e104SGarrett D'Amore 5769b3e104SGarrett D'Amore #include <sys/vlan.h> 5869b3e104SGarrett D'Amore 5969b3e104SGarrett D'Amore #include "elxl.h" 6069b3e104SGarrett D'Amore 6169b3e104SGarrett D'Amore static boolean_t elxl_add_intr(elxl_t *); 6269b3e104SGarrett D'Amore static void elxl_probe_media(elxl_t *); 6369b3e104SGarrett D'Amore static void elxl_set_rxfilter(elxl_t *); 6469b3e104SGarrett D'Amore static void elxl_set_media(elxl_t *); 6569b3e104SGarrett D'Amore static uint16_t elxl_read_eeprom(elxl_t *, int); 6669b3e104SGarrett D'Amore static void elxl_init(elxl_t *); 6769b3e104SGarrett D'Amore static void elxl_stop(elxl_t *); 6869b3e104SGarrett D'Amore static void elxl_reset(elxl_t *); 6969b3e104SGarrett D'Amore static void elxl_getstats(elxl_t *); 7069b3e104SGarrett D'Amore 7169b3e104SGarrett D'Amore static int elxl_eeprom_busy(elxl_t *); 7269b3e104SGarrett D'Amore 7369b3e104SGarrett D'Amore static void elxl_setup_tx(elxl_t *); 7469b3e104SGarrett D'Amore 7569b3e104SGarrett D'Amore static uint16_t elxl_mii_read(void *, uint8_t, uint8_t); 7669b3e104SGarrett D'Amore static void elxl_mii_write(void *, uint8_t, uint8_t, uint16_t); 7769b3e104SGarrett D'Amore static void elxl_mii_notify(void *, link_state_t); 7869b3e104SGarrett D'Amore 7969b3e104SGarrett D'Amore static int elxl_m_stat(void *, uint_t, uint64_t *); 8069b3e104SGarrett D'Amore static int elxl_m_start(void *); 8169b3e104SGarrett D'Amore static void elxl_m_stop(void *); 8269b3e104SGarrett D'Amore static mblk_t *elxl_m_tx(void *, mblk_t *); 8369b3e104SGarrett D'Amore static int elxl_m_promisc(void *, boolean_t); 8469b3e104SGarrett D'Amore static int elxl_m_multicst(void *, boolean_t, const uint8_t *); 8569b3e104SGarrett D'Amore static int elxl_m_unicst(void *, const uint8_t *); 8669b3e104SGarrett D'Amore static int elxl_m_getprop(void *, const char *, mac_prop_id_t, uint_t, 87*0dc2366fSVenugopal Iyer void *); 8869b3e104SGarrett D'Amore static int elxl_m_setprop(void *, const char *, mac_prop_id_t, uint_t, 8969b3e104SGarrett D'Amore const void *); 90*0dc2366fSVenugopal Iyer static void elxl_m_propinfo(void *, const char *, mac_prop_id_t, 91*0dc2366fSVenugopal Iyer mac_prop_info_handle_t); 9269b3e104SGarrett D'Amore static boolean_t elxl_m_getcapab(void *, mac_capab_t cap, void *); 9369b3e104SGarrett D'Amore static uint_t elxl_intr(caddr_t, caddr_t); 9469b3e104SGarrett D'Amore static void elxl_error(elxl_t *, char *, ...); 9569b3e104SGarrett D'Amore static void elxl_linkcheck(void *); 9669b3e104SGarrett D'Amore static int elxl_attach(dev_info_t *); 9769b3e104SGarrett D'Amore static void elxl_detach(elxl_t *); 9869b3e104SGarrett D'Amore static void elxl_suspend(elxl_t *); 9969b3e104SGarrett D'Amore static void elxl_resume(dev_info_t *); 10069b3e104SGarrett D'Amore static int elxl_ddi_attach(dev_info_t *, ddi_attach_cmd_t); 10169b3e104SGarrett D'Amore static int elxl_ddi_detach(dev_info_t *, ddi_detach_cmd_t); 10269b3e104SGarrett D'Amore static int elxl_ddi_quiesce(dev_info_t *); 10369b3e104SGarrett D'Amore 10469b3e104SGarrett D'Amore static ddi_device_acc_attr_t ex_dev_acc_attr = { 10569b3e104SGarrett D'Amore DDI_DEVICE_ATTR_V0, 10669b3e104SGarrett D'Amore DDI_STRUCTURE_LE_ACC, 10769b3e104SGarrett D'Amore DDI_STRICTORDER_ACC 10869b3e104SGarrett D'Amore }; 10969b3e104SGarrett D'Amore 11069b3e104SGarrett D'Amore static ddi_device_acc_attr_t ex_buf_acc_attr = { 11169b3e104SGarrett D'Amore DDI_DEVICE_ATTR_V0, 11269b3e104SGarrett D'Amore DDI_NEVERSWAP_ACC, 11369b3e104SGarrett D'Amore DDI_STORECACHING_OK_ACC 11469b3e104SGarrett D'Amore }; 11569b3e104SGarrett D'Amore 11669b3e104SGarrett D'Amore /* 11769b3e104SGarrett D'Amore * In theory buffers can have more flexible DMA attributes, but since 11869b3e104SGarrett D'Amore * we're just using a preallocated region with bcopy, there is little 11969b3e104SGarrett D'Amore * reason to allow for rougher alignment. (Further, the 8-byte 12069b3e104SGarrett D'Amore * alignment can allow for more efficient bcopy and similar operations 12169b3e104SGarrett D'Amore * from the buffer.) 12269b3e104SGarrett D'Amore */ 12369b3e104SGarrett D'Amore static ddi_dma_attr_t ex_dma_attr = { 12469b3e104SGarrett D'Amore DMA_ATTR_V0, /* dma_attr_version */ 12569b3e104SGarrett D'Amore 0, /* dma_attr_addr_lo */ 12669b3e104SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_addr_hi */ 12769b3e104SGarrett D'Amore 0x00FFFFFFU, /* dma_attr_count_max */ 12869b3e104SGarrett D'Amore 8, /* dma_attr_align */ 12969b3e104SGarrett D'Amore 0x7F, /* dma_attr_burstsizes */ 13069b3e104SGarrett D'Amore 1, /* dma_attr_minxfer */ 13169b3e104SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_maxxfer */ 13269b3e104SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_seg */ 13369b3e104SGarrett D'Amore 1, /* dma_attr_sgllen */ 13469b3e104SGarrett D'Amore 1, /* dma_attr_granular */ 13569b3e104SGarrett D'Amore 0 /* dma_attr_flags */ 13669b3e104SGarrett D'Amore }; 13769b3e104SGarrett D'Amore 13869b3e104SGarrett D'Amore static uint8_t ex_broadcast[6] = { 13969b3e104SGarrett D'Amore 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 14069b3e104SGarrett D'Amore }; 14169b3e104SGarrett D'Amore 14269b3e104SGarrett D'Amore /* 14369b3e104SGarrett D'Amore * Structure to map media-present bits in boards to ifmedia codes and 14469b3e104SGarrett D'Amore * printable media names. Used for table-driven ifmedia initialization. 14569b3e104SGarrett D'Amore */ 14669b3e104SGarrett D'Amore typedef struct ex_media { 14769b3e104SGarrett D'Amore int exm_mpbit; /* media present bit */ 14869b3e104SGarrett D'Amore int exm_xcvr; /* XCVR_SEL_* constant */ 14969b3e104SGarrett D'Amore } ex_media_t; 15069b3e104SGarrett D'Amore 15169b3e104SGarrett D'Amore /* 15269b3e104SGarrett D'Amore * Media table for 3c90x chips. Note that chips with MII have no 15369b3e104SGarrett D'Amore * `native' media. This is sorted in "reverse preference". 15469b3e104SGarrett D'Amore */ 15569b3e104SGarrett D'Amore static ex_media_t ex_native_media[] = { 15669b3e104SGarrett D'Amore { MEDIAOPT_AUI, XCVR_SEL_AUI }, 15769b3e104SGarrett D'Amore { MEDIAOPT_BNC, XCVR_SEL_BNC }, 15869b3e104SGarrett D'Amore { MEDIAOPT_10T, XCVR_SEL_10T }, 15969b3e104SGarrett D'Amore { MEDIAOPT_100TX, XCVR_SEL_AUTO }, /* only 90XB */ 16069b3e104SGarrett D'Amore { MEDIAOPT_100FX, XCVR_SEL_100FX }, 16169b3e104SGarrett D'Amore { MEDIAOPT_MII, XCVR_SEL_MII }, 16269b3e104SGarrett D'Amore { MEDIAOPT_100T4, XCVR_SEL_MII }, 16369b3e104SGarrett D'Amore { 0, 0 }, 16469b3e104SGarrett D'Amore }; 16569b3e104SGarrett D'Amore 16669b3e104SGarrett D'Amore 16769b3e104SGarrett D'Amore /* 16869b3e104SGarrett D'Amore * NB: There are lots of other models that *could* be supported. 16969b3e104SGarrett D'Amore * Specifically there are cardbus and miniPCI variants that could be 17069b3e104SGarrett D'Amore * easily added here, but they require special hacks and I have no 17169b3e104SGarrett D'Amore * access to the hardware required to verify them. Especially they 17269b3e104SGarrett D'Amore * seem to require some extra work in another register window, and I 17369b3e104SGarrett D'Amore * have no supporting documentation. 17469b3e104SGarrett D'Amore */ 17569b3e104SGarrett D'Amore static const struct ex_product { 17669b3e104SGarrett D'Amore uint16_t epp_prodid; /* PCI product ID */ 17769b3e104SGarrett D'Amore const char *epp_name; /* device name */ 17869b3e104SGarrett D'Amore unsigned epp_flags; /* initial softc flags */ 17969b3e104SGarrett D'Amore } ex_products[] = { 18069b3e104SGarrett D'Amore { 0x4500, "3c450-TX", 0 }, 18169b3e104SGarrett D'Amore { 0x7646, "3cSOHO100-TX", 0 }, 18269b3e104SGarrett D'Amore { 0x9000, "3c900-TPO", 0 }, 18369b3e104SGarrett D'Amore { 0x9001, "3c900-COMBO", 0 }, 18469b3e104SGarrett D'Amore { 0x9004, "3c900B-TPO", 0 }, 18569b3e104SGarrett D'Amore { 0x9005, "3c900B-COMBO", 0 }, 18669b3e104SGarrett D'Amore { 0x9006, "3c900B-TPC", 0 }, 18769b3e104SGarrett D'Amore { 0x900a, "3c900B-FL", 0 }, 18869b3e104SGarrett D'Amore { 0x9050, "3c905-TX", 0 }, 18969b3e104SGarrett D'Amore { 0x9051, "3c905-T4", 0 }, 19069b3e104SGarrett D'Amore { 0x9055, "3c905B-TX", 0 }, 19169b3e104SGarrett D'Amore { 0x9056, "3c905B-T4", 0 }, 19269b3e104SGarrett D'Amore { 0x9058, "3c905B-COMBO", 0 }, 19369b3e104SGarrett D'Amore { 0x905a, "3c905B-FX", 0 }, 19469b3e104SGarrett D'Amore { 0x9200, "3c905C-TX", 0 }, 19569b3e104SGarrett D'Amore { 0x9201, "3c920B-EMB", 0 }, 19669b3e104SGarrett D'Amore { 0x9202, "3c920B-EMB-WNM", 0 }, 19769b3e104SGarrett D'Amore { 0x9800, "3c980", 0 }, 19869b3e104SGarrett D'Amore { 0x9805, "3c980C-TXM", 0 }, 19969b3e104SGarrett D'Amore 20069b3e104SGarrett D'Amore { 0, NULL, 0 }, 20169b3e104SGarrett D'Amore }; 20269b3e104SGarrett D'Amore 203*0dc2366fSVenugopal Iyer static char *ex_priv_prop[] = { 204*0dc2366fSVenugopal Iyer "_media", 205*0dc2366fSVenugopal Iyer "_available_media", 206*0dc2366fSVenugopal Iyer NULL 20769b3e104SGarrett D'Amore }; 20869b3e104SGarrett D'Amore 20969b3e104SGarrett D'Amore static mii_ops_t ex_mii_ops = { 21069b3e104SGarrett D'Amore MII_OPS_VERSION, 21169b3e104SGarrett D'Amore elxl_mii_read, 21269b3e104SGarrett D'Amore elxl_mii_write, 21369b3e104SGarrett D'Amore elxl_mii_notify, 21469b3e104SGarrett D'Amore }; 21569b3e104SGarrett D'Amore 21669b3e104SGarrett D'Amore static mac_callbacks_t elxl_m_callbacks = { 217*0dc2366fSVenugopal Iyer MC_GETCAPAB | MC_PROPERTIES, 21869b3e104SGarrett D'Amore elxl_m_stat, 21969b3e104SGarrett D'Amore elxl_m_start, 22069b3e104SGarrett D'Amore elxl_m_stop, 22169b3e104SGarrett D'Amore elxl_m_promisc, 22269b3e104SGarrett D'Amore elxl_m_multicst, 22369b3e104SGarrett D'Amore elxl_m_unicst, 22469b3e104SGarrett D'Amore elxl_m_tx, 22569b3e104SGarrett D'Amore NULL, 226*0dc2366fSVenugopal Iyer NULL, 22769b3e104SGarrett D'Amore elxl_m_getcapab, 22869b3e104SGarrett D'Amore NULL, 22969b3e104SGarrett D'Amore NULL, 23069b3e104SGarrett D'Amore elxl_m_setprop, 231*0dc2366fSVenugopal Iyer elxl_m_getprop, 232*0dc2366fSVenugopal Iyer elxl_m_propinfo 23369b3e104SGarrett D'Amore }; 23469b3e104SGarrett D'Amore 23569b3e104SGarrett D'Amore /* 23669b3e104SGarrett D'Amore * Stream information 23769b3e104SGarrett D'Amore */ 23869b3e104SGarrett D'Amore DDI_DEFINE_STREAM_OPS(ex_devops, nulldev, nulldev, 23969b3e104SGarrett D'Amore elxl_ddi_attach, elxl_ddi_detach, 24069b3e104SGarrett D'Amore nodev, NULL, D_MP, NULL, elxl_ddi_quiesce); 24169b3e104SGarrett D'Amore 24269b3e104SGarrett D'Amore /* 24369b3e104SGarrett D'Amore * Module linkage information. 24469b3e104SGarrett D'Amore */ 24569b3e104SGarrett D'Amore 24669b3e104SGarrett D'Amore static struct modldrv ex_modldrv = { 24769b3e104SGarrett D'Amore &mod_driverops, /* drv_modops */ 24869b3e104SGarrett D'Amore "3Com EtherLink XL", /* drv_linkinfo */ 24969b3e104SGarrett D'Amore &ex_devops /* drv_dev_ops */ 25069b3e104SGarrett D'Amore }; 25169b3e104SGarrett D'Amore 25269b3e104SGarrett D'Amore static struct modlinkage ex_modlinkage = { 25369b3e104SGarrett D'Amore MODREV_1, /* ml_rev */ 25469b3e104SGarrett D'Amore { &ex_modldrv, NULL } /* ml_linkage */ 25569b3e104SGarrett D'Amore }; 25669b3e104SGarrett D'Amore 25769b3e104SGarrett D'Amore int 25869b3e104SGarrett D'Amore _init(void) 25969b3e104SGarrett D'Amore { 26069b3e104SGarrett D'Amore int rv; 26169b3e104SGarrett D'Amore mac_init_ops(&ex_devops, "elxl"); 26269b3e104SGarrett D'Amore if ((rv = mod_install(&ex_modlinkage)) != DDI_SUCCESS) { 26369b3e104SGarrett D'Amore mac_fini_ops(&ex_devops); 26469b3e104SGarrett D'Amore } 26569b3e104SGarrett D'Amore return (rv); 26669b3e104SGarrett D'Amore } 26769b3e104SGarrett D'Amore 26869b3e104SGarrett D'Amore int 26969b3e104SGarrett D'Amore _fini(void) 27069b3e104SGarrett D'Amore { 27169b3e104SGarrett D'Amore int rv; 27269b3e104SGarrett D'Amore if ((rv = mod_remove(&ex_modlinkage)) == DDI_SUCCESS) { 27369b3e104SGarrett D'Amore mac_fini_ops(&ex_devops); 27469b3e104SGarrett D'Amore } 27569b3e104SGarrett D'Amore return (rv); 27669b3e104SGarrett D'Amore } 27769b3e104SGarrett D'Amore 27869b3e104SGarrett D'Amore int 27969b3e104SGarrett D'Amore _info(struct modinfo *modinfop) 28069b3e104SGarrett D'Amore { 28169b3e104SGarrett D'Amore return (mod_info(&ex_modlinkage, modinfop)); 28269b3e104SGarrett D'Amore } 28369b3e104SGarrett D'Amore 28469b3e104SGarrett D'Amore static void 28569b3e104SGarrett D'Amore ex_free_ring(ex_ring_t *r) 28669b3e104SGarrett D'Amore { 28769b3e104SGarrett D'Amore for (int i = 0; i < r->r_count; i++) { 28869b3e104SGarrett D'Amore ex_desc_t *ed = &r->r_desc[i]; 28969b3e104SGarrett D'Amore if (ed->ed_bufaddr) 29069b3e104SGarrett D'Amore (void) ddi_dma_unbind_handle(ed->ed_dmah); 29169b3e104SGarrett D'Amore if (ed->ed_acch) 29269b3e104SGarrett D'Amore ddi_dma_mem_free(&ed->ed_acch); 29369b3e104SGarrett D'Amore if (ed->ed_dmah) 29469b3e104SGarrett D'Amore ddi_dma_free_handle(&ed->ed_dmah); 29569b3e104SGarrett D'Amore } 29669b3e104SGarrett D'Amore 29769b3e104SGarrett D'Amore if (r->r_paddr) 29869b3e104SGarrett D'Amore (void) ddi_dma_unbind_handle(r->r_dmah); 29969b3e104SGarrett D'Amore if (r->r_acch) 30069b3e104SGarrett D'Amore ddi_dma_mem_free(&r->r_acch); 30169b3e104SGarrett D'Amore if (r->r_dmah) 30269b3e104SGarrett D'Amore ddi_dma_free_handle(&r->r_dmah); 30369b3e104SGarrett D'Amore 30469b3e104SGarrett D'Amore kmem_free(r->r_desc, sizeof (ex_desc_t) * r->r_count); 30569b3e104SGarrett D'Amore r->r_desc = NULL; 30669b3e104SGarrett D'Amore } 30769b3e104SGarrett D'Amore 30869b3e104SGarrett D'Amore static void 30969b3e104SGarrett D'Amore elxl_reset_ring(ex_ring_t *r, uint_t dir) 31069b3e104SGarrett D'Amore { 31169b3e104SGarrett D'Amore ex_desc_t *ed; 31269b3e104SGarrett D'Amore ex_pd_t *pd; 31369b3e104SGarrett D'Amore 31469b3e104SGarrett D'Amore if (dir == DDI_DMA_WRITE) { 31569b3e104SGarrett D'Amore /* transmit ring, not linked yet */ 31669b3e104SGarrett D'Amore for (int i = 0; i < r->r_count; i++) { 31769b3e104SGarrett D'Amore ed = &r->r_desc[i]; 31869b3e104SGarrett D'Amore pd = ed->ed_pd; 31969b3e104SGarrett D'Amore PUT_PD(r, pd->pd_link, 0); 32069b3e104SGarrett D'Amore PUT_PD(r, pd->pd_fsh, 0); 32169b3e104SGarrett D'Amore PUT_PD(r, pd->pd_len, EX_FR_LAST); 32269b3e104SGarrett D'Amore PUT_PD(r, pd->pd_addr, ed->ed_bufaddr); 32369b3e104SGarrett D'Amore } 32469b3e104SGarrett D'Amore r->r_head = NULL; 32569b3e104SGarrett D'Amore r->r_tail = NULL; 32669b3e104SGarrett D'Amore r->r_avail = r->r_count; 32769b3e104SGarrett D'Amore } else { 32869b3e104SGarrett D'Amore /* receive is linked into a list */ 32969b3e104SGarrett D'Amore for (int i = 0; i < r->r_count; i++) { 33069b3e104SGarrett D'Amore ed = &r->r_desc[i]; 33169b3e104SGarrett D'Amore pd = ed->ed_pd; 33269b3e104SGarrett D'Amore PUT_PD(r, pd->pd_link, ed->ed_next->ed_descaddr); 33369b3e104SGarrett D'Amore PUT_PD(r, pd->pd_status, 0); 33469b3e104SGarrett D'Amore PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST); 33569b3e104SGarrett D'Amore PUT_PD(r, pd->pd_addr, ed->ed_bufaddr); 33669b3e104SGarrett D'Amore } 33769b3e104SGarrett D'Amore r->r_head = &r->r_desc[0]; 33869b3e104SGarrett D'Amore r->r_tail = NULL; 33969b3e104SGarrett D'Amore r->r_avail = 0; 34069b3e104SGarrett D'Amore } 3415109e3feSGarrett D'Amore (void) ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); 34269b3e104SGarrett D'Amore } 34369b3e104SGarrett D'Amore 34469b3e104SGarrett D'Amore static boolean_t 34569b3e104SGarrett D'Amore ex_alloc_ring(elxl_t *sc, int count, ex_ring_t *r, uint_t dir) 34669b3e104SGarrett D'Amore { 34769b3e104SGarrett D'Amore dev_info_t *dip = sc->ex_dip; 34869b3e104SGarrett D'Amore int i; 34969b3e104SGarrett D'Amore int rv; 35069b3e104SGarrett D'Amore size_t len; 35169b3e104SGarrett D'Amore ddi_dma_cookie_t dmac; 35269b3e104SGarrett D'Amore unsigned ndmac; 35369b3e104SGarrett D'Amore 35469b3e104SGarrett D'Amore r->r_count = count; 35569b3e104SGarrett D'Amore r->r_desc = kmem_zalloc(sizeof (ex_desc_t) * count, KM_SLEEP); 35669b3e104SGarrett D'Amore 35769b3e104SGarrett D'Amore rv = ddi_dma_alloc_handle(dip, &ex_dma_attr, DDI_DMA_DONTWAIT, 35869b3e104SGarrett D'Amore NULL, &r->r_dmah); 35969b3e104SGarrett D'Amore if (rv != DDI_SUCCESS) { 36069b3e104SGarrett D'Amore elxl_error(sc, "unable to allocate descriptor dma handle"); 36169b3e104SGarrett D'Amore return (B_FALSE); 36269b3e104SGarrett D'Amore } 36369b3e104SGarrett D'Amore 36469b3e104SGarrett D'Amore rv = ddi_dma_mem_alloc(r->r_dmah, count * sizeof (struct ex_pd), 36569b3e104SGarrett D'Amore &ex_dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL, 36669b3e104SGarrett D'Amore (caddr_t *)&r->r_pd, &len, &r->r_acch); 36769b3e104SGarrett D'Amore if (rv != DDI_SUCCESS) { 36869b3e104SGarrett D'Amore elxl_error(sc, "unable to allocate descriptor memory"); 36969b3e104SGarrett D'Amore return (B_FALSE); 37069b3e104SGarrett D'Amore } 37169b3e104SGarrett D'Amore bzero(r->r_pd, len); 37269b3e104SGarrett D'Amore 37369b3e104SGarrett D'Amore rv = ddi_dma_addr_bind_handle(r->r_dmah, NULL, 37469b3e104SGarrett D'Amore (caddr_t)r->r_pd, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 37569b3e104SGarrett D'Amore DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac); 37669b3e104SGarrett D'Amore if (rv != DDI_DMA_MAPPED) { 37769b3e104SGarrett D'Amore elxl_error(sc, "unable to map descriptor memory"); 37869b3e104SGarrett D'Amore return (B_FALSE); 37969b3e104SGarrett D'Amore } 38069b3e104SGarrett D'Amore r->r_paddr = dmac.dmac_address; 38169b3e104SGarrett D'Amore 38269b3e104SGarrett D'Amore for (i = 0; i < count; i++) { 38369b3e104SGarrett D'Amore ex_desc_t *ed = &r->r_desc[i]; 38469b3e104SGarrett D'Amore ex_pd_t *pd = &r->r_pd[i]; 38569b3e104SGarrett D'Amore 38669b3e104SGarrett D'Amore ed->ed_pd = pd; 38769b3e104SGarrett D'Amore ed->ed_off = (i * sizeof (ex_pd_t)); 38869b3e104SGarrett D'Amore ed->ed_descaddr = r->r_paddr + (i * sizeof (ex_pd_t)); 38969b3e104SGarrett D'Amore 39069b3e104SGarrett D'Amore /* Link the high level descriptors into a ring. */ 39169b3e104SGarrett D'Amore ed->ed_next = &r->r_desc[(i + 1) % count]; 39269b3e104SGarrett D'Amore ed->ed_next->ed_prev = ed; 39369b3e104SGarrett D'Amore 39469b3e104SGarrett D'Amore rv = ddi_dma_alloc_handle(dip, &ex_dma_attr, 39569b3e104SGarrett D'Amore DDI_DMA_DONTWAIT, NULL, &ed->ed_dmah); 39669b3e104SGarrett D'Amore if (rv != 0) { 39769b3e104SGarrett D'Amore elxl_error(sc, "can't allocate buf dma handle"); 39869b3e104SGarrett D'Amore return (B_FALSE); 39969b3e104SGarrett D'Amore } 40069b3e104SGarrett D'Amore rv = ddi_dma_mem_alloc(ed->ed_dmah, EX_BUFSZ, &ex_buf_acc_attr, 40169b3e104SGarrett D'Amore DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &ed->ed_buf, 40269b3e104SGarrett D'Amore &len, &ed->ed_acch); 40369b3e104SGarrett D'Amore if (rv != DDI_SUCCESS) { 40469b3e104SGarrett D'Amore elxl_error(sc, "unable to allocate buf memory"); 40569b3e104SGarrett D'Amore return (B_FALSE); 40669b3e104SGarrett D'Amore } 40769b3e104SGarrett D'Amore bzero(ed->ed_buf, len); 40869b3e104SGarrett D'Amore 40969b3e104SGarrett D'Amore rv = ddi_dma_addr_bind_handle(ed->ed_dmah, NULL, 41069b3e104SGarrett D'Amore ed->ed_buf, len, dir | DDI_DMA_STREAMING, 41169b3e104SGarrett D'Amore DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac); 41269b3e104SGarrett D'Amore if (rv != DDI_DMA_MAPPED) { 41369b3e104SGarrett D'Amore elxl_error(sc, "unable to map buf memory"); 41469b3e104SGarrett D'Amore return (B_FALSE); 41569b3e104SGarrett D'Amore } 41669b3e104SGarrett D'Amore ed->ed_bufaddr = dmac.dmac_address; 41769b3e104SGarrett D'Amore } 41869b3e104SGarrett D'Amore 41969b3e104SGarrett D'Amore elxl_reset_ring(r, dir); 42069b3e104SGarrett D'Amore 42169b3e104SGarrett D'Amore return (B_TRUE); 42269b3e104SGarrett D'Amore } 42369b3e104SGarrett D'Amore 42469b3e104SGarrett D'Amore static boolean_t 42569b3e104SGarrett D'Amore elxl_add_intr(elxl_t *sc) 42669b3e104SGarrett D'Amore { 42769b3e104SGarrett D'Amore dev_info_t *dip; 42869b3e104SGarrett D'Amore int actual; 42969b3e104SGarrett D'Amore uint_t ipri; 43069b3e104SGarrett D'Amore 43169b3e104SGarrett D'Amore int rv; 43269b3e104SGarrett D'Amore 43369b3e104SGarrett D'Amore dip = sc->ex_dip; 43469b3e104SGarrett D'Amore 43569b3e104SGarrett D'Amore rv = ddi_intr_alloc(dip, &sc->ex_intrh, DDI_INTR_TYPE_FIXED, 43669b3e104SGarrett D'Amore 0, 1, &actual, DDI_INTR_ALLOC_STRICT); 43769b3e104SGarrett D'Amore if ((rv != DDI_SUCCESS) || (actual != 1)) { 43869b3e104SGarrett D'Amore elxl_error(sc, "Unable to allocate interrupt, %d, count %d", 43969b3e104SGarrett D'Amore rv, actual); 44069b3e104SGarrett D'Amore return (B_FALSE); 44169b3e104SGarrett D'Amore } 44269b3e104SGarrett D'Amore 44369b3e104SGarrett D'Amore if (ddi_intr_get_pri(sc->ex_intrh, &ipri) != DDI_SUCCESS) { 44469b3e104SGarrett D'Amore elxl_error(sc, "Unable to get interrupt priority"); 44569b3e104SGarrett D'Amore return (B_FALSE); 44669b3e104SGarrett D'Amore } 44769b3e104SGarrett D'Amore 44869b3e104SGarrett D'Amore if (ddi_intr_add_handler(sc->ex_intrh, elxl_intr, sc, NULL) != 44969b3e104SGarrett D'Amore DDI_SUCCESS) { 45069b3e104SGarrett D'Amore elxl_error(sc, "Can't add interrupt handler"); 45169b3e104SGarrett D'Amore (void) ddi_intr_free(sc->ex_intrh); 45269b3e104SGarrett D'Amore sc->ex_intrh = NULL; 45369b3e104SGarrett D'Amore return (B_FALSE); 45469b3e104SGarrett D'Amore } 45569b3e104SGarrett D'Amore mutex_init(&sc->ex_intrlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); 45669b3e104SGarrett D'Amore mutex_init(&sc->ex_txlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); 45769b3e104SGarrett D'Amore 45869b3e104SGarrett D'Amore return (B_TRUE); 45969b3e104SGarrett D'Amore } 46069b3e104SGarrett D'Amore 46169b3e104SGarrett D'Amore static int 46269b3e104SGarrett D'Amore elxl_attach(dev_info_t *dip) 46369b3e104SGarrett D'Amore { 46469b3e104SGarrett D'Amore elxl_t *sc; 46569b3e104SGarrett D'Amore mac_register_t *macp; 46669b3e104SGarrett D'Amore uint16_t val; 46769b3e104SGarrett D'Amore uint16_t venid; 46869b3e104SGarrett D'Amore uint16_t devid; 46969b3e104SGarrett D'Amore int i; 47069b3e104SGarrett D'Amore 47169b3e104SGarrett D'Amore sc = kmem_zalloc(sizeof (*sc), KM_SLEEP); 47269b3e104SGarrett D'Amore ddi_set_driver_private(dip, sc); 47369b3e104SGarrett D'Amore sc->ex_dip = dip; 47469b3e104SGarrett D'Amore 47569b3e104SGarrett D'Amore if (pci_config_setup(dip, &sc->ex_pcih) != DDI_SUCCESS) { 47669b3e104SGarrett D'Amore elxl_error(sc, "unable to setup PCI config handle"); 47769b3e104SGarrett D'Amore goto fail; 47869b3e104SGarrett D'Amore } 47969b3e104SGarrett D'Amore venid = pci_config_get16(sc->ex_pcih, PCI_CONF_VENID); 48069b3e104SGarrett D'Amore devid = pci_config_get16(sc->ex_pcih, PCI_CONF_DEVID); 48169b3e104SGarrett D'Amore 48269b3e104SGarrett D'Amore if (venid != 0x10b7) { 48369b3e104SGarrett D'Amore /* Not a 3Com part! */ 48469b3e104SGarrett D'Amore elxl_error(sc, "Unsupported vendor id (0x%x)", venid); 48569b3e104SGarrett D'Amore goto fail; 48669b3e104SGarrett D'Amore } 48769b3e104SGarrett D'Amore for (i = 0; ex_products[i].epp_name; i++) { 48869b3e104SGarrett D'Amore if (devid == ex_products[i].epp_prodid) { 48969b3e104SGarrett D'Amore cmn_err(CE_CONT, "?%s%d: 3Com %s", 49069b3e104SGarrett D'Amore ddi_driver_name(dip), 49169b3e104SGarrett D'Amore ddi_get_instance(dip), 49269b3e104SGarrett D'Amore ex_products[i].epp_name); 49369b3e104SGarrett D'Amore sc->ex_conf = ex_products[i].epp_flags; 49469b3e104SGarrett D'Amore break; 49569b3e104SGarrett D'Amore } 49669b3e104SGarrett D'Amore } 49769b3e104SGarrett D'Amore if (ex_products[i].epp_name == NULL) { 49869b3e104SGarrett D'Amore /* Not a produce we know how to support */ 49969b3e104SGarrett D'Amore elxl_error(sc, "Unsupported device id (0x%x)", devid); 50069b3e104SGarrett D'Amore elxl_error(sc, "Driver may or may not function."); 50169b3e104SGarrett D'Amore } 50269b3e104SGarrett D'Amore 50369b3e104SGarrett D'Amore pci_config_put16(sc->ex_pcih, PCI_CONF_COMM, 50469b3e104SGarrett D'Amore pci_config_get16(sc->ex_pcih, PCI_CONF_COMM) | 50569b3e104SGarrett D'Amore PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME); 50669b3e104SGarrett D'Amore 50769b3e104SGarrett D'Amore if (ddi_regs_map_setup(dip, 1, &sc->ex_regsva, 0, 0, &ex_dev_acc_attr, 50869b3e104SGarrett D'Amore &sc->ex_regsh) != DDI_SUCCESS) { 50969b3e104SGarrett D'Amore elxl_error(sc, "Unable to map device registers"); 51069b3e104SGarrett D'Amore goto fail; 51169b3e104SGarrett D'Amore } 51269b3e104SGarrett D'Amore 51369b3e104SGarrett D'Amore if (!elxl_add_intr(sc)) { 51469b3e104SGarrett D'Amore goto fail; 51569b3e104SGarrett D'Amore } 51669b3e104SGarrett D'Amore 51769b3e104SGarrett D'Amore elxl_reset(sc); 51869b3e104SGarrett D'Amore 51969b3e104SGarrett D'Amore val = elxl_read_eeprom(sc, EE_OEM_ADDR_0); 52069b3e104SGarrett D'Amore sc->ex_factaddr[0] = val >> 8; 52169b3e104SGarrett D'Amore sc->ex_factaddr[1] = val & 0xff; 52269b3e104SGarrett D'Amore val = elxl_read_eeprom(sc, EE_OEM_ADDR_1); 52369b3e104SGarrett D'Amore sc->ex_factaddr[2] = val >> 8; 52469b3e104SGarrett D'Amore sc->ex_factaddr[3] = val & 0xff; 52569b3e104SGarrett D'Amore val = elxl_read_eeprom(sc, EE_OEM_ADDR_2); 52669b3e104SGarrett D'Amore sc->ex_factaddr[4] = val >> 8; 52769b3e104SGarrett D'Amore sc->ex_factaddr[5] = val & 0xff; 52869b3e104SGarrett D'Amore bcopy(sc->ex_factaddr, sc->ex_curraddr, 6); 52969b3e104SGarrett D'Amore 53069b3e104SGarrett D'Amore sc->ex_capab = elxl_read_eeprom(sc, EE_CAPABILITIES); 53169b3e104SGarrett D'Amore 53269b3e104SGarrett D'Amore /* 53369b3e104SGarrett D'Amore * Is this a 90XB? If bit 2 (supportsLargePackets) is set, or 53469b3e104SGarrett D'Amore * bit (supportsNoTxLength) is clear, then its a 90X. 53569b3e104SGarrett D'Amore * Otherwise its a 90XB. 53669b3e104SGarrett D'Amore */ 53769b3e104SGarrett D'Amore if ((sc->ex_capab & (1 << 2)) || !(sc->ex_capab & (1 << 9))) { 53869b3e104SGarrett D'Amore sc->ex_conf &= ~CONF_90XB; 53969b3e104SGarrett D'Amore } else { 54069b3e104SGarrett D'Amore sc->ex_conf |= CONF_90XB; 54169b3e104SGarrett D'Amore } 54269b3e104SGarrett D'Amore 54369b3e104SGarrett D'Amore if (!ex_alloc_ring(sc, EX_NRX, &sc->ex_rxring, DDI_DMA_READ)) { 54469b3e104SGarrett D'Amore goto fail; 54569b3e104SGarrett D'Amore } 54669b3e104SGarrett D'Amore 54769b3e104SGarrett D'Amore if (!ex_alloc_ring(sc, EX_NTX, &sc->ex_txring, DDI_DMA_WRITE)) { 54869b3e104SGarrett D'Amore goto fail; 54969b3e104SGarrett D'Amore } 55069b3e104SGarrett D'Amore 55169b3e104SGarrett D'Amore elxl_probe_media(sc); 55269b3e104SGarrett D'Amore 55369b3e104SGarrett D'Amore /* 55469b3e104SGarrett D'Amore * The probe may have indicated MII! 55569b3e104SGarrett D'Amore */ 55669b3e104SGarrett D'Amore if (sc->ex_mediaopt & (MEDIAOPT_MII | MEDIAOPT_100TX)) { 55769b3e104SGarrett D'Amore sc->ex_miih = mii_alloc(sc, sc->ex_dip, &ex_mii_ops); 55869b3e104SGarrett D'Amore if (sc->ex_miih == NULL) { 55969b3e104SGarrett D'Amore goto fail; 56069b3e104SGarrett D'Amore } 56169b3e104SGarrett D'Amore /* 56269b3e104SGarrett D'Amore * Note: The 90XB models can in theory support pause, 56369b3e104SGarrett D'Amore * but we're not enabling now due to lack of units for 56469b3e104SGarrett D'Amore * testing with. If this is changed, make sure to 56569b3e104SGarrett D'Amore * update the code in elxl_mii_notify to set the flow 56669b3e104SGarrett D'Amore * control field in the W3_MAC_CONTROL register. 56769b3e104SGarrett D'Amore */ 56869b3e104SGarrett D'Amore mii_set_pauseable(sc->ex_miih, B_FALSE, B_FALSE); 56969b3e104SGarrett D'Amore } 57069b3e104SGarrett D'Amore if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 57169b3e104SGarrett D'Amore elxl_error(sc, "MAC register allocation failed"); 57269b3e104SGarrett D'Amore goto fail; 57369b3e104SGarrett D'Amore } 57469b3e104SGarrett D'Amore macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 57569b3e104SGarrett D'Amore macp->m_driver = sc; 57669b3e104SGarrett D'Amore macp->m_dip = dip; 57769b3e104SGarrett D'Amore macp->m_src_addr = sc->ex_curraddr; 57869b3e104SGarrett D'Amore macp->m_callbacks = &elxl_m_callbacks; 57969b3e104SGarrett D'Amore macp->m_min_sdu = 0; 58069b3e104SGarrett D'Amore macp->m_max_sdu = ETHERMTU; 58169b3e104SGarrett D'Amore macp->m_margin = VLAN_TAGSZ; 58269b3e104SGarrett D'Amore macp->m_priv_props = ex_priv_prop; 58369b3e104SGarrett D'Amore 58469b3e104SGarrett D'Amore (void) ddi_intr_enable(sc->ex_intrh); 58569b3e104SGarrett D'Amore 58669b3e104SGarrett D'Amore if (mac_register(macp, &sc->ex_mach) == DDI_SUCCESS) { 58769b3e104SGarrett D'Amore 58869b3e104SGarrett D'Amore /* 58969b3e104SGarrett D'Amore * Note: we don't want to start link checking 59069b3e104SGarrett D'Amore * until *after* we have added the MAC handle. 59169b3e104SGarrett D'Amore */ 59269b3e104SGarrett D'Amore if (sc->ex_mediaopt & 59369b3e104SGarrett D'Amore (MEDIAOPT_MASK & ~(MEDIAOPT_MII | MEDIAOPT_100TX))) { 59469b3e104SGarrett D'Amore 59569b3e104SGarrett D'Amore /* Check non-MII link state once per second. */ 59669b3e104SGarrett D'Amore sc->ex_linkcheck = 59769b3e104SGarrett D'Amore ddi_periodic_add(elxl_linkcheck, sc, 10000000, 0); 59869b3e104SGarrett D'Amore } 59969b3e104SGarrett D'Amore 60069b3e104SGarrett D'Amore mac_free(macp); 60169b3e104SGarrett D'Amore return (DDI_SUCCESS); 60269b3e104SGarrett D'Amore } 60369b3e104SGarrett D'Amore 60469b3e104SGarrett D'Amore mac_free(macp); 60569b3e104SGarrett D'Amore 60669b3e104SGarrett D'Amore fail: 60769b3e104SGarrett D'Amore elxl_detach(sc); 60869b3e104SGarrett D'Amore return (DDI_FAILURE); 60969b3e104SGarrett D'Amore } 61069b3e104SGarrett D'Amore 61169b3e104SGarrett D'Amore /* 61269b3e104SGarrett D'Amore * Find the media present on non-MII chips, and select the one to use. 61369b3e104SGarrett D'Amore */ 61469b3e104SGarrett D'Amore static void 61569b3e104SGarrett D'Amore elxl_probe_media(elxl_t *sc) 61669b3e104SGarrett D'Amore { 61769b3e104SGarrett D'Amore ex_media_t *exm; 61869b3e104SGarrett D'Amore uint32_t config; 61969b3e104SGarrett D'Amore uint32_t default_media; 62069b3e104SGarrett D'Amore uint16_t media_options; 62169b3e104SGarrett D'Amore 62269b3e104SGarrett D'Amore SET_WIN(3); 62369b3e104SGarrett D'Amore config = GET32(W3_INTERNAL_CONFIG); 62469b3e104SGarrett D'Amore media_options = GET16(W3_MEDIAOPT); 62569b3e104SGarrett D'Amore 62669b3e104SGarrett D'Amore /* 62769b3e104SGarrett D'Amore * We modify the media_options field so that we have a 62869b3e104SGarrett D'Amore * consistent view of the media available, without worrying 62969b3e104SGarrett D'Amore * about the version of ASIC, etc. 63069b3e104SGarrett D'Amore */ 63169b3e104SGarrett D'Amore 63269b3e104SGarrett D'Amore /* 63369b3e104SGarrett D'Amore * 100BASE-TX is handled differently on 90XB from 90X. Older 63469b3e104SGarrett D'Amore * parts use the external MII to provide this support. 63569b3e104SGarrett D'Amore */ 63669b3e104SGarrett D'Amore if (sc->ex_conf & CONF_90XB) { 63769b3e104SGarrett D'Amore if (media_options & MEDIAOPT_100TX) { 63869b3e104SGarrett D'Amore /* 63969b3e104SGarrett D'Amore * 3Com advises that we should only ever use the 64069b3e104SGarrett D'Amore * auto mode. Notably, it seems that there should 64169b3e104SGarrett D'Amore * never be a 90XB board with the MEDIAOPT_10T bit set 64269b3e104SGarrett D'Amore * without this bit. If it happens, the driver will 64369b3e104SGarrett D'Amore * run in compatible 10BASE-T only mode. 64469b3e104SGarrett D'Amore */ 64569b3e104SGarrett D'Amore media_options &= ~MEDIAOPT_10T; 64669b3e104SGarrett D'Amore } 64769b3e104SGarrett D'Amore } else { 64869b3e104SGarrett D'Amore if (media_options & MEDIAOPT_100TX) { 64969b3e104SGarrett D'Amore /* 65069b3e104SGarrett D'Amore * If this occurs, we really want to use it like 65169b3e104SGarrett D'Amore * an MII device. Generally in this situation we 65269b3e104SGarrett D'Amore * want to use the MII exclusively, and there ought 65369b3e104SGarrett D'Amore * not be a 10bT transceiver. 65469b3e104SGarrett D'Amore */ 65569b3e104SGarrett D'Amore media_options |= MEDIAOPT_MII; 65669b3e104SGarrett D'Amore media_options &= ~MEDIAOPT_100TX; 65769b3e104SGarrett D'Amore media_options &= ~MEDIAOPT_10T; 65869b3e104SGarrett D'Amore 65969b3e104SGarrett D'Amore /* 66069b3e104SGarrett D'Amore * Additionally, some of these devices map all 66169b3e104SGarrett D'Amore * internal PHY register at *every* address, not 66269b3e104SGarrett D'Amore * just the "allowed" address 24. 66369b3e104SGarrett D'Amore */ 66469b3e104SGarrett D'Amore sc->ex_conf |= CONF_INTPHY; 66569b3e104SGarrett D'Amore } 66669b3e104SGarrett D'Amore /* 66769b3e104SGarrett D'Amore * Early versions didn't have 10FL models, and used this 66869b3e104SGarrett D'Amore * bit for something else (VCO). 66969b3e104SGarrett D'Amore */ 67069b3e104SGarrett D'Amore media_options &= ~MEDIAOPT_10FL; 67169b3e104SGarrett D'Amore } 67269b3e104SGarrett D'Amore if (media_options & MEDIAOPT_100T4) { 67369b3e104SGarrett D'Amore /* 100BASE-T4 units all use the MII bus. */ 67469b3e104SGarrett D'Amore media_options |= MEDIAOPT_MII; 67569b3e104SGarrett D'Amore media_options &= ~MEDIAOPT_100T4; 67669b3e104SGarrett D'Amore } 67769b3e104SGarrett D'Amore 67869b3e104SGarrett D'Amore /* Save our media options. */ 67969b3e104SGarrett D'Amore sc->ex_mediaopt = media_options; 68069b3e104SGarrett D'Amore 68169b3e104SGarrett D'Amore #define APPEND_MEDIA(str, bit, name) \ 68269b3e104SGarrett D'Amore if (media_options & (bit)) { \ 68369b3e104SGarrett D'Amore (void) strlcat(str, *str ? "," : "", sizeof (str)); \ 68469b3e104SGarrett D'Amore (void) strlcat(str, name, sizeof (str)); \ 68569b3e104SGarrett D'Amore } 68669b3e104SGarrett D'Amore 68769b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, (MEDIAOPT_MII|MEDIAOPT_100TX), "mii"); 68869b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10T, "tp-hdx,tp-fdx"); 68969b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, MEDIAOPT_100FX, "fx-hdx,fx-fdx"); 69069b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, MEDIAOPT_BNC, "bnc"); 69169b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, MEDIAOPT_AUI, "aui"); 69269b3e104SGarrett D'Amore APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10FL, "fl-hdx,fl-fdx"); 69369b3e104SGarrett D'Amore 69469b3e104SGarrett D'Amore if (config & XCVR_SEL_100TX) { 69569b3e104SGarrett D'Amore /* Only found on 90XB. Don't use this, use AUTO instead! */ 69669b3e104SGarrett D'Amore config |= XCVR_SEL_AUTO; 69769b3e104SGarrett D'Amore config &= ~XCVR_SEL_100TX; 69869b3e104SGarrett D'Amore } 69969b3e104SGarrett D'Amore 70069b3e104SGarrett D'Amore default_media = (config & XCVR_SEL_MASK); 70169b3e104SGarrett D'Amore 70269b3e104SGarrett D'Amore /* Sanity check that there are any media! */ 70369b3e104SGarrett D'Amore if ((media_options & MEDIAOPT_MASK) == 0) { 70469b3e104SGarrett D'Amore elxl_error(sc, 70569b3e104SGarrett D'Amore "No media present? Attempting to use default."); 70669b3e104SGarrett D'Amore /* 70769b3e104SGarrett D'Amore * This "default" may be non-sensical. At worst it should 70869b3e104SGarrett D'Amore * cause a busted link. 70969b3e104SGarrett D'Amore */ 71069b3e104SGarrett D'Amore sc->ex_xcvr = default_media; 71169b3e104SGarrett D'Amore } 71269b3e104SGarrett D'Amore 71369b3e104SGarrett D'Amore for (exm = ex_native_media; exm->exm_mpbit != 0; exm++) { 71469b3e104SGarrett D'Amore if (media_options & exm->exm_mpbit) { 71569b3e104SGarrett D'Amore if (exm->exm_xcvr == default_media) { 71669b3e104SGarrett D'Amore /* preferred default is present, just use it */ 71769b3e104SGarrett D'Amore sc->ex_xcvr = default_media; 71869b3e104SGarrett D'Amore return; 71969b3e104SGarrett D'Amore } 72069b3e104SGarrett D'Amore 72169b3e104SGarrett D'Amore sc->ex_xcvr = exm->exm_xcvr; 72269b3e104SGarrett D'Amore /* but keep trying for other more preferred options */ 72369b3e104SGarrett D'Amore } 72469b3e104SGarrett D'Amore } 72569b3e104SGarrett D'Amore } 72669b3e104SGarrett D'Amore 72769b3e104SGarrett D'Amore /* 72869b3e104SGarrett D'Amore * Setup transmitter parameters. 72969b3e104SGarrett D'Amore */ 73069b3e104SGarrett D'Amore static void 73169b3e104SGarrett D'Amore elxl_setup_tx(elxl_t *sc) 73269b3e104SGarrett D'Amore { 73369b3e104SGarrett D'Amore /* 73469b3e104SGarrett D'Amore * Disable reclaim threshold for 90xB, set free threshold to 73569b3e104SGarrett D'Amore * 6 * 256 = 1536 for 90x. 73669b3e104SGarrett D'Amore */ 73769b3e104SGarrett D'Amore if (sc->ex_conf & CONF_90XB) 73869b3e104SGarrett D'Amore PUT_CMD(CMD_SET_TXRECLAIM | 255); 73969b3e104SGarrett D'Amore else 74069b3e104SGarrett D'Amore PUT8(REG_TXFREETHRESH, 6); 74169b3e104SGarrett D'Amore 74269b3e104SGarrett D'Amore /* 74369b3e104SGarrett D'Amore * We've seen underflows at the root cause of NIC hangs on 74469b3e104SGarrett D'Amore * older cards. Use a store-and-forward model to prevent that. 74569b3e104SGarrett D'Amore */ 74669b3e104SGarrett D'Amore PUT_CMD(CMD_SET_TXSTART | EX_BUFSZ >> 2); 74769b3e104SGarrett D'Amore } 74869b3e104SGarrett D'Amore 74969b3e104SGarrett D'Amore /* 75069b3e104SGarrett D'Amore * Bring device up. 75169b3e104SGarrett D'Amore */ 75269b3e104SGarrett D'Amore static void 75369b3e104SGarrett D'Amore elxl_init(elxl_t *sc) 75469b3e104SGarrett D'Amore { 75569b3e104SGarrett D'Amore if (sc->ex_suspended) 75669b3e104SGarrett D'Amore return; 75769b3e104SGarrett D'Amore 75869b3e104SGarrett D'Amore WAIT_CMD(sc); 75969b3e104SGarrett D'Amore elxl_stop(sc); 76069b3e104SGarrett D'Amore 76169b3e104SGarrett D'Amore PUT_CMD(CMD_RX_RESET); 76269b3e104SGarrett D'Amore WAIT_CMD(sc); 76369b3e104SGarrett D'Amore PUT_CMD(CMD_TX_RESET); 76469b3e104SGarrett D'Amore WAIT_CMD(sc); 76569b3e104SGarrett D'Amore 76669b3e104SGarrett D'Amore /* Load Tx parameters. */ 76769b3e104SGarrett D'Amore elxl_setup_tx(sc); 76869b3e104SGarrett D'Amore 76969b3e104SGarrett D'Amore PUT32(REG_DMACTRL, GET32(REG_DMACTRL) | DMACTRL_UPRXEAREN); 77069b3e104SGarrett D'Amore 77169b3e104SGarrett D'Amore PUT_CMD(CMD_IND_ENABLE | INT_WATCHED); 77269b3e104SGarrett D'Amore PUT_CMD(CMD_INT_ENABLE | INT_WATCHED); 77369b3e104SGarrett D'Amore 77469b3e104SGarrett D'Amore PUT_CMD(CMD_INT_ACK | 0xff); 77569b3e104SGarrett D'Amore 77669b3e104SGarrett D'Amore elxl_set_media(sc); 77769b3e104SGarrett D'Amore elxl_set_rxfilter(sc); 77869b3e104SGarrett D'Amore 77969b3e104SGarrett D'Amore /* Configure for VLAN tag sizing. */ 78069b3e104SGarrett D'Amore SET_WIN(3); 78169b3e104SGarrett D'Amore if (sc->ex_conf & CONF_90XB) { 78269b3e104SGarrett D'Amore PUT16(W3_MAX_PKT_SIZE, EX_BUFSZ); 78369b3e104SGarrett D'Amore } else { 78469b3e104SGarrett D'Amore PUT16(W3_MAC_CONTROL, GET16(W3_MAC_CONTROL) | 78569b3e104SGarrett D'Amore MAC_CONTROL_ALLOW_LARGE); 78669b3e104SGarrett D'Amore } 78769b3e104SGarrett D'Amore 78869b3e104SGarrett D'Amore PUT_CMD(CMD_SET_RXEARLY | (EX_BUFSZ >> 2)); 78969b3e104SGarrett D'Amore 79069b3e104SGarrett D'Amore PUT_CMD(CMD_STATS_ENABLE); 79169b3e104SGarrett D'Amore PUT_CMD(CMD_TX_ENABLE); 79269b3e104SGarrett D'Amore PUT32(REG_UPLISTPTR, sc->ex_rxring.r_paddr); 79369b3e104SGarrett D'Amore PUT_CMD(CMD_RX_ENABLE); 79469b3e104SGarrett D'Amore PUT_CMD(CMD_UP_UNSTALL); 79569b3e104SGarrett D'Amore } 79669b3e104SGarrett D'Amore 79769b3e104SGarrett D'Amore /* 79869b3e104SGarrett D'Amore * Set multicast receive filter. Also take care of promiscuous mode. 79969b3e104SGarrett D'Amore * Note that *some* of this hardware is fully capable of either a 256 80069b3e104SGarrett D'Amore * or 64 bit multicast hash. However, we can't determine what the 80169b3e104SGarrett D'Amore * size of the hash table is easily, and so we are expected to be able 80269b3e104SGarrett D'Amore * to resubmit the entire list of addresses each time. This puts an 80369b3e104SGarrett D'Amore * onerous burden on the driver to maintain its list of multicast 80469b3e104SGarrett D'Amore * addresses. Since multicast stuff is usually not that performance 80569b3e104SGarrett D'Amore * sensitive, and since we don't usually have much of it, we are just 80669b3e104SGarrett D'Amore * going to skip it. We allow the upper layers to filter it, as 80769b3e104SGarrett D'Amore * needed, by setting the all-multicast bit if the hardware can do it. 80869b3e104SGarrett D'Amore * This also reduces our test burden. 80969b3e104SGarrett D'Amore */ 81069b3e104SGarrett D'Amore static void 81169b3e104SGarrett D'Amore elxl_set_rxfilter(elxl_t *sc) 81269b3e104SGarrett D'Amore { 81369b3e104SGarrett D'Amore uint16_t mask = FILTER_UNICAST | FILTER_ALLBCAST; 81469b3e104SGarrett D'Amore 81569b3e104SGarrett D'Amore if (sc->ex_suspended) 81669b3e104SGarrett D'Amore return; 81769b3e104SGarrett D'Amore 81869b3e104SGarrett D'Amore /* 81969b3e104SGarrett D'Amore * Set the station address and clear the station mask. The latter 82069b3e104SGarrett D'Amore * is needed for 90x cards, 0 is the default for 90xB cards. 82169b3e104SGarrett D'Amore */ 82269b3e104SGarrett D'Amore SET_WIN(2); 82369b3e104SGarrett D'Amore for (int i = 0; i < ETHERADDRL; i++) { 82469b3e104SGarrett D'Amore PUT8(W2_STATION_ADDRESS + i, sc->ex_curraddr[i]); 82569b3e104SGarrett D'Amore PUT8(W2_STATION_MASK + i, 0); 82669b3e104SGarrett D'Amore } 82769b3e104SGarrett D'Amore 82869b3e104SGarrett D'Amore if (sc->ex_mccount) { 82969b3e104SGarrett D'Amore mask |= FILTER_ALLMULTI; 83069b3e104SGarrett D'Amore } 83169b3e104SGarrett D'Amore if (sc->ex_promisc) { 83269b3e104SGarrett D'Amore mask |= FILTER_PROMISC; 83369b3e104SGarrett D'Amore } 83469b3e104SGarrett D'Amore PUT_CMD(CMD_SET_FILTER | mask); 83569b3e104SGarrett D'Amore } 83669b3e104SGarrett D'Amore 83769b3e104SGarrett D'Amore static void 83869b3e104SGarrett D'Amore elxl_set_media(elxl_t *sc) 83969b3e104SGarrett D'Amore { 84069b3e104SGarrett D'Amore uint32_t configreg; 84169b3e104SGarrett D'Amore 84269b3e104SGarrett D'Amore SET_WIN(4); 84369b3e104SGarrett D'Amore PUT16(W4_MEDIASTAT, 0); 84469b3e104SGarrett D'Amore PUT_CMD(CMD_BNC_DISABLE); 84569b3e104SGarrett D'Amore drv_usecwait(800); 84669b3e104SGarrett D'Amore 84769b3e104SGarrett D'Amore /* 84869b3e104SGarrett D'Amore * Now turn on the selected media/transceiver. 84969b3e104SGarrett D'Amore */ 85069b3e104SGarrett D'Amore switch (sc->ex_xcvr) { 85169b3e104SGarrett D'Amore case XCVR_SEL_10T: 85269b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; 85369b3e104SGarrett D'Amore PUT16(W4_MEDIASTAT, 85469b3e104SGarrett D'Amore MEDIASTAT_JABGUARD_EN | MEDIASTAT_LINKBEAT_EN); 85569b3e104SGarrett D'Amore drv_usecwait(800); 85669b3e104SGarrett D'Amore break; 85769b3e104SGarrett D'Amore 85869b3e104SGarrett D'Amore case XCVR_SEL_BNC: 85969b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; 86069b3e104SGarrett D'Amore PUT_CMD(CMD_BNC_ENABLE); 86169b3e104SGarrett D'Amore drv_usecwait(800); 86269b3e104SGarrett D'Amore break; 86369b3e104SGarrett D'Amore 86469b3e104SGarrett D'Amore case XCVR_SEL_100FX: 86569b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; /* Is this really true? */ 86669b3e104SGarrett D'Amore PUT16(W4_MEDIASTAT, MEDIASTAT_LINKBEAT_EN); 86769b3e104SGarrett D'Amore drv_usecwait(800); 86869b3e104SGarrett D'Amore break; 86969b3e104SGarrett D'Amore 87069b3e104SGarrett D'Amore case XCVR_SEL_AUI: 87169b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; 87269b3e104SGarrett D'Amore PUT16(W4_MEDIASTAT, MEDIASTAT_SQE_EN); 87369b3e104SGarrett D'Amore drv_usecwait(800); 87469b3e104SGarrett D'Amore break; 87569b3e104SGarrett D'Amore 87669b3e104SGarrett D'Amore case XCVR_SEL_AUTO: 87769b3e104SGarrett D'Amore case XCVR_SEL_MII: 87869b3e104SGarrett D'Amore /* 87969b3e104SGarrett D'Amore * This is due to paranoia. If a card claims 88069b3e104SGarrett D'Amore * to default to MII, but doesn't have it set in 88169b3e104SGarrett D'Amore * media options, then we don't want to leave 88269b3e104SGarrett D'Amore * the MII active or we'll have problems derferencing 88369b3e104SGarrett D'Amore * the "mii handle". 88469b3e104SGarrett D'Amore */ 88569b3e104SGarrett D'Amore if (sc->ex_miih) { 88669b3e104SGarrett D'Amore sc->ex_mii_active = B_TRUE; 88769b3e104SGarrett D'Amore } else { 88869b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; 88969b3e104SGarrett D'Amore } 89069b3e104SGarrett D'Amore break; 89169b3e104SGarrett D'Amore 89269b3e104SGarrett D'Amore default: 89369b3e104SGarrett D'Amore sc->ex_mii_active = B_FALSE; 89469b3e104SGarrett D'Amore elxl_error(sc, "Impossible media setting!"); 89569b3e104SGarrett D'Amore break; 89669b3e104SGarrett D'Amore } 89769b3e104SGarrett D'Amore 89869b3e104SGarrett D'Amore SET_WIN(3); 89969b3e104SGarrett D'Amore configreg = GET32(W3_INTERNAL_CONFIG); 90069b3e104SGarrett D'Amore 90169b3e104SGarrett D'Amore configreg &= ~(XCVR_SEL_MASK); 90269b3e104SGarrett D'Amore configreg |= (sc->ex_xcvr); 90369b3e104SGarrett D'Amore 90469b3e104SGarrett D'Amore PUT32(W3_INTERNAL_CONFIG, configreg); 90569b3e104SGarrett D'Amore 90669b3e104SGarrett D'Amore /* 90769b3e104SGarrett D'Amore * If we're not using MII, force the full-duplex setting. MII 90869b3e104SGarrett D'Amore * based modes handle the full-duplex setting via the MII 90969b3e104SGarrett D'Amore * notify callback. 91069b3e104SGarrett D'Amore */ 91169b3e104SGarrett D'Amore if (!sc->ex_mii_active) { 91269b3e104SGarrett D'Amore uint16_t mctl; 91369b3e104SGarrett D'Amore mctl = GET16(W3_MAC_CONTROL); 91469b3e104SGarrett D'Amore if (sc->ex_fdx) { 91569b3e104SGarrett D'Amore mctl |= MAC_CONTROL_FDX; 91669b3e104SGarrett D'Amore } else { 91769b3e104SGarrett D'Amore mctl &= ~MAC_CONTROL_FDX; 91869b3e104SGarrett D'Amore } 91969b3e104SGarrett D'Amore PUT16(W3_MAC_CONTROL, mctl); 92069b3e104SGarrett D'Amore } 92169b3e104SGarrett D'Amore } 92269b3e104SGarrett D'Amore 92369b3e104SGarrett D'Amore /* 92469b3e104SGarrett D'Amore * Get currently-selected media from card. 92569b3e104SGarrett D'Amore * (if_media callback, may be called before interface is brought up). 92669b3e104SGarrett D'Amore */ 92769b3e104SGarrett D'Amore static void 92869b3e104SGarrett D'Amore elxl_linkcheck(void *arg) 92969b3e104SGarrett D'Amore { 93069b3e104SGarrett D'Amore elxl_t *sc = arg; 93169b3e104SGarrett D'Amore uint16_t stat; 93269b3e104SGarrett D'Amore link_state_t link; 93369b3e104SGarrett D'Amore 93469b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 93569b3e104SGarrett D'Amore if (sc->ex_mii_active) { 93669b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 93769b3e104SGarrett D'Amore return; 93869b3e104SGarrett D'Amore } 93969b3e104SGarrett D'Amore if (sc->ex_running && !sc->ex_suspended) { 94069b3e104SGarrett D'Amore switch (sc->ex_xcvr) { 94169b3e104SGarrett D'Amore case XCVR_SEL_100FX: 94269b3e104SGarrett D'Amore /* these media we can detect link on */ 94369b3e104SGarrett D'Amore SET_WIN(4); 94469b3e104SGarrett D'Amore stat = GET16(W4_MEDIASTAT); 94569b3e104SGarrett D'Amore if (stat & MEDIASTAT_LINKDETECT) { 94669b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_UP; 94769b3e104SGarrett D'Amore sc->ex_speed = 100000000; 94869b3e104SGarrett D'Amore } else { 94969b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_DOWN; 95069b3e104SGarrett D'Amore sc->ex_speed = 0; 95169b3e104SGarrett D'Amore } 95269b3e104SGarrett D'Amore break; 95369b3e104SGarrett D'Amore 95469b3e104SGarrett D'Amore case XCVR_SEL_10T: 95569b3e104SGarrett D'Amore /* these media we can detect link on */ 95669b3e104SGarrett D'Amore SET_WIN(4); 95769b3e104SGarrett D'Amore stat = GET16(W4_MEDIASTAT); 95869b3e104SGarrett D'Amore if (stat & MEDIASTAT_LINKDETECT) { 95969b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_UP; 96069b3e104SGarrett D'Amore sc->ex_speed = 10000000; 96169b3e104SGarrett D'Amore } else { 96269b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_DOWN; 96369b3e104SGarrett D'Amore sc->ex_speed = 0; 96469b3e104SGarrett D'Amore } 96569b3e104SGarrett D'Amore break; 96669b3e104SGarrett D'Amore 96769b3e104SGarrett D'Amore case XCVR_SEL_BNC: 96869b3e104SGarrett D'Amore case XCVR_SEL_AUI: 96969b3e104SGarrett D'Amore default: 97069b3e104SGarrett D'Amore /* 97169b3e104SGarrett D'Amore * For these we don't really know the answer, 97269b3e104SGarrett D'Amore * but if we lie then at least it won't cause 97369b3e104SGarrett D'Amore * ifconfig to turn off the RUNNING flag. 97469b3e104SGarrett D'Amore * This is necessary because we might 97569b3e104SGarrett D'Amore * transition from LINK_STATE_DOWN when 97669b3e104SGarrett D'Amore * switching media. 97769b3e104SGarrett D'Amore */ 97869b3e104SGarrett D'Amore sc->ex_speed = 10000000; 97969b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_UP; 98069b3e104SGarrett D'Amore break; 98169b3e104SGarrett D'Amore } 98269b3e104SGarrett D'Amore SET_WIN(3); 98369b3e104SGarrett D'Amore sc->ex_duplex = GET16(W3_MAC_CONTROL) & MAC_CONTROL_FDX ? 98469b3e104SGarrett D'Amore LINK_DUPLEX_FULL : LINK_DUPLEX_HALF; 98569b3e104SGarrett D'Amore } else { 98669b3e104SGarrett D'Amore sc->ex_speed = 0; 98769b3e104SGarrett D'Amore sc->ex_duplex = LINK_DUPLEX_UNKNOWN; 98869b3e104SGarrett D'Amore sc->ex_link = LINK_STATE_UNKNOWN; 98969b3e104SGarrett D'Amore } 99069b3e104SGarrett D'Amore link = sc->ex_link; 99169b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 99269b3e104SGarrett D'Amore 99369b3e104SGarrett D'Amore mac_link_update(sc->ex_mach, link); 99469b3e104SGarrett D'Amore } 99569b3e104SGarrett D'Amore 99669b3e104SGarrett D'Amore static int 99769b3e104SGarrett D'Amore elxl_m_promisc(void *arg, boolean_t on) 99869b3e104SGarrett D'Amore { 99969b3e104SGarrett D'Amore elxl_t *sc = arg; 100069b3e104SGarrett D'Amore 100169b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 100269b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 100369b3e104SGarrett D'Amore sc->ex_promisc = on; 100469b3e104SGarrett D'Amore elxl_set_rxfilter(sc); 100569b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 100669b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 100769b3e104SGarrett D'Amore return (0); 100869b3e104SGarrett D'Amore } 100969b3e104SGarrett D'Amore 101069b3e104SGarrett D'Amore static int 101169b3e104SGarrett D'Amore elxl_m_multicst(void *arg, boolean_t add, const uint8_t *addr) 101269b3e104SGarrett D'Amore { 101369b3e104SGarrett D'Amore elxl_t *sc = arg; 101469b3e104SGarrett D'Amore 101569b3e104SGarrett D'Amore _NOTE(ARGUNUSED(addr)); 101669b3e104SGarrett D'Amore 101769b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 101869b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 101969b3e104SGarrett D'Amore if (add) { 102069b3e104SGarrett D'Amore sc->ex_mccount++; 102169b3e104SGarrett D'Amore if (sc->ex_mccount == 1) { 102269b3e104SGarrett D'Amore elxl_set_rxfilter(sc); 102369b3e104SGarrett D'Amore } 102469b3e104SGarrett D'Amore } else { 102569b3e104SGarrett D'Amore sc->ex_mccount--; 102669b3e104SGarrett D'Amore if (sc->ex_mccount == 0) { 102769b3e104SGarrett D'Amore elxl_set_rxfilter(sc); 102869b3e104SGarrett D'Amore } 102969b3e104SGarrett D'Amore } 103069b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 103169b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 103269b3e104SGarrett D'Amore return (0); 103369b3e104SGarrett D'Amore } 103469b3e104SGarrett D'Amore 103569b3e104SGarrett D'Amore static int 103669b3e104SGarrett D'Amore elxl_m_unicst(void *arg, const uint8_t *addr) 103769b3e104SGarrett D'Amore { 103869b3e104SGarrett D'Amore elxl_t *sc = arg; 103969b3e104SGarrett D'Amore 104069b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 104169b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 104269b3e104SGarrett D'Amore bcopy(addr, sc->ex_curraddr, ETHERADDRL); 104369b3e104SGarrett D'Amore elxl_set_rxfilter(sc); 104469b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 104569b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 104669b3e104SGarrett D'Amore 104769b3e104SGarrett D'Amore return (0); 104869b3e104SGarrett D'Amore } 104969b3e104SGarrett D'Amore 105069b3e104SGarrett D'Amore static mblk_t * 105169b3e104SGarrett D'Amore elxl_m_tx(void *arg, mblk_t *mp) 105269b3e104SGarrett D'Amore { 105369b3e104SGarrett D'Amore elxl_t *sc = arg; 105469b3e104SGarrett D'Amore ex_desc_t *txd; 105569b3e104SGarrett D'Amore ex_desc_t *first; 105669b3e104SGarrett D'Amore ex_desc_t *tail; 105769b3e104SGarrett D'Amore size_t len; 105869b3e104SGarrett D'Amore ex_ring_t *r; 105969b3e104SGarrett D'Amore ex_pd_t *pd; 106069b3e104SGarrett D'Amore uint32_t cflags; 106169b3e104SGarrett D'Amore mblk_t *nmp; 106269b3e104SGarrett D'Amore boolean_t reenable = B_FALSE; 106369b3e104SGarrett D'Amore boolean_t reset = B_FALSE; 106469b3e104SGarrett D'Amore uint32_t paddr; 106569b3e104SGarrett D'Amore 106669b3e104SGarrett D'Amore r = &sc->ex_txring; 106769b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 106869b3e104SGarrett D'Amore if (sc->ex_suspended) { 106969b3e104SGarrett D'Amore while (mp != NULL) { 107069b3e104SGarrett D'Amore sc->ex_nocarrier++; 107169b3e104SGarrett D'Amore nmp = mp->b_next; 107269b3e104SGarrett D'Amore freemsg(mp); 107369b3e104SGarrett D'Amore mp = nmp; 107469b3e104SGarrett D'Amore } 107569b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 107669b3e104SGarrett D'Amore return (NULL); 107769b3e104SGarrett D'Amore } 107869b3e104SGarrett D'Amore 107969b3e104SGarrett D'Amore for (int limit = (EX_NTX * 2); limit; limit--) { 108069b3e104SGarrett D'Amore uint8_t stat = GET8(REG_TXSTATUS); 108169b3e104SGarrett D'Amore if ((stat & TXSTATUS_COMPLETE) == 0) { 108269b3e104SGarrett D'Amore break; 108369b3e104SGarrett D'Amore } 108469b3e104SGarrett D'Amore if (stat & TXSTATUS_MAXCOLLISIONS) { 108569b3e104SGarrett D'Amore reenable = B_TRUE; 108669b3e104SGarrett D'Amore sc->ex_excoll++; 108769b3e104SGarrett D'Amore } 108869b3e104SGarrett D'Amore if ((stat & TXSTATUS_ERRS) != 0) { 108969b3e104SGarrett D'Amore reset = B_TRUE; 109069b3e104SGarrett D'Amore if (stat & TXSTATUS_JABBER) { 109169b3e104SGarrett D'Amore sc->ex_jabber++; 109269b3e104SGarrett D'Amore } 109369b3e104SGarrett D'Amore if (stat & TXSTATUS_RECLAIM_ERR) { 109469b3e104SGarrett D'Amore sc->ex_txerr++; 109569b3e104SGarrett D'Amore } 109669b3e104SGarrett D'Amore if (stat & TXSTATUS_UNDERRUN) { 109769b3e104SGarrett D'Amore sc->ex_uflo++; 109869b3e104SGarrett D'Amore } 109969b3e104SGarrett D'Amore } 110069b3e104SGarrett D'Amore PUT8(REG_TXSTATUS, 0); 110169b3e104SGarrett D'Amore } 110269b3e104SGarrett D'Amore 110369b3e104SGarrett D'Amore if (reset || reenable) { 110469b3e104SGarrett D'Amore paddr = GET32(REG_DNLISTPTR); 110569b3e104SGarrett D'Amore if (reset) { 110669b3e104SGarrett D'Amore WAIT_CMD(sc); 110769b3e104SGarrett D'Amore PUT_CMD(CMD_TX_RESET); 110869b3e104SGarrett D'Amore WAIT_CMD(sc); 110969b3e104SGarrett D'Amore elxl_setup_tx(sc); 111069b3e104SGarrett D'Amore } 111169b3e104SGarrett D'Amore PUT_CMD(CMD_TX_ENABLE); 111269b3e104SGarrett D'Amore if (paddr) { 111369b3e104SGarrett D'Amore PUT32(REG_DNLISTPTR, paddr); 111469b3e104SGarrett D'Amore } 111569b3e104SGarrett D'Amore } 111669b3e104SGarrett D'Amore 111769b3e104SGarrett D'Amore /* first reclaim any free descriptors */ 111869b3e104SGarrett D'Amore while (r->r_avail < r->r_count) { 111969b3e104SGarrett D'Amore 112069b3e104SGarrett D'Amore paddr = GET32(REG_DNLISTPTR); 112169b3e104SGarrett D'Amore txd = r->r_head; 112269b3e104SGarrett D'Amore if (paddr == txd->ed_descaddr) { 112369b3e104SGarrett D'Amore /* still processing this one, we're done */ 112469b3e104SGarrett D'Amore break; 112569b3e104SGarrett D'Amore } 112669b3e104SGarrett D'Amore if (paddr == 0) { 112769b3e104SGarrett D'Amore /* done processing the entire list! */ 112869b3e104SGarrett D'Amore r->r_head = NULL; 112969b3e104SGarrett D'Amore r->r_tail = NULL; 113069b3e104SGarrett D'Amore r->r_avail = r->r_count; 113169b3e104SGarrett D'Amore break; 113269b3e104SGarrett D'Amore } 113369b3e104SGarrett D'Amore r->r_avail++; 113469b3e104SGarrett D'Amore r->r_head = txd->ed_next; 113569b3e104SGarrett D'Amore } 113669b3e104SGarrett D'Amore 113769b3e104SGarrett D'Amore if ((r->r_avail < r->r_count) && (GET32(REG_DNLISTPTR) != 0)) { 113869b3e104SGarrett D'Amore PUT_CMD(CMD_DN_STALL); 113969b3e104SGarrett D'Amore WAIT_CMD(sc); 114069b3e104SGarrett D'Amore } 114169b3e104SGarrett D'Amore 114269b3e104SGarrett D'Amore first = NULL; 114369b3e104SGarrett D'Amore tail = r->r_tail; 114469b3e104SGarrett D'Amore 114569b3e104SGarrett D'Amore /* 114669b3e104SGarrett D'Amore * If there is already a tx list, select the next desc on the list. 114769b3e104SGarrett D'Amore * Otherwise, just pick the first descriptor. 114869b3e104SGarrett D'Amore */ 114969b3e104SGarrett D'Amore txd = tail ? tail->ed_next : &r->r_desc[0]; 115069b3e104SGarrett D'Amore 115169b3e104SGarrett D'Amore while ((mp != NULL) && (r->r_avail)) { 115269b3e104SGarrett D'Amore 115369b3e104SGarrett D'Amore nmp = mp->b_next; 115469b3e104SGarrett D'Amore 115569b3e104SGarrett D'Amore len = msgsize(mp); 115669b3e104SGarrett D'Amore if (len > (ETHERMAX + VLAN_TAGSZ)) { 115769b3e104SGarrett D'Amore sc->ex_txerr++; 115869b3e104SGarrett D'Amore freemsg(mp); 115969b3e104SGarrett D'Amore mp = nmp; 116069b3e104SGarrett D'Amore continue; 116169b3e104SGarrett D'Amore } 116269b3e104SGarrett D'Amore 116369b3e104SGarrett D'Amore cflags = 0; 116469b3e104SGarrett D'Amore if ((sc->ex_conf & CONF_90XB) != 0) { 116569b3e104SGarrett D'Amore uint32_t pflags; 116669b3e104SGarrett D'Amore hcksum_retrieve(mp, NULL, NULL, NULL, NULL, NULL, NULL, 116769b3e104SGarrett D'Amore &pflags); 116869b3e104SGarrett D'Amore if (pflags & HCK_IPV4_HDRCKSUM) { 116969b3e104SGarrett D'Amore cflags |= EX_DPD_IPCKSUM; 117069b3e104SGarrett D'Amore } 117169b3e104SGarrett D'Amore if (pflags & HCK_FULLCKSUM) { 117269b3e104SGarrett D'Amore cflags |= (EX_DPD_TCPCKSUM | EX_DPD_UDPCKSUM); 117369b3e104SGarrett D'Amore } 117469b3e104SGarrett D'Amore } 117569b3e104SGarrett D'Amore 117669b3e104SGarrett D'Amore /* Mark this descriptor is in use. We're committed now. */ 117769b3e104SGarrett D'Amore mcopymsg(mp, txd->ed_buf); /* frees the mblk! */ 117869b3e104SGarrett D'Amore r->r_avail--; 117969b3e104SGarrett D'Amore mp = nmp; 118069b3e104SGarrett D'Amore 118169b3e104SGarrett D'Amore /* Accounting stuff. */ 118269b3e104SGarrett D'Amore sc->ex_opackets++; 118369b3e104SGarrett D'Amore sc->ex_obytes += len; 118469b3e104SGarrett D'Amore if (txd->ed_buf[0] & 0x1) { 118569b3e104SGarrett D'Amore if (bcmp(txd->ed_buf, ex_broadcast, ETHERADDRL) != 0) { 118669b3e104SGarrett D'Amore sc->ex_multixmt++; 118769b3e104SGarrett D'Amore } else { 118869b3e104SGarrett D'Amore sc->ex_brdcstxmt++; 118969b3e104SGarrett D'Amore } 119069b3e104SGarrett D'Amore } 119169b3e104SGarrett D'Amore 119269b3e104SGarrett D'Amore pd = txd->ed_pd; 119369b3e104SGarrett D'Amore 119469b3e104SGarrett D'Amore 119569b3e104SGarrett D'Amore /* 119669b3e104SGarrett D'Amore * Zero pad the frame if its too short. This 119769b3e104SGarrett D'Amore * also avoids a checksum offload bug. 119869b3e104SGarrett D'Amore */ 119969b3e104SGarrett D'Amore if (len < 30) { 120069b3e104SGarrett D'Amore bzero(txd->ed_buf + len, ETHERMIN - len); 120169b3e104SGarrett D'Amore len = ETHERMIN; 120269b3e104SGarrett D'Amore } 120369b3e104SGarrett D'Amore 120469b3e104SGarrett D'Amore /* 120569b3e104SGarrett D'Amore * If this our first packet so far, record the head 120669b3e104SGarrett D'Amore * of the list. 120769b3e104SGarrett D'Amore */ 120869b3e104SGarrett D'Amore if (first == NULL) { 120969b3e104SGarrett D'Amore first = txd; 121069b3e104SGarrett D'Amore } 121169b3e104SGarrett D'Amore 121269b3e104SGarrett D'Amore (void) ddi_dma_sync(txd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); 121369b3e104SGarrett D'Amore 121469b3e104SGarrett D'Amore PUT_PD(r, pd->pd_link, 0); 121569b3e104SGarrett D'Amore PUT_PD(r, pd->pd_fsh, len | cflags); 121669b3e104SGarrett D'Amore PUT_PD(r, pd->pd_addr, txd->ed_bufaddr); 121769b3e104SGarrett D'Amore PUT_PD(r, pd->pd_len, len | EX_FR_LAST); 121869b3e104SGarrett D'Amore 121969b3e104SGarrett D'Amore /* 122069b3e104SGarrett D'Amore * Write the link into the previous descriptor. Note that 122169b3e104SGarrett D'Amore * if this is the first packet (so no previous queued), this 122269b3e104SGarrett D'Amore * will be benign because the previous descriptor won't be 122369b3e104SGarrett D'Amore * on any tx list. (Furthermore, we'll clear its link field 122469b3e104SGarrett D'Amore * when we do later use it.) 122569b3e104SGarrett D'Amore */ 122669b3e104SGarrett D'Amore PUT_PD(r, txd->ed_prev->ed_pd->pd_link, txd->ed_descaddr); 122769b3e104SGarrett D'Amore } 122869b3e104SGarrett D'Amore 122969b3e104SGarrett D'Amore /* 123069b3e104SGarrett D'Amore * Are we submitting any packets? 123169b3e104SGarrett D'Amore */ 123269b3e104SGarrett D'Amore if (first != NULL) { 123369b3e104SGarrett D'Amore /* Interrupt on the last packet. */ 123469b3e104SGarrett D'Amore PUT_PD(r, pd->pd_fsh, len | cflags | EX_DPD_DNIND); 123569b3e104SGarrett D'Amore 123669b3e104SGarrett D'Amore if (tail == NULL) { 123769b3e104SGarrett D'Amore /* No packets pending, so its a new list head! */ 123869b3e104SGarrett D'Amore r->r_head = first; 123969b3e104SGarrett D'Amore } else { 124069b3e104SGarrett D'Amore pd = tail->ed_pd; 124169b3e104SGarrett D'Amore /* We've added frames, so don't interrupt mid-list. */ 124269b3e104SGarrett D'Amore PUT_PD(r, pd->pd_fsh, 124369b3e104SGarrett D'Amore GET_PD(r, pd->pd_fsh) & ~(EX_DPD_DNIND)); 124469b3e104SGarrett D'Amore } 124569b3e104SGarrett D'Amore /* Record the last descriptor. */ 124669b3e104SGarrett D'Amore r->r_tail = txd; 124769b3e104SGarrett D'Amore 124869b3e104SGarrett D'Amore /* flush the entire ring - we're stopped so its safe */ 124969b3e104SGarrett D'Amore (void) ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); 125069b3e104SGarrett D'Amore } 125169b3e104SGarrett D'Amore 125269b3e104SGarrett D'Amore /* Restart transmitter. */ 125369b3e104SGarrett D'Amore if (sc->ex_txring.r_head) { 125469b3e104SGarrett D'Amore PUT32(REG_DNLISTPTR, sc->ex_txring.r_head->ed_descaddr); 125569b3e104SGarrett D'Amore } 125669b3e104SGarrett D'Amore PUT_CMD(CMD_DN_UNSTALL); 125769b3e104SGarrett D'Amore 125869b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 125969b3e104SGarrett D'Amore 126069b3e104SGarrett D'Amore return (mp); 126169b3e104SGarrett D'Amore } 126269b3e104SGarrett D'Amore 126369b3e104SGarrett D'Amore static mblk_t * 126469b3e104SGarrett D'Amore elxl_recv(elxl_t *sc, ex_desc_t *rxd, uint32_t stat) 126569b3e104SGarrett D'Amore { 126669b3e104SGarrett D'Amore mblk_t *mp = NULL; 126769b3e104SGarrett D'Amore uint32_t len; 126869b3e104SGarrett D'Amore 126969b3e104SGarrett D'Amore len = stat & EX_UPD_PKTLENMASK; 127069b3e104SGarrett D'Amore if (stat & (EX_UPD_ERR_VLAN | EX_UPD_OVERFLOW)) { 127169b3e104SGarrett D'Amore if (stat & EX_UPD_RUNT) { 127269b3e104SGarrett D'Amore sc->ex_runt++; 127369b3e104SGarrett D'Amore } 127469b3e104SGarrett D'Amore if (stat & EX_UPD_OVERRUN) { 127569b3e104SGarrett D'Amore sc->ex_oflo++; 127669b3e104SGarrett D'Amore } 127769b3e104SGarrett D'Amore if (stat & EX_UPD_CRCERR) { 127869b3e104SGarrett D'Amore sc->ex_fcs++; 127969b3e104SGarrett D'Amore } 128069b3e104SGarrett D'Amore if (stat & EX_UPD_ALIGNERR) { 128169b3e104SGarrett D'Amore sc->ex_align++; 128269b3e104SGarrett D'Amore } 128369b3e104SGarrett D'Amore if (stat & EX_UPD_OVERFLOW) { 128469b3e104SGarrett D'Amore sc->ex_toolong++; 128569b3e104SGarrett D'Amore } 128669b3e104SGarrett D'Amore return (NULL); 128769b3e104SGarrett D'Amore } 128869b3e104SGarrett D'Amore if (len < sizeof (struct ether_header)) { 128969b3e104SGarrett D'Amore sc->ex_runt++; 129069b3e104SGarrett D'Amore return (NULL); 129169b3e104SGarrett D'Amore } 129269b3e104SGarrett D'Amore if (len > (ETHERMAX + VLAN_TAGSZ)) { 129369b3e104SGarrett D'Amore /* Allow four bytes for the VLAN header */ 129469b3e104SGarrett D'Amore sc->ex_toolong++; 129569b3e104SGarrett D'Amore return (NULL); 129669b3e104SGarrett D'Amore } 129769b3e104SGarrett D'Amore if ((mp = allocb(len + 14, BPRI_HI)) == NULL) { 129869b3e104SGarrett D'Amore sc->ex_allocbfail++; 129969b3e104SGarrett D'Amore return (NULL); 130069b3e104SGarrett D'Amore } 130169b3e104SGarrett D'Amore 13025109e3feSGarrett D'Amore (void) ddi_dma_sync(rxd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL); 130369b3e104SGarrett D'Amore mp->b_rptr += 14; 130469b3e104SGarrett D'Amore mp->b_wptr = mp->b_rptr + len; 130569b3e104SGarrett D'Amore bcopy(rxd->ed_buf, mp->b_rptr, len); 130669b3e104SGarrett D'Amore 130769b3e104SGarrett D'Amore sc->ex_ipackets++; 130869b3e104SGarrett D'Amore sc->ex_ibytes += len; 130969b3e104SGarrett D'Amore if (rxd->ed_buf[0] & 0x1) { 131069b3e104SGarrett D'Amore if (bcmp(rxd->ed_buf, ex_broadcast, ETHERADDRL) != 0) { 131169b3e104SGarrett D'Amore sc->ex_multircv++; 131269b3e104SGarrett D'Amore } else { 131369b3e104SGarrett D'Amore sc->ex_brdcstrcv++; 131469b3e104SGarrett D'Amore } 131569b3e104SGarrett D'Amore } 131669b3e104SGarrett D'Amore 131769b3e104SGarrett D'Amore /* 131869b3e104SGarrett D'Amore * Set the incoming checksum information for the packet. 131969b3e104SGarrett D'Amore */ 132069b3e104SGarrett D'Amore if (((sc->ex_conf & CONF_90XB) != 0) && 132169b3e104SGarrett D'Amore ((stat & EX_UPD_IPCHECKED) != 0) && 132269b3e104SGarrett D'Amore ((stat & (EX_UPD_CKSUMERR)) == 0)) { 132369b3e104SGarrett D'Amore uint32_t pflags = 0; 132469b3e104SGarrett D'Amore if (stat & EX_UPD_IPCHECKED) { 132569b3e104SGarrett D'Amore pflags |= HCK_IPV4_HDRCKSUM; 132669b3e104SGarrett D'Amore } 132769b3e104SGarrett D'Amore if (stat & (EX_UPD_TCPCHECKED | EX_UPD_UDPCHECKED)) { 132869b3e104SGarrett D'Amore pflags |= (HCK_FULLCKSUM | HCK_FULLCKSUM_OK); 132969b3e104SGarrett D'Amore } 133069b3e104SGarrett D'Amore (void) hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0, pflags, 0); 133169b3e104SGarrett D'Amore } 133269b3e104SGarrett D'Amore 133369b3e104SGarrett D'Amore return (mp); 133469b3e104SGarrett D'Amore } 133569b3e104SGarrett D'Amore 133669b3e104SGarrett D'Amore static int 133769b3e104SGarrett D'Amore elxl_m_start(void *arg) 133869b3e104SGarrett D'Amore { 133969b3e104SGarrett D'Amore elxl_t *sc = arg; 134069b3e104SGarrett D'Amore 134169b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 134269b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 134369b3e104SGarrett D'Amore 134469b3e104SGarrett D'Amore elxl_init(sc); 134569b3e104SGarrett D'Amore sc->ex_running = B_TRUE; 134669b3e104SGarrett D'Amore 134769b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 134869b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 134969b3e104SGarrett D'Amore 135069b3e104SGarrett D'Amore if (sc->ex_miih) { 135169b3e104SGarrett D'Amore mii_start(sc->ex_miih); 135269b3e104SGarrett D'Amore } 135369b3e104SGarrett D'Amore return (0); 135469b3e104SGarrett D'Amore } 135569b3e104SGarrett D'Amore 135669b3e104SGarrett D'Amore static void 135769b3e104SGarrett D'Amore elxl_m_stop(void *arg) 135869b3e104SGarrett D'Amore { 135969b3e104SGarrett D'Amore elxl_t *sc = arg; 136069b3e104SGarrett D'Amore 136169b3e104SGarrett D'Amore if (sc->ex_miih) { 136269b3e104SGarrett D'Amore mii_stop(sc->ex_miih); 136369b3e104SGarrett D'Amore } 136469b3e104SGarrett D'Amore 136569b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 136669b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 136769b3e104SGarrett D'Amore 136869b3e104SGarrett D'Amore elxl_stop(sc); 136969b3e104SGarrett D'Amore sc->ex_running = B_FALSE; 137069b3e104SGarrett D'Amore 137169b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 137269b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 137369b3e104SGarrett D'Amore } 137469b3e104SGarrett D'Amore 137569b3e104SGarrett D'Amore static boolean_t 137669b3e104SGarrett D'Amore elxl_m_getcapab(void *arg, mac_capab_t cap, void *data) 137769b3e104SGarrett D'Amore { 137869b3e104SGarrett D'Amore elxl_t *sc = arg; 137969b3e104SGarrett D'Amore switch (cap) { 138069b3e104SGarrett D'Amore case MAC_CAPAB_HCKSUM: { 138169b3e104SGarrett D'Amore uint32_t *flags = data; 138269b3e104SGarrett D'Amore if (sc->ex_conf & CONF_90XB) { 138369b3e104SGarrett D'Amore *flags = HCKSUM_IPHDRCKSUM | HCKSUM_INET_FULL_V4; 138469b3e104SGarrett D'Amore return (B_TRUE); 138569b3e104SGarrett D'Amore } 138669b3e104SGarrett D'Amore return (B_FALSE); 138769b3e104SGarrett D'Amore } 138869b3e104SGarrett D'Amore default: 138969b3e104SGarrett D'Amore return (B_FALSE); 139069b3e104SGarrett D'Amore } 139169b3e104SGarrett D'Amore } 139269b3e104SGarrett D'Amore 139369b3e104SGarrett D'Amore static int 1394*0dc2366fSVenugopal Iyer elxl_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz, 1395*0dc2366fSVenugopal Iyer void *val) 139669b3e104SGarrett D'Amore { 139769b3e104SGarrett D'Amore elxl_t *sc = arg; 139869b3e104SGarrett D'Amore int rv; 139969b3e104SGarrett D'Amore 140069b3e104SGarrett D'Amore if (sc->ex_mii_active) { 1401*0dc2366fSVenugopal Iyer rv = mii_m_getprop(sc->ex_miih, name, num, sz, val); 140269b3e104SGarrett D'Amore if (rv != ENOTSUP) 140369b3e104SGarrett D'Amore return (rv); 140469b3e104SGarrett D'Amore } 140569b3e104SGarrett D'Amore 140669b3e104SGarrett D'Amore switch (num) { 140769b3e104SGarrett D'Amore case MAC_PROP_DUPLEX: 1408*0dc2366fSVenugopal Iyer *(uint8_t *)val = sc->ex_duplex; 140969b3e104SGarrett D'Amore break; 141069b3e104SGarrett D'Amore case MAC_PROP_SPEED: 141169b3e104SGarrett D'Amore *(uint8_t *)val = sc->ex_speed; 141269b3e104SGarrett D'Amore break; 141369b3e104SGarrett D'Amore case MAC_PROP_STATUS: 141469b3e104SGarrett D'Amore bcopy(&sc->ex_link, val, sizeof (link_state_t)); 141569b3e104SGarrett D'Amore break; 141669b3e104SGarrett D'Amore 141769b3e104SGarrett D'Amore case MAC_PROP_PRIVATE: 141869b3e104SGarrett D'Amore if (strcmp(name, "_media") == 0) { 141969b3e104SGarrett D'Amore char *str; 142069b3e104SGarrett D'Amore 142169b3e104SGarrett D'Amore switch (sc->ex_xcvr) { 142269b3e104SGarrett D'Amore case XCVR_SEL_AUTO: 142369b3e104SGarrett D'Amore case XCVR_SEL_MII: 142469b3e104SGarrett D'Amore str = "mii"; 142569b3e104SGarrett D'Amore break; 142669b3e104SGarrett D'Amore case XCVR_SEL_10T: 142769b3e104SGarrett D'Amore str = sc->ex_fdx ? "tp-fdx" : "tp-hdx"; 142869b3e104SGarrett D'Amore break; 142969b3e104SGarrett D'Amore case XCVR_SEL_BNC: 143069b3e104SGarrett D'Amore str = "bnc"; 143169b3e104SGarrett D'Amore break; 143269b3e104SGarrett D'Amore case XCVR_SEL_AUI: 143369b3e104SGarrett D'Amore if (sc->ex_mediaopt & MEDIAOPT_10FL) { 143469b3e104SGarrett D'Amore str = sc->ex_fdx ? "fl-fdx" : "fl-hdx"; 143569b3e104SGarrett D'Amore } else { 143669b3e104SGarrett D'Amore str = "aui"; 143769b3e104SGarrett D'Amore } 143869b3e104SGarrett D'Amore break; 143969b3e104SGarrett D'Amore case XCVR_SEL_100FX: 144069b3e104SGarrett D'Amore str = sc->ex_fdx ? "fx-fdx" : "fx-hdx"; 144169b3e104SGarrett D'Amore break; 144269b3e104SGarrett D'Amore default: 144369b3e104SGarrett D'Amore str = "unknown"; 144469b3e104SGarrett D'Amore break; 144569b3e104SGarrett D'Amore } 144669b3e104SGarrett D'Amore (void) snprintf(val, sz, "%s", str); 144769b3e104SGarrett D'Amore return (0); 144869b3e104SGarrett D'Amore } 144969b3e104SGarrett D'Amore /* 145069b3e104SGarrett D'Amore * This available media property is a hack, and should 145169b3e104SGarrett D'Amore * be removed when we can provide proper support for 145269b3e104SGarrett D'Amore * querying it as proposed in PSARC 2009/235. (At the 145369b3e104SGarrett D'Amore * moment the implementation lacks support for using 145469b3e104SGarrett D'Amore * MAC_PROP_POSSIBLE with private properties.) 145569b3e104SGarrett D'Amore */ 145669b3e104SGarrett D'Amore if (strcmp(name, "_available_media") == 0) { 145769b3e104SGarrett D'Amore (void) snprintf(val, sz, "%s", sc->ex_medias); 145869b3e104SGarrett D'Amore return (0); 145969b3e104SGarrett D'Amore } 146069b3e104SGarrett D'Amore break; 146169b3e104SGarrett D'Amore } 146269b3e104SGarrett D'Amore return (ENOTSUP); 146369b3e104SGarrett D'Amore } 146469b3e104SGarrett D'Amore 146569b3e104SGarrett D'Amore static int 146669b3e104SGarrett D'Amore elxl_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz, 146769b3e104SGarrett D'Amore const void *val) 146869b3e104SGarrett D'Amore { 146969b3e104SGarrett D'Amore elxl_t *sc = arg; 147069b3e104SGarrett D'Amore int rv; 147169b3e104SGarrett D'Amore 147269b3e104SGarrett D'Amore if (sc->ex_mii_active) { 147369b3e104SGarrett D'Amore rv = mii_m_setprop(sc->ex_miih, name, num, sz, val); 147469b3e104SGarrett D'Amore if (rv != ENOTSUP) { 147569b3e104SGarrett D'Amore return (rv); 147669b3e104SGarrett D'Amore } 147769b3e104SGarrett D'Amore } 147869b3e104SGarrett D'Amore switch (num) { 147969b3e104SGarrett D'Amore 148069b3e104SGarrett D'Amore case MAC_PROP_PRIVATE: 148169b3e104SGarrett D'Amore if (strcmp(name, "_media") == 0) { 148269b3e104SGarrett D'Amore uint32_t mopt = sc->ex_mediaopt; 148369b3e104SGarrett D'Amore 148469b3e104SGarrett D'Amore if (strcmp(val, "mii") == 0) { 148569b3e104SGarrett D'Amore if (mopt & MEDIAOPT_100TX) { 148669b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_AUTO; 148769b3e104SGarrett D'Amore } else if (mopt & MEDIAOPT_MII) { 148869b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_MII; 148969b3e104SGarrett D'Amore } else { 149069b3e104SGarrett D'Amore return (EINVAL); 149169b3e104SGarrett D'Amore } 149269b3e104SGarrett D'Amore } else if (strcmp(val, "tp-fdx") == 0) { 149369b3e104SGarrett D'Amore /* select media option */ 149469b3e104SGarrett D'Amore if (mopt & MEDIAOPT_10T) { 149569b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_10T; 149669b3e104SGarrett D'Amore sc->ex_fdx = B_TRUE; 149769b3e104SGarrett D'Amore } else { 149869b3e104SGarrett D'Amore return (EINVAL); 149969b3e104SGarrett D'Amore } 150069b3e104SGarrett D'Amore } else if (strcmp(val, "tp-hdx") == 0) { 150169b3e104SGarrett D'Amore /* select media option */ 150269b3e104SGarrett D'Amore if (mopt & MEDIAOPT_10T) { 150369b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_10T; 150469b3e104SGarrett D'Amore sc->ex_fdx = B_FALSE; 150569b3e104SGarrett D'Amore } else { 150669b3e104SGarrett D'Amore return (EINVAL); 150769b3e104SGarrett D'Amore } 150869b3e104SGarrett D'Amore } else if (strcmp(val, "fx-fdx") == 0) { 150969b3e104SGarrett D'Amore if (mopt & MEDIAOPT_100FX) { 151069b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_100FX; 151169b3e104SGarrett D'Amore sc->ex_fdx = B_TRUE; 151269b3e104SGarrett D'Amore } else { 151369b3e104SGarrett D'Amore return (EINVAL); 151469b3e104SGarrett D'Amore } 151569b3e104SGarrett D'Amore } else if (strcmp(val, "fx-hdx") == 0) { 151669b3e104SGarrett D'Amore if (mopt & MEDIAOPT_100FX) { 151769b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_100FX; 151869b3e104SGarrett D'Amore sc->ex_fdx = B_FALSE; 151969b3e104SGarrett D'Amore } else { 152069b3e104SGarrett D'Amore return (EINVAL); 152169b3e104SGarrett D'Amore } 152269b3e104SGarrett D'Amore } else if (strcmp(val, "bnc") == 0) { 152369b3e104SGarrett D'Amore if (mopt & MEDIAOPT_BNC) { 152469b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_BNC; 152569b3e104SGarrett D'Amore sc->ex_fdx = B_FALSE; 152669b3e104SGarrett D'Amore } else { 152769b3e104SGarrett D'Amore return (EINVAL); 152869b3e104SGarrett D'Amore } 152969b3e104SGarrett D'Amore } else if (strcmp(val, "aui") == 0) { 153069b3e104SGarrett D'Amore if (mopt & MEDIAOPT_AUI) { 153169b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_AUI; 153269b3e104SGarrett D'Amore sc->ex_fdx = B_FALSE; 153369b3e104SGarrett D'Amore } else { 153469b3e104SGarrett D'Amore return (EINVAL); 153569b3e104SGarrett D'Amore } 153669b3e104SGarrett D'Amore } else if (strcmp(val, "fl-fdx") == 0) { 153769b3e104SGarrett D'Amore if (mopt & MEDIAOPT_10FL) { 153869b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_AUI; 153969b3e104SGarrett D'Amore sc->ex_fdx = B_TRUE; 154069b3e104SGarrett D'Amore } else { 154169b3e104SGarrett D'Amore return (EINVAL); 154269b3e104SGarrett D'Amore } 154369b3e104SGarrett D'Amore } else if (strcmp(val, "fl-hdx") == 0) { 154469b3e104SGarrett D'Amore if (mopt & MEDIAOPT_10FL) { 154569b3e104SGarrett D'Amore sc->ex_xcvr = XCVR_SEL_AUI; 154669b3e104SGarrett D'Amore sc->ex_fdx = B_FALSE; 154769b3e104SGarrett D'Amore } else { 154869b3e104SGarrett D'Amore return (EINVAL); 154969b3e104SGarrett D'Amore } 155069b3e104SGarrett D'Amore 155169b3e104SGarrett D'Amore } else { 155269b3e104SGarrett D'Amore return (EINVAL); 155369b3e104SGarrett D'Amore } 155469b3e104SGarrett D'Amore goto reset; 155569b3e104SGarrett D'Amore } 155669b3e104SGarrett D'Amore break; 155769b3e104SGarrett D'Amore default: 155869b3e104SGarrett D'Amore break; 155969b3e104SGarrett D'Amore } 156069b3e104SGarrett D'Amore 156169b3e104SGarrett D'Amore return (ENOTSUP); 156269b3e104SGarrett D'Amore 156369b3e104SGarrett D'Amore reset: 156469b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 156569b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 156669b3e104SGarrett D'Amore if (!sc->ex_suspended) { 156769b3e104SGarrett D'Amore elxl_reset(sc); 156869b3e104SGarrett D'Amore if (sc->ex_running) { 156969b3e104SGarrett D'Amore elxl_init(sc); 157069b3e104SGarrett D'Amore } 157169b3e104SGarrett D'Amore } 157269b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 157369b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 157469b3e104SGarrett D'Amore return (0); 157569b3e104SGarrett D'Amore } 157669b3e104SGarrett D'Amore 1577*0dc2366fSVenugopal Iyer static void 1578*0dc2366fSVenugopal Iyer elxl_m_propinfo(void *arg, const char *name, mac_prop_id_t num, 1579*0dc2366fSVenugopal Iyer mac_prop_info_handle_t prh) 1580*0dc2366fSVenugopal Iyer { 1581*0dc2366fSVenugopal Iyer elxl_t *sc = arg; 1582*0dc2366fSVenugopal Iyer 1583*0dc2366fSVenugopal Iyer if (sc->ex_mii_active) 1584*0dc2366fSVenugopal Iyer mii_m_propinfo(sc->ex_miih, name, num, prh); 1585*0dc2366fSVenugopal Iyer 1586*0dc2366fSVenugopal Iyer switch (num) { 1587*0dc2366fSVenugopal Iyer case MAC_PROP_DUPLEX: 1588*0dc2366fSVenugopal Iyer case MAC_PROP_SPEED: 1589*0dc2366fSVenugopal Iyer case MAC_PROP_STATUS: 1590*0dc2366fSVenugopal Iyer mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 1591*0dc2366fSVenugopal Iyer break; 1592*0dc2366fSVenugopal Iyer 1593*0dc2366fSVenugopal Iyer case MAC_PROP_PRIVATE: 1594*0dc2366fSVenugopal Iyer if (strcmp(name, "_available_media") == 0) 1595*0dc2366fSVenugopal Iyer mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 1596*0dc2366fSVenugopal Iyer break; 1597*0dc2366fSVenugopal Iyer } 1598*0dc2366fSVenugopal Iyer } 1599*0dc2366fSVenugopal Iyer 160069b3e104SGarrett D'Amore static int 160169b3e104SGarrett D'Amore elxl_m_stat(void *arg, uint_t stat, uint64_t *val) 160269b3e104SGarrett D'Amore { 160369b3e104SGarrett D'Amore elxl_t *sc = arg; 160469b3e104SGarrett D'Amore 160569b3e104SGarrett D'Amore if (stat == MAC_STAT_IFSPEED) { 160669b3e104SGarrett D'Amore elxl_getstats(sc); 160769b3e104SGarrett D'Amore } 160869b3e104SGarrett D'Amore 160969b3e104SGarrett D'Amore if ((sc->ex_mii_active) && 161069b3e104SGarrett D'Amore (mii_m_getstat(sc->ex_miih, stat, val) == 0)) { 161169b3e104SGarrett D'Amore return (0); 161269b3e104SGarrett D'Amore } 161369b3e104SGarrett D'Amore 161469b3e104SGarrett D'Amore switch (stat) { 161569b3e104SGarrett D'Amore case MAC_STAT_IFSPEED: 161669b3e104SGarrett D'Amore *val = sc->ex_speed; 161769b3e104SGarrett D'Amore break; 161869b3e104SGarrett D'Amore 161969b3e104SGarrett D'Amore case ETHER_STAT_LINK_DUPLEX: 162069b3e104SGarrett D'Amore *val = sc->ex_duplex; 162169b3e104SGarrett D'Amore break; 162269b3e104SGarrett D'Amore 162369b3e104SGarrett D'Amore case MAC_STAT_MULTIRCV: 162469b3e104SGarrett D'Amore *val = sc->ex_multircv; 162569b3e104SGarrett D'Amore break; 162669b3e104SGarrett D'Amore 162769b3e104SGarrett D'Amore case MAC_STAT_BRDCSTRCV: 162869b3e104SGarrett D'Amore *val = sc->ex_brdcstrcv; 162969b3e104SGarrett D'Amore break; 163069b3e104SGarrett D'Amore 163169b3e104SGarrett D'Amore case MAC_STAT_MULTIXMT: 163269b3e104SGarrett D'Amore *val = sc->ex_multixmt; 163369b3e104SGarrett D'Amore break; 163469b3e104SGarrett D'Amore 163569b3e104SGarrett D'Amore case MAC_STAT_BRDCSTXMT: 163669b3e104SGarrett D'Amore *val = sc->ex_brdcstxmt; 163769b3e104SGarrett D'Amore break; 163869b3e104SGarrett D'Amore 163969b3e104SGarrett D'Amore case MAC_STAT_IPACKETS: 164069b3e104SGarrett D'Amore *val = sc->ex_ipackets; 164169b3e104SGarrett D'Amore break; 164269b3e104SGarrett D'Amore 164369b3e104SGarrett D'Amore case MAC_STAT_OPACKETS: 164469b3e104SGarrett D'Amore *val = sc->ex_opackets; 164569b3e104SGarrett D'Amore break; 164669b3e104SGarrett D'Amore 164769b3e104SGarrett D'Amore case MAC_STAT_RBYTES: 164869b3e104SGarrett D'Amore *val = sc->ex_ibytes; 164969b3e104SGarrett D'Amore break; 165069b3e104SGarrett D'Amore case MAC_STAT_OBYTES: 165169b3e104SGarrett D'Amore *val = sc->ex_obytes; 165269b3e104SGarrett D'Amore break; 165369b3e104SGarrett D'Amore 165469b3e104SGarrett D'Amore case MAC_STAT_COLLISIONS: 165569b3e104SGarrett D'Amore case ETHER_STAT_FIRST_COLLISIONS: 165669b3e104SGarrett D'Amore *val = sc->ex_singlecol + sc->ex_multcol; 165769b3e104SGarrett D'Amore break; 165869b3e104SGarrett D'Amore 165969b3e104SGarrett D'Amore case ETHER_STAT_MULTI_COLLISIONS: 166069b3e104SGarrett D'Amore *val = sc->ex_multcol; 166169b3e104SGarrett D'Amore break; 166269b3e104SGarrett D'Amore 166369b3e104SGarrett D'Amore case ETHER_STAT_TX_LATE_COLLISIONS: 166469b3e104SGarrett D'Amore *val = sc->ex_latecol; 166569b3e104SGarrett D'Amore break; 166669b3e104SGarrett D'Amore 166769b3e104SGarrett D'Amore case ETHER_STAT_ALIGN_ERRORS: 166869b3e104SGarrett D'Amore *val = sc->ex_align; 166969b3e104SGarrett D'Amore break; 167069b3e104SGarrett D'Amore 167169b3e104SGarrett D'Amore case ETHER_STAT_FCS_ERRORS: 167269b3e104SGarrett D'Amore *val = sc->ex_fcs; 167369b3e104SGarrett D'Amore break; 167469b3e104SGarrett D'Amore 167569b3e104SGarrett D'Amore case ETHER_STAT_SQE_ERRORS: 167669b3e104SGarrett D'Amore *val = sc->ex_sqe; 167769b3e104SGarrett D'Amore break; 167869b3e104SGarrett D'Amore 167969b3e104SGarrett D'Amore case ETHER_STAT_DEFER_XMTS: 168069b3e104SGarrett D'Amore *val = sc->ex_defer; 168169b3e104SGarrett D'Amore break; 168269b3e104SGarrett D'Amore 168369b3e104SGarrett D'Amore case ETHER_STAT_CARRIER_ERRORS: 168469b3e104SGarrett D'Amore *val = sc->ex_nocarrier; 168569b3e104SGarrett D'Amore break; 168669b3e104SGarrett D'Amore 168769b3e104SGarrett D'Amore case ETHER_STAT_TOOLONG_ERRORS: 168869b3e104SGarrett D'Amore *val = sc->ex_toolong; 168969b3e104SGarrett D'Amore break; 169069b3e104SGarrett D'Amore 169169b3e104SGarrett D'Amore case ETHER_STAT_EX_COLLISIONS: 169269b3e104SGarrett D'Amore *val = sc->ex_excoll; 169369b3e104SGarrett D'Amore break; 169469b3e104SGarrett D'Amore 169569b3e104SGarrett D'Amore case MAC_STAT_OVERFLOWS: 169669b3e104SGarrett D'Amore *val = sc->ex_oflo; 169769b3e104SGarrett D'Amore break; 169869b3e104SGarrett D'Amore 169969b3e104SGarrett D'Amore case MAC_STAT_UNDERFLOWS: 170069b3e104SGarrett D'Amore *val = sc->ex_uflo; 170169b3e104SGarrett D'Amore break; 170269b3e104SGarrett D'Amore 170369b3e104SGarrett D'Amore case ETHER_STAT_TOOSHORT_ERRORS: 170469b3e104SGarrett D'Amore *val = sc->ex_runt; 170569b3e104SGarrett D'Amore break; 170669b3e104SGarrett D'Amore 170769b3e104SGarrett D'Amore case ETHER_STAT_JABBER_ERRORS: 170869b3e104SGarrett D'Amore *val = sc->ex_jabber; 170969b3e104SGarrett D'Amore break; 171069b3e104SGarrett D'Amore 171169b3e104SGarrett D'Amore case MAC_STAT_NORCVBUF: 171269b3e104SGarrett D'Amore *val = sc->ex_allocbfail; 171369b3e104SGarrett D'Amore break; 171469b3e104SGarrett D'Amore 171569b3e104SGarrett D'Amore case MAC_STAT_OERRORS: 171669b3e104SGarrett D'Amore *val = sc->ex_jabber + sc->ex_latecol + sc->ex_uflo; 171769b3e104SGarrett D'Amore break; 171869b3e104SGarrett D'Amore 171969b3e104SGarrett D'Amore case MAC_STAT_IERRORS: 172069b3e104SGarrett D'Amore *val = sc->ex_align + sc->ex_fcs + sc->ex_runt + 172169b3e104SGarrett D'Amore sc->ex_toolong + sc->ex_oflo + sc->ex_allocbfail; 172269b3e104SGarrett D'Amore break; 172369b3e104SGarrett D'Amore 172469b3e104SGarrett D'Amore default: 172569b3e104SGarrett D'Amore return (ENOTSUP); 172669b3e104SGarrett D'Amore } 172769b3e104SGarrett D'Amore return (0); 172869b3e104SGarrett D'Amore } 172969b3e104SGarrett D'Amore 173069b3e104SGarrett D'Amore static uint_t 173169b3e104SGarrett D'Amore elxl_intr(caddr_t arg, caddr_t dontcare) 173269b3e104SGarrett D'Amore { 173369b3e104SGarrett D'Amore elxl_t *sc = (void *)arg; 173469b3e104SGarrett D'Amore uint16_t stat; 173569b3e104SGarrett D'Amore mblk_t *mphead = NULL; 173669b3e104SGarrett D'Amore mblk_t **mpp = &mphead; 173769b3e104SGarrett D'Amore 173869b3e104SGarrett D'Amore _NOTE(ARGUNUSED(dontcare)); 173969b3e104SGarrett D'Amore 174069b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 174169b3e104SGarrett D'Amore if (sc->ex_suspended) { 174269b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 174369b3e104SGarrett D'Amore return (DDI_INTR_UNCLAIMED); 174469b3e104SGarrett D'Amore } 174569b3e104SGarrett D'Amore 174669b3e104SGarrett D'Amore stat = GET16(REG_CMD_STAT); 174769b3e104SGarrett D'Amore 174869b3e104SGarrett D'Amore if ((stat & INT_LATCH) == 0) { 174969b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 175069b3e104SGarrett D'Amore return (DDI_INTR_UNCLAIMED); 175169b3e104SGarrett D'Amore } 175269b3e104SGarrett D'Amore 175369b3e104SGarrett D'Amore /* 175469b3e104SGarrett D'Amore * Acknowledge interrupts. 175569b3e104SGarrett D'Amore */ 175669b3e104SGarrett D'Amore PUT_CMD(CMD_INT_ACK | (stat & INT_WATCHED) | INT_LATCH); 175769b3e104SGarrett D'Amore 175869b3e104SGarrett D'Amore if (stat & INT_HOST_ERROR) { 175969b3e104SGarrett D'Amore /* XXX: Potentially a good spot for FMA */ 176069b3e104SGarrett D'Amore elxl_error(sc, "Adapter failure (%x)", stat); 176169b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 176269b3e104SGarrett D'Amore elxl_reset(sc); 176369b3e104SGarrett D'Amore if (sc->ex_running) 176469b3e104SGarrett D'Amore elxl_init(sc); 176569b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 176669b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 176769b3e104SGarrett D'Amore return (DDI_INTR_CLAIMED); 176869b3e104SGarrett D'Amore } 176969b3e104SGarrett D'Amore if (stat & INT_UP_COMPLETE) { 177069b3e104SGarrett D'Amore ex_ring_t *r; 177169b3e104SGarrett D'Amore ex_desc_t *rxd; 177269b3e104SGarrett D'Amore ex_pd_t *pd; 177369b3e104SGarrett D'Amore mblk_t *mp; 177469b3e104SGarrett D'Amore uint32_t pktstat; 177569b3e104SGarrett D'Amore 177669b3e104SGarrett D'Amore r = &sc->ex_rxring; 177769b3e104SGarrett D'Amore 177869b3e104SGarrett D'Amore for (;;) { 177969b3e104SGarrett D'Amore rxd = r->r_head; 178069b3e104SGarrett D'Amore pd = rxd->ed_pd; 178169b3e104SGarrett D'Amore 17825109e3feSGarrett D'Amore (void) ddi_dma_sync(r->r_dmah, rxd->ed_off, 178369b3e104SGarrett D'Amore sizeof (ex_pd_t), DDI_DMA_SYNC_FORKERNEL); 178469b3e104SGarrett D'Amore 178569b3e104SGarrett D'Amore pktstat = GET_PD(r, pd->pd_status); 178669b3e104SGarrett D'Amore 178769b3e104SGarrett D'Amore if ((pktstat & EX_UPD_COMPLETE) == 0) { 178869b3e104SGarrett D'Amore break; 178969b3e104SGarrett D'Amore } 179069b3e104SGarrett D'Amore 179169b3e104SGarrett D'Amore /* Advance head to next packet. */ 179269b3e104SGarrett D'Amore r->r_head = r->r_head->ed_next; 179369b3e104SGarrett D'Amore 179469b3e104SGarrett D'Amore if ((mp = elxl_recv(sc, rxd, pktstat)) != NULL) { 179569b3e104SGarrett D'Amore *mpp = mp; 179669b3e104SGarrett D'Amore mpp = &mp->b_next; 179769b3e104SGarrett D'Amore } 179869b3e104SGarrett D'Amore 179969b3e104SGarrett D'Amore /* clear the upComplete status, reset other fields */ 180069b3e104SGarrett D'Amore PUT_PD(r, pd->pd_status, 0); 180169b3e104SGarrett D'Amore PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST); 180269b3e104SGarrett D'Amore PUT_PD(r, pd->pd_addr, rxd->ed_bufaddr); 18035109e3feSGarrett D'Amore (void) ddi_dma_sync(r->r_dmah, rxd->ed_off, 180469b3e104SGarrett D'Amore sizeof (ex_pd_t), DDI_DMA_SYNC_FORDEV); 180569b3e104SGarrett D'Amore } 180669b3e104SGarrett D'Amore 180769b3e104SGarrett D'Amore /* 180869b3e104SGarrett D'Amore * If the engine stalled processing (due to 180969b3e104SGarrett D'Amore * insufficient UPDs usually), restart it. 181069b3e104SGarrett D'Amore */ 181169b3e104SGarrett D'Amore if (GET32(REG_UPLISTPTR) == 0) { 181269b3e104SGarrett D'Amore /* 181369b3e104SGarrett D'Amore * This seems that it can happen in an RX overrun 181469b3e104SGarrett D'Amore * situation. 181569b3e104SGarrett D'Amore */ 181669b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 181769b3e104SGarrett D'Amore if (sc->ex_running) 181869b3e104SGarrett D'Amore elxl_init(sc); 181969b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 182069b3e104SGarrett D'Amore } 182169b3e104SGarrett D'Amore PUT_CMD(CMD_UP_UNSTALL); 182269b3e104SGarrett D'Amore } 182369b3e104SGarrett D'Amore 182469b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 182569b3e104SGarrett D'Amore 182669b3e104SGarrett D'Amore if (mphead) { 182769b3e104SGarrett D'Amore mac_rx(sc->ex_mach, NULL, mphead); 182869b3e104SGarrett D'Amore } 182969b3e104SGarrett D'Amore if (stat & INT_STATS) { 183069b3e104SGarrett D'Amore elxl_getstats(sc); 183169b3e104SGarrett D'Amore } 183269b3e104SGarrett D'Amore if (stat & INT_DN_COMPLETE) { 183369b3e104SGarrett D'Amore mac_tx_update(sc->ex_mach); 183469b3e104SGarrett D'Amore } 183569b3e104SGarrett D'Amore 183669b3e104SGarrett D'Amore return (DDI_INTR_CLAIMED); 183769b3e104SGarrett D'Amore } 183869b3e104SGarrett D'Amore 183969b3e104SGarrett D'Amore static void 184069b3e104SGarrett D'Amore elxl_getstats(elxl_t *sc) 184169b3e104SGarrett D'Amore { 184269b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 184369b3e104SGarrett D'Amore if (sc->ex_suspended) { 184469b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 184569b3e104SGarrett D'Amore return; 184669b3e104SGarrett D'Amore } 184769b3e104SGarrett D'Amore 184869b3e104SGarrett D'Amore SET_WIN(6); 184969b3e104SGarrett D'Amore /* 185069b3e104SGarrett D'Amore * We count the packets and bytes elsewhere, but we need to 185169b3e104SGarrett D'Amore * read the registers to clear them. 185269b3e104SGarrett D'Amore */ 185369b3e104SGarrett D'Amore (void) GET8(W6_RX_FRAMES); 185469b3e104SGarrett D'Amore (void) GET8(W6_TX_FRAMES); 185569b3e104SGarrett D'Amore (void) GET8(W6_UPPER_FRAMES); 185669b3e104SGarrett D'Amore (void) GET8(W6_RX_OVERRUNS); /* counted by elxl_recv */ 185769b3e104SGarrett D'Amore (void) GET16(W6_RX_BYTES); 185869b3e104SGarrett D'Amore (void) GET16(W6_TX_BYTES); 185969b3e104SGarrett D'Amore 186069b3e104SGarrett D'Amore sc->ex_defer += GET8(W6_DEFER); 186169b3e104SGarrett D'Amore sc->ex_latecol += GET8(W6_TX_LATE_COL); 186269b3e104SGarrett D'Amore sc->ex_singlecol += GET8(W6_SINGLE_COL); 186369b3e104SGarrett D'Amore sc->ex_multcol += GET8(W6_MULT_COL); 186469b3e104SGarrett D'Amore sc->ex_sqe += GET8(W6_SQE_ERRORS); 186569b3e104SGarrett D'Amore sc->ex_nocarrier += GET8(W6_NO_CARRIER); 186669b3e104SGarrett D'Amore 186769b3e104SGarrett D'Amore SET_WIN(4); 186869b3e104SGarrett D'Amore /* Note: we ought to report this somewhere... */ 186969b3e104SGarrett D'Amore (void) GET8(W4_BADSSD); 187069b3e104SGarrett D'Amore 187169b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 187269b3e104SGarrett D'Amore } 187369b3e104SGarrett D'Amore 187469b3e104SGarrett D'Amore static void 187569b3e104SGarrett D'Amore elxl_reset(elxl_t *sc) 187669b3e104SGarrett D'Amore { 187769b3e104SGarrett D'Amore PUT_CMD(CMD_GLOBAL_RESET); 187869b3e104SGarrett D'Amore /* 187969b3e104SGarrett D'Amore * Some ASICs need a longer time (20 ms) to come properly out 188069b3e104SGarrett D'Amore * of reset. Do not reduce this value. 188169b3e104SGarrett D'Amore * 188269b3e104SGarrett D'Amore * Note that this occurs only during attach and failure recovery, 188369b3e104SGarrett D'Amore * so it should be mostly harmless. 188469b3e104SGarrett D'Amore */ 188569b3e104SGarrett D'Amore drv_usecwait(20000); 188669b3e104SGarrett D'Amore WAIT_CMD(sc); 188769b3e104SGarrett D'Amore } 188869b3e104SGarrett D'Amore 188969b3e104SGarrett D'Amore static void 189069b3e104SGarrett D'Amore elxl_stop(elxl_t *sc) 189169b3e104SGarrett D'Amore { 189269b3e104SGarrett D'Amore ASSERT(mutex_owned(&sc->ex_intrlock)); 189369b3e104SGarrett D'Amore ASSERT(mutex_owned(&sc->ex_txlock)); 189469b3e104SGarrett D'Amore 189569b3e104SGarrett D'Amore if (sc->ex_suspended) 189669b3e104SGarrett D'Amore return; 189769b3e104SGarrett D'Amore 189869b3e104SGarrett D'Amore PUT_CMD(CMD_RX_DISABLE); 189969b3e104SGarrett D'Amore PUT_CMD(CMD_TX_DISABLE); 190069b3e104SGarrett D'Amore PUT_CMD(CMD_BNC_DISABLE); 190169b3e104SGarrett D'Amore 190269b3e104SGarrett D'Amore elxl_reset_ring(&sc->ex_rxring, DDI_DMA_READ); 190369b3e104SGarrett D'Amore elxl_reset_ring(&sc->ex_txring, DDI_DMA_WRITE); 190469b3e104SGarrett D'Amore 190569b3e104SGarrett D'Amore PUT_CMD(CMD_INT_ACK | INT_LATCH); 190669b3e104SGarrett D'Amore /* Disable all interrupts. (0 means "none".) */ 190769b3e104SGarrett D'Amore PUT_CMD(CMD_INT_ENABLE | 0); 190869b3e104SGarrett D'Amore } 190969b3e104SGarrett D'Amore 191069b3e104SGarrett D'Amore static void 191169b3e104SGarrett D'Amore elxl_suspend(elxl_t *sc) 191269b3e104SGarrett D'Amore { 191369b3e104SGarrett D'Amore if (sc->ex_miih) { 191469b3e104SGarrett D'Amore mii_suspend(sc->ex_miih); 191569b3e104SGarrett D'Amore } 191669b3e104SGarrett D'Amore 191769b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 191869b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 191969b3e104SGarrett D'Amore elxl_stop(sc); 192069b3e104SGarrett D'Amore sc->ex_suspended = B_TRUE; 192169b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 192269b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 192369b3e104SGarrett D'Amore } 192469b3e104SGarrett D'Amore 192569b3e104SGarrett D'Amore static void 192669b3e104SGarrett D'Amore elxl_resume(dev_info_t *dip) 192769b3e104SGarrett D'Amore { 192869b3e104SGarrett D'Amore elxl_t *sc; 192969b3e104SGarrett D'Amore 193069b3e104SGarrett D'Amore /* This should always succeed. */ 193169b3e104SGarrett D'Amore sc = ddi_get_driver_private(dip); 193269b3e104SGarrett D'Amore ASSERT(sc); 193369b3e104SGarrett D'Amore 193469b3e104SGarrett D'Amore mutex_enter(&sc->ex_intrlock); 193569b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 193669b3e104SGarrett D'Amore sc->ex_suspended = B_FALSE; 193769b3e104SGarrett D'Amore elxl_reset(sc); 193869b3e104SGarrett D'Amore if (sc->ex_running) 193969b3e104SGarrett D'Amore elxl_init(sc); 194069b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 194169b3e104SGarrett D'Amore mutex_exit(&sc->ex_intrlock); 194269b3e104SGarrett D'Amore 194369b3e104SGarrett D'Amore if (sc->ex_miih) { 194469b3e104SGarrett D'Amore mii_resume(sc->ex_miih); 194569b3e104SGarrett D'Amore } 194669b3e104SGarrett D'Amore } 194769b3e104SGarrett D'Amore 194869b3e104SGarrett D'Amore static void 194969b3e104SGarrett D'Amore elxl_detach(elxl_t *sc) 195069b3e104SGarrett D'Amore { 195169b3e104SGarrett D'Amore if (sc->ex_miih) { 195269b3e104SGarrett D'Amore /* Detach all PHYs */ 195369b3e104SGarrett D'Amore mii_free(sc->ex_miih); 195469b3e104SGarrett D'Amore } 195569b3e104SGarrett D'Amore if (sc->ex_linkcheck) { 195669b3e104SGarrett D'Amore ddi_periodic_delete(sc->ex_linkcheck); 195769b3e104SGarrett D'Amore } 195869b3e104SGarrett D'Amore 195969b3e104SGarrett D'Amore if (sc->ex_intrh != NULL) { 196069b3e104SGarrett D'Amore (void) ddi_intr_disable(sc->ex_intrh); 196169b3e104SGarrett D'Amore (void) ddi_intr_remove_handler(sc->ex_intrh); 196269b3e104SGarrett D'Amore (void) ddi_intr_free(sc->ex_intrh); 196369b3e104SGarrett D'Amore mutex_destroy(&sc->ex_intrlock); 196469b3e104SGarrett D'Amore mutex_destroy(&sc->ex_txlock); 196569b3e104SGarrett D'Amore } 196669b3e104SGarrett D'Amore 196769b3e104SGarrett D'Amore if (sc->ex_pcih) { 196869b3e104SGarrett D'Amore pci_config_teardown(&sc->ex_pcih); 196969b3e104SGarrett D'Amore } 197069b3e104SGarrett D'Amore if (sc->ex_regsh) { 197169b3e104SGarrett D'Amore ddi_regs_map_free(&sc->ex_regsh); 197269b3e104SGarrett D'Amore } 197369b3e104SGarrett D'Amore ex_free_ring(&sc->ex_txring); 197469b3e104SGarrett D'Amore ex_free_ring(&sc->ex_rxring); 197569b3e104SGarrett D'Amore 197669b3e104SGarrett D'Amore kmem_free(sc, sizeof (*sc)); 197769b3e104SGarrett D'Amore } 197869b3e104SGarrett D'Amore 197969b3e104SGarrett D'Amore /* 198069b3e104SGarrett D'Amore * Read EEPROM data. If we can't unbusy the EEPROM, then zero will be 198169b3e104SGarrett D'Amore * returned. This will probably result in a bogus node address. 198269b3e104SGarrett D'Amore */ 198369b3e104SGarrett D'Amore static uint16_t 198469b3e104SGarrett D'Amore elxl_read_eeprom(elxl_t *sc, int offset) 198569b3e104SGarrett D'Amore { 198669b3e104SGarrett D'Amore uint16_t data = 0; 198769b3e104SGarrett D'Amore 198869b3e104SGarrett D'Amore SET_WIN(0); 198969b3e104SGarrett D'Amore if (elxl_eeprom_busy(sc)) 199069b3e104SGarrett D'Amore goto out; 199169b3e104SGarrett D'Amore 199269b3e104SGarrett D'Amore PUT16(W0_EE_CMD, EE_CMD_READ | (offset & 0x3f)); 199369b3e104SGarrett D'Amore if (elxl_eeprom_busy(sc)) 199469b3e104SGarrett D'Amore goto out; 199569b3e104SGarrett D'Amore data = GET16(W0_EE_DATA); 199669b3e104SGarrett D'Amore out: 199769b3e104SGarrett D'Amore return (data); 199869b3e104SGarrett D'Amore } 199969b3e104SGarrett D'Amore 200069b3e104SGarrett D'Amore static int 200169b3e104SGarrett D'Amore elxl_eeprom_busy(elxl_t *sc) 200269b3e104SGarrett D'Amore { 200369b3e104SGarrett D'Amore int i = 2000; 200469b3e104SGarrett D'Amore 200569b3e104SGarrett D'Amore while (i--) { 200669b3e104SGarrett D'Amore if (!(GET16(W0_EE_CMD) & EE_CMD_BUSY)) 200769b3e104SGarrett D'Amore return (0); 200869b3e104SGarrett D'Amore drv_usecwait(100); 200969b3e104SGarrett D'Amore } 201069b3e104SGarrett D'Amore elxl_error(sc, "Eeprom stays busy."); 201169b3e104SGarrett D'Amore return (1); 201269b3e104SGarrett D'Amore } 201369b3e104SGarrett D'Amore 201469b3e104SGarrett D'Amore static void 201569b3e104SGarrett D'Amore ex_mii_send_bits(struct ex_softc *sc, uint16_t bits, int cnt) 201669b3e104SGarrett D'Amore { 201769b3e104SGarrett D'Amore uint16_t val; 201869b3e104SGarrett D'Amore ASSERT(cnt > 0); 201969b3e104SGarrett D'Amore 202069b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DIR); 202169b3e104SGarrett D'Amore drv_usecwait(1); 202269b3e104SGarrett D'Amore 202369b3e104SGarrett D'Amore for (int i = (1 << (cnt - 1)); i; i >>= 1) { 202469b3e104SGarrett D'Amore if (bits & i) { 202569b3e104SGarrett D'Amore val = PHYSMGMT_DIR | PHYSMGMT_DATA; 202669b3e104SGarrett D'Amore } else { 202769b3e104SGarrett D'Amore val = PHYSMGMT_DIR; 202869b3e104SGarrett D'Amore } 202969b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, val); 203069b3e104SGarrett D'Amore drv_usecwait(1); 203169b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, val | PHYSMGMT_CLK); 203269b3e104SGarrett D'Amore drv_usecwait(1); 203369b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, val); 203469b3e104SGarrett D'Amore drv_usecwait(1); 203569b3e104SGarrett D'Amore } 203669b3e104SGarrett D'Amore } 203769b3e104SGarrett D'Amore 203869b3e104SGarrett D'Amore static void 203969b3e104SGarrett D'Amore ex_mii_sync(struct ex_softc *sc) 204069b3e104SGarrett D'Amore { 204169b3e104SGarrett D'Amore /* 204269b3e104SGarrett D'Amore * We set the data bit output, and strobe the clock 32 times. 204369b3e104SGarrett D'Amore */ 204469b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR); 204569b3e104SGarrett D'Amore drv_usecwait(1); 204669b3e104SGarrett D'Amore 204769b3e104SGarrett D'Amore for (int i = 0; i < 32; i++) { 204869b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR | PHYSMGMT_CLK); 204969b3e104SGarrett D'Amore drv_usecwait(1); 205069b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR); 205169b3e104SGarrett D'Amore drv_usecwait(1); 205269b3e104SGarrett D'Amore } 205369b3e104SGarrett D'Amore } 205469b3e104SGarrett D'Amore 205569b3e104SGarrett D'Amore static uint16_t 205669b3e104SGarrett D'Amore elxl_mii_read(void *arg, uint8_t phy, uint8_t reg) 205769b3e104SGarrett D'Amore { 205869b3e104SGarrett D'Amore elxl_t *sc = arg; 205969b3e104SGarrett D'Amore uint16_t data; 206069b3e104SGarrett D'Amore int val; 206169b3e104SGarrett D'Amore 206269b3e104SGarrett D'Amore if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID) 206369b3e104SGarrett D'Amore return (0xffff); 206469b3e104SGarrett D'Amore 206569b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 206669b3e104SGarrett D'Amore SET_WIN(4); 206769b3e104SGarrett D'Amore 206869b3e104SGarrett D'Amore ex_mii_sync(sc); 206969b3e104SGarrett D'Amore 207069b3e104SGarrett D'Amore ex_mii_send_bits(sc, 1, 2); /* start */ 207169b3e104SGarrett D'Amore ex_mii_send_bits(sc, 2, 2); /* read command */ 207269b3e104SGarrett D'Amore ex_mii_send_bits(sc, phy, 5); 207369b3e104SGarrett D'Amore ex_mii_send_bits(sc, reg, 5); 207469b3e104SGarrett D'Amore 207569b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, 0); /* switch to input */ 207669b3e104SGarrett D'Amore drv_usecwait(1); 207769b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_CLK); /* turnaround time */ 207869b3e104SGarrett D'Amore drv_usecwait(1); 207969b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, 0); 208069b3e104SGarrett D'Amore drv_usecwait(1); 208169b3e104SGarrett D'Amore 208269b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_CLK); /* idle time */ 208369b3e104SGarrett D'Amore drv_usecwait(1); 208469b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, 0); 208569b3e104SGarrett D'Amore drv_usecwait(1); 208669b3e104SGarrett D'Amore 208769b3e104SGarrett D'Amore for (data = 0, val = 0x8000; val; val >>= 1) { 208869b3e104SGarrett D'Amore if (GET16(W4_PHYSMGMT) & PHYSMGMT_DATA) { 208969b3e104SGarrett D'Amore data |= val; 209069b3e104SGarrett D'Amore } 209169b3e104SGarrett D'Amore /* strobe the clock */ 209269b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_CLK); 209369b3e104SGarrett D'Amore drv_usecwait(1); 209469b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, 0); 209569b3e104SGarrett D'Amore drv_usecwait(1); 209669b3e104SGarrett D'Amore } 209769b3e104SGarrett D'Amore 209869b3e104SGarrett D'Amore /* return to output mode */ 209969b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DIR); 210069b3e104SGarrett D'Amore drv_usecwait(1); 210169b3e104SGarrett D'Amore 210269b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 210369b3e104SGarrett D'Amore 210469b3e104SGarrett D'Amore return (data); 210569b3e104SGarrett D'Amore } 210669b3e104SGarrett D'Amore 210769b3e104SGarrett D'Amore static void 210869b3e104SGarrett D'Amore elxl_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data) 210969b3e104SGarrett D'Amore { 211069b3e104SGarrett D'Amore elxl_t *sc = arg; 211169b3e104SGarrett D'Amore 211269b3e104SGarrett D'Amore if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID) 211369b3e104SGarrett D'Amore return; 211469b3e104SGarrett D'Amore 211569b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 211669b3e104SGarrett D'Amore SET_WIN(4); 211769b3e104SGarrett D'Amore 211869b3e104SGarrett D'Amore ex_mii_sync(sc); 211969b3e104SGarrett D'Amore ex_mii_send_bits(sc, 1, 2); /* start */ 212069b3e104SGarrett D'Amore ex_mii_send_bits(sc, 1, 2); /* write */ 212169b3e104SGarrett D'Amore ex_mii_send_bits(sc, phy, 5); 212269b3e104SGarrett D'Amore ex_mii_send_bits(sc, reg, 5); 212369b3e104SGarrett D'Amore ex_mii_send_bits(sc, 2, 2); /* ack/turnaround */ 212469b3e104SGarrett D'Amore ex_mii_send_bits(sc, data, 16); 212569b3e104SGarrett D'Amore 212669b3e104SGarrett D'Amore /* return to output mode */ 212769b3e104SGarrett D'Amore PUT16(W4_PHYSMGMT, PHYSMGMT_DIR); 212869b3e104SGarrett D'Amore drv_usecwait(1); 212969b3e104SGarrett D'Amore 213069b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 213169b3e104SGarrett D'Amore } 213269b3e104SGarrett D'Amore 213369b3e104SGarrett D'Amore static void 213469b3e104SGarrett D'Amore elxl_mii_notify(void *arg, link_state_t link) 213569b3e104SGarrett D'Amore { 213669b3e104SGarrett D'Amore elxl_t *sc = arg; 213769b3e104SGarrett D'Amore int mctl; 213869b3e104SGarrett D'Amore link_duplex_t duplex; 213969b3e104SGarrett D'Amore 214069b3e104SGarrett D'Amore duplex = mii_get_duplex(sc->ex_miih); 214169b3e104SGarrett D'Amore 214269b3e104SGarrett D'Amore mutex_enter(&sc->ex_txlock); 214369b3e104SGarrett D'Amore if (!sc->ex_mii_active) { 214469b3e104SGarrett D'Amore /* If we're using some other legacy media, bail out now */ 214569b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 214669b3e104SGarrett D'Amore return; 214769b3e104SGarrett D'Amore } 214869b3e104SGarrett D'Amore if (!sc->ex_suspended) { 214969b3e104SGarrett D'Amore SET_WIN(3); 215069b3e104SGarrett D'Amore mctl = GET16(W3_MAC_CONTROL); 215169b3e104SGarrett D'Amore if (duplex == LINK_DUPLEX_FULL) 215269b3e104SGarrett D'Amore mctl |= MAC_CONTROL_FDX; 215369b3e104SGarrett D'Amore else 215469b3e104SGarrett D'Amore mctl &= ~MAC_CONTROL_FDX; 215569b3e104SGarrett D'Amore PUT16(W3_MAC_CONTROL, mctl); 215669b3e104SGarrett D'Amore } 215769b3e104SGarrett D'Amore mutex_exit(&sc->ex_txlock); 215869b3e104SGarrett D'Amore 215969b3e104SGarrett D'Amore mac_link_update(sc->ex_mach, link); 216069b3e104SGarrett D'Amore } 216169b3e104SGarrett D'Amore 216269b3e104SGarrett D'Amore static int 216369b3e104SGarrett D'Amore elxl_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 216469b3e104SGarrett D'Amore { 216569b3e104SGarrett D'Amore switch (cmd) { 216669b3e104SGarrett D'Amore case DDI_ATTACH: 216769b3e104SGarrett D'Amore return (elxl_attach(dip)); 216869b3e104SGarrett D'Amore 216969b3e104SGarrett D'Amore case DDI_RESUME: 217069b3e104SGarrett D'Amore elxl_resume(dip); 217169b3e104SGarrett D'Amore return (DDI_SUCCESS); 217269b3e104SGarrett D'Amore 217369b3e104SGarrett D'Amore default: 217469b3e104SGarrett D'Amore return (DDI_FAILURE); 217569b3e104SGarrett D'Amore } 217669b3e104SGarrett D'Amore } 217769b3e104SGarrett D'Amore 217869b3e104SGarrett D'Amore static int 217969b3e104SGarrett D'Amore elxl_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 218069b3e104SGarrett D'Amore { 218169b3e104SGarrett D'Amore elxl_t *sc; 218269b3e104SGarrett D'Amore 218369b3e104SGarrett D'Amore sc = ddi_get_driver_private(dip); 218469b3e104SGarrett D'Amore ASSERT(sc); 218569b3e104SGarrett D'Amore 218669b3e104SGarrett D'Amore switch (cmd) { 218769b3e104SGarrett D'Amore case DDI_DETACH: 218869b3e104SGarrett D'Amore if (mac_disable(sc->ex_mach) != 0) { 218969b3e104SGarrett D'Amore return (DDI_FAILURE); 219069b3e104SGarrett D'Amore } 219169b3e104SGarrett D'Amore (void) mac_unregister(sc->ex_mach); 219287974390SGarrett D'Amore elxl_detach(sc); 219369b3e104SGarrett D'Amore return (DDI_SUCCESS); 219469b3e104SGarrett D'Amore 219569b3e104SGarrett D'Amore case DDI_SUSPEND: 219669b3e104SGarrett D'Amore elxl_suspend(sc); 219769b3e104SGarrett D'Amore return (DDI_SUCCESS); 219869b3e104SGarrett D'Amore 219969b3e104SGarrett D'Amore default: 220069b3e104SGarrett D'Amore return (DDI_FAILURE); 220169b3e104SGarrett D'Amore } 220269b3e104SGarrett D'Amore } 220369b3e104SGarrett D'Amore 220469b3e104SGarrett D'Amore static int 220569b3e104SGarrett D'Amore elxl_ddi_quiesce(dev_info_t *dip) 220669b3e104SGarrett D'Amore { 220769b3e104SGarrett D'Amore elxl_t *sc; 220869b3e104SGarrett D'Amore 220969b3e104SGarrett D'Amore sc = ddi_get_driver_private(dip); 221069b3e104SGarrett D'Amore ASSERT(sc); 221169b3e104SGarrett D'Amore 221269b3e104SGarrett D'Amore if (!sc->ex_suspended) 221369b3e104SGarrett D'Amore elxl_reset(sc); 221469b3e104SGarrett D'Amore return (DDI_SUCCESS); 221569b3e104SGarrett D'Amore } 221669b3e104SGarrett D'Amore 221769b3e104SGarrett D'Amore static void 221869b3e104SGarrett D'Amore elxl_error(elxl_t *sc, char *fmt, ...) 221969b3e104SGarrett D'Amore { 222069b3e104SGarrett D'Amore va_list ap; 222169b3e104SGarrett D'Amore char buf[256]; 222269b3e104SGarrett D'Amore 222369b3e104SGarrett D'Amore va_start(ap, fmt); 222469b3e104SGarrett D'Amore (void) vsnprintf(buf, sizeof (buf), fmt, ap); 222569b3e104SGarrett D'Amore va_end(ap); 222669b3e104SGarrett D'Amore 222769b3e104SGarrett D'Amore cmn_err(CE_WARN, "%s%d: %s", 222869b3e104SGarrett D'Amore ddi_driver_name(sc->ex_dip), ddi_get_instance(sc->ex_dip), buf); 222969b3e104SGarrett D'Amore } 2230