Copyright (c) 2007 by Garrett D'Amore . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of any co-contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MACRONIX FAST ETHERNET DRIVER /* * Solaris driver for ethernet cards based on the Macronix 98715 * * Copyright (c) 2007 by Garrett D'Amore . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mxfe.h" #include "mxfeimpl.h" /* * Driver globals. */ /* patchable debug flag ... must not be static! */ #ifdef DEBUG unsigned mxfe_debug = DWARN; #endif /* table of supported devices */ static mxfe_card_t mxfe_cards[] = { /* * Lite-On products */ { 0x11ad, 0xc115, 0, 0, "Lite-On LC82C115", MXFE_PNICII }, /* * Macronix chips */ { 0x10d9, 0x0531, 0x25, 0xff, "Macronix MX98715AEC", MXFE_98715AEC }, { 0x10d9, 0x0531, 0x20, 0xff, "Macronix MX98715A", MXFE_98715A }, { 0x10d9, 0x0531, 0x60, 0xff, "Macronix MX98715B", MXFE_98715B }, { 0x10d9, 0x0531, 0x30, 0xff, "Macronix MX98725", MXFE_98725 }, { 0x10d9, 0x0531, 0x00, 0xff, "Macronix MX98715", MXFE_98715 }, { 0x10d9, 0x0512, 0, 0, "Macronix MX98713", MXFE_98713 }, /* * Compex (relabeled Macronix products) */ { 0x11fc, 0x9881, 0x00, 0x00, "Compex 9881", MXFE_98713 }, { 0x11fc, 0x9881, 0x10, 0xff, "Compex 9881A", MXFE_98713A }, /* * Models listed here */ { 0x11ad, 0xc001, 0, 0, "Linksys LNE100TX", MXFE_PNICII }, { 0x2646, 0x000b, 0, 0, "Kingston KNE111TX", MXFE_PNICII }, { 0x1154, 0x0308, 0, 0, "Buffalo LGY-PCI-TXL", MXFE_98715AEC }, }; #define ETHERVLANMTU (ETHERMAX + 4) /* * Function prototypes */ static int mxfe_attach(dev_info_t *, ddi_attach_cmd_t); static int mxfe_detach(dev_info_t *, ddi_detach_cmd_t); static int mxfe_resume(dev_info_t *); static int mxfe_quiesce(dev_info_t *); static int mxfe_m_unicst(void *, const uint8_t *); static int mxfe_m_multicst(void *, boolean_t, const uint8_t *); static int mxfe_m_promisc(void *, boolean_t); static mblk_t *mxfe_m_tx(void *, mblk_t *); static int mxfe_m_stat(void *, uint_t, uint64_t *); static int mxfe_m_start(void *); static void mxfe_m_stop(void *); static int mxfe_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *); static int mxfe_m_setprop(void *, const char *, mac_prop_id_t, uint_t, const void *); static void mxfe_m_propinfo(void *, const char *, mac_prop_id_t, mac_prop_info_handle_t); static unsigned mxfe_intr(caddr_t); static void mxfe_startmac(mxfe_t *); static void mxfe_stopmac(mxfe_t *); static void mxfe_resetrings(mxfe_t *); static boolean_t mxfe_initialize(mxfe_t *); static void mxfe_startall(mxfe_t *); static void mxfe_stopall(mxfe_t *); static void mxfe_resetall(mxfe_t *); static mxfe_txbuf_t *mxfe_alloctxbuf(mxfe_t *); static void mxfe_destroytxbuf(mxfe_txbuf_t *); static mxfe_rxbuf_t *mxfe_allocrxbuf(mxfe_t *); static void mxfe_destroyrxbuf(mxfe_rxbuf_t *); static void mxfe_send_setup(mxfe_t *); static boolean_t mxfe_send(mxfe_t *, mblk_t *); static int mxfe_allocrxring(mxfe_t *); static void mxfe_freerxring(mxfe_t *); static int mxfe_alloctxring(mxfe_t *); static void mxfe_freetxring(mxfe_t *); static void mxfe_error(dev_info_t *, char *, ...); static uint8_t mxfe_sromwidth(mxfe_t *); static uint16_t mxfe_readsromword(mxfe_t *, unsigned); static void mxfe_readsrom(mxfe_t *, unsigned, unsigned, void *); static void mxfe_getfactaddr(mxfe_t *, uchar_t *); static uint8_t mxfe_miireadbit(mxfe_t *); static void mxfe_miiwritebit(mxfe_t *, uint8_t); static void mxfe_miitristate(mxfe_t *); static uint16_t mxfe_miiread(mxfe_t *, int, int); static void mxfe_miiwrite(mxfe_t *, int, int, uint16_t); static uint16_t mxfe_miireadgeneral(mxfe_t *, int, int); static void mxfe_miiwritegeneral(mxfe_t *, int, int, uint16_t); static uint16_t mxfe_miiread98713(mxfe_t *, int, int); static void mxfe_miiwrite98713(mxfe_t *, int, int, uint16_t); static void mxfe_startphy(mxfe_t *); static void mxfe_stopphy(mxfe_t *); static void mxfe_startphymii(mxfe_t *); static void mxfe_startphynway(mxfe_t *); static void mxfe_startnway(mxfe_t *); static void mxfe_reportlink(mxfe_t *); static void mxfe_checklink(mxfe_t *); static void mxfe_checklinkmii(mxfe_t *); static void mxfe_checklinknway(mxfe_t *); static void mxfe_disableinterrupts(mxfe_t *); static void mxfe_enableinterrupts(mxfe_t *); static void mxfe_reclaim(mxfe_t *); static boolean_t mxfe_receive(mxfe_t *, mblk_t **); #ifdef DEBUG static void mxfe_dprintf(mxfe_t *, const char *, int, char *, ...); #endif #define KIOIP KSTAT_INTR_PTR(mxfep->mxfe_intrstat) static mac_callbacks_t mxfe_m_callbacks = { MC_SETPROP | MC_GETPROP | MC_PROPINFO, mxfe_m_stat, mxfe_m_start, mxfe_m_stop, mxfe_m_promisc, mxfe_m_multicst, mxfe_m_unicst, mxfe_m_tx, NULL, NULL, /* mc_ioctl */ NULL, /* mc_getcapab */ NULL, /* mc_open */ NULL, /* mc_close */ mxfe_m_setprop, mxfe_m_getprop, mxfe_m_propinfo }; /* * Stream information */ DDI_DEFINE_STREAM_OPS(mxfe_devops, nulldev, nulldev, mxfe_attach, mxfe_detach, nodev, NULL, D_MP, NULL, mxfe_quiesce); /* * Module linkage information. */ static struct modldrv mxfe_modldrv = { &mod_driverops, /* drv_modops */ "Macronix Fast Ethernet", /* drv_linkinfo */ &mxfe_devops /* drv_dev_ops */ }; static struct modlinkage mxfe_modlinkage = { MODREV_1, /* ml_rev */ { &mxfe_modldrv, NULL } /* ml_linkage */ }; /* * Device attributes. */ static ddi_device_acc_attr_t mxfe_devattr = { DDI_DEVICE_ATTR_V0, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC }; static ddi_device_acc_attr_t mxfe_bufattr = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC }; static ddi_dma_attr_t mxfe_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* dma_attr_addr_lo */ 0xFFFFFFFFU, /* dma_attr_addr_hi */ 0x7FFFFFFFU, /* dma_attr_count_max */ 4, /* dma_attr_align */ 0x3F, /* dma_attr_burstsizes */ 1, /* dma_attr_minxfer */ 0xFFFFFFFFU, /* dma_attr_maxxfer */ 0xFFFFFFFFU, /* dma_attr_seg */ 1, /* dma_attr_sgllen */ 1, /* dma_attr_granular */ 0 /* dma_attr_flags */ }; /* * Tx buffers can be arbitrarily aligned. Additionally, they can * cross a page boundary, so we use the two buffer addresses of the * chip to provide a two-entry scatter-gather list. */ static ddi_dma_attr_t mxfe_dma_txattr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* dma_attr_addr_lo */ 0xFFFFFFFFU, /* dma_attr_addr_hi */ 0x7FFFFFFFU, /* dma_attr_count_max */ 1, /* dma_attr_align */ 0x3F, /* dma_attr_burstsizes */ 1, /* dma_attr_minxfer */ 0xFFFFFFFFU, /* dma_attr_maxxfer */ 0xFFFFFFFFU, /* dma_attr_seg */ 2, /* dma_attr_sgllen */ 1, /* dma_attr_granular */ 0 /* dma_attr_flags */ }; /* * Ethernet addresses. */ static uchar_t mxfe_broadcast[ETHERADDRL] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* * DDI entry points. */ int _init(void) { int rv; mac_init_ops(&mxfe_devops, "mxfe"); if ((rv = mod_install(&mxfe_modlinkage)) != DDI_SUCCESS) { mac_fini_ops(&mxfe_devops); } return (rv); } int _fini(void) { int rv; if ((rv = mod_remove(&mxfe_modlinkage)) == DDI_SUCCESS) { mac_fini_ops(&mxfe_devops); } return (rv); } int _info(struct modinfo *modinfop) { return (mod_info(&mxfe_modlinkage, modinfop)); } int mxfe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { mxfe_t *mxfep; mac_register_t *macp; int inst = ddi_get_instance(dip); ddi_acc_handle_t pci; uint16_t venid; uint16_t devid; uint16_t revid; uint16_t svid; uint16_t ssid; uint16_t cachesize; mxfe_card_t *cardp; int i; switch (cmd) { case DDI_RESUME: return (mxfe_resume(dip)); case DDI_ATTACH: break; default: return (DDI_FAILURE); } /* this card is a bus master, reject any slave-only slot */ if (ddi_slaveonly(dip) == DDI_SUCCESS) { mxfe_error(dip, "slot does not support PCI bus-master"); return (DDI_FAILURE); } /* PCI devices shouldn't generate hilevel interrupts */ if (ddi_intr_hilevel(dip, 0) != 0) { mxfe_error(dip, "hilevel interrupts not supported"); return (DDI_FAILURE); } if (pci_config_setup(dip, &pci) != DDI_SUCCESS) { mxfe_error(dip, "unable to setup PCI config handle"); return (DDI_FAILURE); } venid = pci_config_get16(pci, PCI_VID); devid = pci_config_get16(pci, PCI_DID); revid = pci_config_get16(pci, PCI_RID); svid = pci_config_get16(pci, PCI_SVID); ssid = pci_config_get16(pci, PCI_SSID); /* * the last entry in the card table matches every possible * card, so the for-loop always terminates properly. */ cardp = NULL; for (i = 0; i < (sizeof (mxfe_cards) / sizeof (mxfe_card_t)); i++) { if ((venid == mxfe_cards[i].card_venid) && (devid == mxfe_cards[i].card_devid) && ((revid & mxfe_cards[i].card_revmask) == mxfe_cards[i].card_revid)) { cardp = &mxfe_cards[i]; } if ((svid == mxfe_cards[i].card_venid) && (ssid == mxfe_cards[i].card_devid) && ((revid & mxfe_cards[i].card_revmask) == mxfe_cards[i].card_revid)) { cardp = &mxfe_cards[i]; break; } } if (cardp == NULL) { pci_config_teardown(&pci); mxfe_error(dip, "Unable to identify PCI card"); return (DDI_FAILURE); } if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", cardp->card_cardname) != DDI_PROP_SUCCESS) { pci_config_teardown(&pci); mxfe_error(dip, "Unable to create model property"); return (DDI_FAILURE); } /* * Grab the PCI cachesize -- we use this to program the * cache-optimization bus access bits. */ cachesize = pci_config_get8(pci, PCI_CLS); /* this cannot fail */ mxfep = kmem_zalloc(sizeof (mxfe_t), KM_SLEEP); ddi_set_driver_private(dip, mxfep); /* get the interrupt block cookie */ if (ddi_get_iblock_cookie(dip, 0, &mxfep->mxfe_icookie) != DDI_SUCCESS) { mxfe_error(dip, "ddi_get_iblock_cookie failed"); pci_config_teardown(&pci); kmem_free(mxfep, sizeof (mxfe_t)); return (DDI_FAILURE); } mxfep->mxfe_dip = dip; mxfep->mxfe_cardp = cardp; mxfep->mxfe_phyaddr = -1; mxfep->mxfe_cachesize = cachesize; /* default properties */ mxfep->mxfe_adv_aneg = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_autoneg_cap", 1); mxfep->mxfe_adv_100T4 = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_100T4_cap", 1); mxfep->mxfe_adv_100fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_100fdx_cap", 1); mxfep->mxfe_adv_100hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_100hdx_cap", 1); mxfep->mxfe_adv_10fdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_10fdx_cap", 1); mxfep->mxfe_adv_10hdx = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "adv_10hdx_cap", 1); DBG(DPCI, "PCI vendor id = %x", venid); DBG(DPCI, "PCI device id = %x", devid); DBG(DPCI, "PCI revision id = %x", revid); DBG(DPCI, "PCI cachesize = %d", cachesize); DBG(DPCI, "PCI COMM = %x", pci_config_get8(pci, PCI_CMD)); DBG(DPCI, "PCI STAT = %x", pci_config_get8(pci, PCI_STAT)); mutex_init(&mxfep->mxfe_xmtlock, NULL, MUTEX_DRIVER, mxfep->mxfe_icookie); mutex_init(&mxfep->mxfe_intrlock, NULL, MUTEX_DRIVER, mxfep->mxfe_icookie); /* * Enable bus master, IO space, and memory space accesses. */ pci_config_put16(pci, PCI_CMD, pci_config_get16(pci, PCI_CMD) | PCI_CMD_BME | PCI_CMD_MAE | PCI_CMD_MWIE); /* we're done with this now, drop it */ pci_config_teardown(&pci); /* * Initialize interrupt kstat. This should not normally fail, since * we don't use a persistent stat. We do it this way to avoid having * to test for it at run time on the hot path. */ mxfep->mxfe_intrstat = kstat_create("mxfe", inst, "intr", "controller", KSTAT_TYPE_INTR, 1, 0); if (mxfep->mxfe_intrstat == NULL) { mxfe_error(dip, "kstat_create failed"); goto failed; } kstat_install(mxfep->mxfe_intrstat); /* * Map in the device registers. */ if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mxfep->mxfe_regs, 0, 0, &mxfe_devattr, &mxfep->mxfe_regshandle)) { mxfe_error(dip, "ddi_regs_map_setup failed"); goto failed; } /* * Allocate DMA resources (descriptor rings and buffers). */ if ((mxfe_allocrxring(mxfep) != DDI_SUCCESS) || (mxfe_alloctxring(mxfep) != DDI_SUCCESS)) { mxfe_error(dip, "unable to allocate DMA resources"); goto failed; } /* Initialize the chip. */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); if (!mxfe_initialize(mxfep)) { mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); goto failed; } mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); /* Determine the number of address bits to our EEPROM. */ mxfep->mxfe_sromwidth = mxfe_sromwidth(mxfep); /* * Get the factory ethernet address. This becomes the current * ethernet address (it can be overridden later via ifconfig). */ mxfe_getfactaddr(mxfep, mxfep->mxfe_curraddr); mxfep->mxfe_promisc = B_FALSE; /* * Establish interrupt handler. */ if (ddi_add_intr(dip, 0, NULL, NULL, mxfe_intr, (caddr_t)mxfep) != DDI_SUCCESS) { mxfe_error(dip, "unable to add interrupt"); goto failed; } /* TODO: do the power management stuff */ if ((macp = mac_alloc(MAC_VERSION)) == NULL) { mxfe_error(dip, "mac_alloc failed"); goto failed; } macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; macp->m_driver = mxfep; macp->m_dip = dip; macp->m_src_addr = mxfep->mxfe_curraddr; macp->m_callbacks = &mxfe_m_callbacks; macp->m_min_sdu = 0; macp->m_max_sdu = ETHERMTU; macp->m_margin = VLAN_TAGSZ; if (mac_register(macp, &mxfep->mxfe_mh) == DDI_SUCCESS) { mac_free(macp); return (DDI_SUCCESS); } /* failed to register with MAC */ mac_free(macp); failed: if (mxfep->mxfe_icookie != NULL) { ddi_remove_intr(dip, 0, mxfep->mxfe_icookie); } if (mxfep->mxfe_intrstat) { kstat_delete(mxfep->mxfe_intrstat); } mutex_destroy(&mxfep->mxfe_intrlock); mutex_destroy(&mxfep->mxfe_xmtlock); mxfe_freerxring(mxfep); mxfe_freetxring(mxfep); if (mxfep->mxfe_regshandle != NULL) { ddi_regs_map_free(&mxfep->mxfe_regshandle); } kmem_free(mxfep, sizeof (mxfe_t)); return (DDI_FAILURE); } int mxfe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { mxfe_t *mxfep; mxfep = ddi_get_driver_private(dip); if (mxfep == NULL) { mxfe_error(dip, "no soft state in detach!"); return (DDI_FAILURE); } switch (cmd) { case DDI_DETACH: if (mac_unregister(mxfep->mxfe_mh) != 0) { return (DDI_FAILURE); } /* make sure hardware is quiesced */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); mxfep->mxfe_flags &= ~MXFE_RUNNING; mxfe_stopall(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); /* clean up and shut down device */ ddi_remove_intr(dip, 0, mxfep->mxfe_icookie); /* clean up kstats */ kstat_delete(mxfep->mxfe_intrstat); ddi_prop_remove_all(dip); /* free up any left over buffers or DMA resources */ mxfe_freerxring(mxfep); mxfe_freetxring(mxfep); ddi_regs_map_free(&mxfep->mxfe_regshandle); mutex_destroy(&mxfep->mxfe_intrlock); mutex_destroy(&mxfep->mxfe_xmtlock); kmem_free(mxfep, sizeof (mxfe_t)); return (DDI_SUCCESS); case DDI_SUSPEND: /* quiesce the hardware */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); mxfep->mxfe_flags |= MXFE_SUSPENDED; mxfe_stopall(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); return (DDI_SUCCESS); default: return (DDI_FAILURE); } } int mxfe_resume(dev_info_t *dip) { mxfe_t *mxfep; if ((mxfep = ddi_get_driver_private(dip)) == NULL) { return (DDI_FAILURE); } mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); mxfep->mxfe_flags &= ~MXFE_SUSPENDED; /* re-initialize chip */ if (!mxfe_initialize(mxfep)) { mxfe_error(mxfep->mxfe_dip, "unable to resume chip!"); mxfep->mxfe_flags |= MXFE_SUSPENDED; mutex_exit(&mxfep->mxfe_intrlock); mutex_exit(&mxfep->mxfe_xmtlock); return (DDI_SUCCESS); } /* start the chip */ if (mxfep->mxfe_flags & MXFE_RUNNING) { mxfe_startall(mxfep); } /* drop locks */ mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); return (DDI_SUCCESS); } int mxfe_quiesce(dev_info_t *dip) { mxfe_t *mxfep; if ((mxfep = ddi_get_driver_private(dip)) == NULL) { return (DDI_FAILURE); } /* just do a hard reset of everything */ SETBIT(mxfep, CSR_PAR, PAR_RESET); return (DDI_SUCCESS); } /*ARGSUSED*/ int mxfe_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr) { /* we already receive all multicast frames */ return (0); } int mxfe_m_promisc(void *arg, boolean_t on) { mxfe_t *mxfep = arg; /* exclusive access to the card while we reprogram it */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); /* save current promiscuous mode state for replay in resume */ mxfep->mxfe_promisc = on; if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == MXFE_RUNNING) { if (on) SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); else CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); } mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); return (0); } int mxfe_m_unicst(void *arg, const uint8_t *macaddr) { mxfe_t *mxfep = arg; mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); bcopy(macaddr, mxfep->mxfe_curraddr, ETHERADDRL); mxfe_resetall(mxfep); mutex_exit(&mxfep->mxfe_intrlock); mutex_exit(&mxfep->mxfe_xmtlock); return (0); } mblk_t * mxfe_m_tx(void *arg, mblk_t *mp) { mxfe_t *mxfep = arg; mblk_t *nmp; mutex_enter(&mxfep->mxfe_xmtlock); if (mxfep->mxfe_flags & MXFE_SUSPENDED) { mutex_exit(&mxfep->mxfe_xmtlock); return (mp); } while (mp != NULL) { nmp = mp->b_next; mp->b_next = NULL; if (!mxfe_send(mxfep, mp)) { mp->b_next = nmp; break; } mp = nmp; } mutex_exit(&mxfep->mxfe_xmtlock); return (mp); } /* * Hardware management. */ boolean_t mxfe_initialize(mxfe_t *mxfep) { int i; unsigned val; uint32_t par, nar; ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); DBG(DCHATTY, "resetting!"); SETBIT(mxfep, CSR_PAR, PAR_RESET); for (i = 1; i < 10; i++) { drv_usecwait(5); val = GETCSR(mxfep, CSR_PAR); if (!(val & PAR_RESET)) { break; } } if (i == 10) { mxfe_error(mxfep->mxfe_dip, "timed out waiting for reset!"); return (B_FALSE); } /* initialize busctl register */ par = PAR_BAR | PAR_MRME | PAR_MRLE | PAR_MWIE; /* set the cache alignment if its supported */ switch (mxfep->mxfe_cachesize) { case 8: par |= PAR_CALIGN_8; break; case 16: par |= PAR_CALIGN_16; break; case 32: par |= PAR_CALIGN_32; break; default: par &= ~(PAR_MWIE | PAR_MRME | PAR_MRLE); } /* leave the burst length at zero, indicating infinite burst */ PUTCSR(mxfep, CSR_PAR, par); mxfe_resetrings(mxfep); /* clear the lost packet counter (cleared on read) */ (void) GETCSR(mxfep, CSR_LPC); /* a few other NAR bits */ nar = GETCSR(mxfep, CSR_NAR); nar &= ~NAR_RX_HO; /* disable hash only filtering */ nar |= NAR_RX_HP; /* hash perfect forwarding */ nar |= NAR_RX_MULTI; /* receive all multicast */ nar |= NAR_SF; /* store-and-forward */ if (mxfep->mxfe_promisc) { nar |= NAR_RX_PROMISC; } else { nar &= ~NAR_RX_PROMISC; } PUTCSR(mxfep, CSR_NAR, nar); mxfe_send_setup(mxfep); return (B_TRUE); } /* * Serial EEPROM access - inspired by the FreeBSD implementation. */ uint8_t mxfe_sromwidth(mxfe_t *mxfep) { int i; int eeread; uint8_t addrlen = 8; eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP; PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP); drv_usecwait(1); PUTCSR(mxfep, CSR_SPR, eeread); /* command bits first */ for (i = 4; i != 0; i >>= 1) { unsigned val = (SROM_READCMD & i) ? SPR_SROM_DIN : 0; PUTCSR(mxfep, CSR_SPR, eeread | val); drv_usecwait(1); PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK); drv_usecwait(1); } PUTCSR(mxfep, CSR_SPR, eeread); for (addrlen = 1; addrlen <= 12; addrlen++) { PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK); drv_usecwait(1); if (!(GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT)) { PUTCSR(mxfep, CSR_SPR, eeread); drv_usecwait(1); break; } PUTCSR(mxfep, CSR_SPR, eeread); drv_usecwait(1); } /* turn off accesses to the EEPROM */ PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP); DBG(DSROM, "detected srom width = %d bits", addrlen); return ((addrlen < 4 || addrlen > 12) ? 6 : addrlen); } /* * The words in EEPROM are stored in little endian order. We * shift bits out in big endian order, though. This requires * a byte swap on some platforms. */ uint16_t mxfe_readsromword(mxfe_t *mxfep, unsigned romaddr) { int i; uint16_t word = 0; uint16_t retval; int eeread; uint8_t addrlen; int readcmd; uchar_t *ptr; eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP; addrlen = mxfep->mxfe_sromwidth; readcmd = (SROM_READCMD << addrlen) | romaddr; if (romaddr >= (1 << addrlen)) { /* too big to fit! */ return (0); } PUTCSR(mxfep, CSR_SPR, eeread & ~SPR_SROM_CHIP); PUTCSR(mxfep, CSR_SPR, eeread); /* command and address bits */ for (i = 4 + addrlen; i >= 0; i--) { short val = (readcmd & (1 << i)) ? SPR_SROM_DIN : 0; PUTCSR(mxfep, CSR_SPR, eeread | val); drv_usecwait(1); PUTCSR(mxfep, CSR_SPR, eeread | val | SPR_SROM_CLOCK); drv_usecwait(1); } PUTCSR(mxfep, CSR_SPR, eeread); for (i = 0; i < 16; i++) { PUTCSR(mxfep, CSR_SPR, eeread | SPR_SROM_CLOCK); drv_usecwait(1); word <<= 1; if (GETCSR(mxfep, CSR_SPR) & SPR_SROM_DOUT) { word |= 1; } PUTCSR(mxfep, CSR_SPR, eeread); drv_usecwait(1); } /* turn off accesses to the EEPROM */ PUTCSR(mxfep, CSR_SPR, eeread &~ SPR_SROM_CHIP); /* * Fix up the endianness thing. Note that the values * are stored in little endian format on the SROM. */ DBG(DSROM, "got value %d from SROM (before swap)", word); ptr = (uchar_t *)&word; retval = (ptr[1] << 8) | ptr[0]; return (retval); } void mxfe_readsrom(mxfe_t *mxfep, unsigned romaddr, unsigned len, void *dest) { char *ptr = dest; int i; uint16_t word; for (i = 0; i < len; i++) { word = mxfe_readsromword(mxfep, romaddr + i); bcopy(&word, ptr, 2); ptr += 2; DBG(DSROM, "word at %d is 0x%x", romaddr + i, word); } } void mxfe_getfactaddr(mxfe_t *mxfep, uchar_t *eaddr) { uint16_t word; uchar_t *ptr; /* first read to get the location of mac address in srom */ word = mxfe_readsromword(mxfep, SROM_ENADDR / 2); ptr = (uchar_t *)&word; word = (ptr[1] << 8) | ptr[0]; /* then read the actual mac address */ mxfe_readsrom(mxfep, word / 2, ETHERADDRL / 2, eaddr); DBG(DMACID, "factory ethernet address = %02x:%02x:%02x:%02x:%02x:%02x", eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); } void mxfe_startphy(mxfe_t *mxfep) { switch (MXFE_MODEL(mxfep)) { case MXFE_98713A: mxfe_startphymii(mxfep); break; default: mxfe_startphynway(mxfep); break; } } void mxfe_stopphy(mxfe_t *mxfep) { uint32_t nar; int i; /* stop the phy timer */ PUTCSR(mxfep, CSR_TIMER, 0); switch (MXFE_MODEL(mxfep)) { case MXFE_98713A: for (i = 0; i < 32; i++) { mxfe_miiwrite(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL, MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE); } break; default: DBG(DPHY, "resetting SIA"); PUTCSR(mxfep, CSR_SIA, SIA_RESET); drv_usecwait(500); CLRBIT(mxfep, CSR_TCTL, TCTL_PWR | TCTL_ANE); nar = GETCSR(mxfep, CSR_NAR); nar &= ~(NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX); nar |= NAR_SPEED; PUTCSR(mxfep, CSR_NAR, nar); break; } /* * mark the link state unknown */ if (!mxfep->mxfe_resetting) { mxfep->mxfe_linkup = LINK_STATE_UNKNOWN; mxfep->mxfe_ifspeed = 0; mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; if (mxfep->mxfe_flags & MXFE_RUNNING) mxfe_reportlink(mxfep); } } /* * NWay support. */ void mxfe_startnway(mxfe_t *mxfep) { unsigned nar; unsigned tctl; unsigned restart; /* this should not happen in a healthy system */ if (mxfep->mxfe_nwaystate != MXFE_NOLINK) { DBG(DWARN, "link start called out of state (%x)", mxfep->mxfe_nwaystate); return; } if (mxfep->mxfe_adv_aneg == 0) { /* not done for forced mode */ return; } nar = GETCSR(mxfep, CSR_NAR); restart = nar & (NAR_TX_ENABLE | NAR_RX_ENABLE); nar &= ~restart; if (restart != 0) mxfe_stopmac(mxfep); nar |= NAR_SCR | NAR_PCS | NAR_HBD; nar &= ~(NAR_FDX); tctl = GETCSR(mxfep, CSR_TCTL); tctl &= ~(TCTL_100FDX | TCTL_100HDX | TCTL_HDX); if (mxfep->mxfe_adv_100fdx) { tctl |= TCTL_100FDX; } if (mxfep->mxfe_adv_100hdx) { tctl |= TCTL_100HDX; } if (mxfep->mxfe_adv_10fdx) { nar |= NAR_FDX; } if (mxfep->mxfe_adv_10hdx) { tctl |= TCTL_HDX; } tctl |= TCTL_PWR | TCTL_ANE | TCTL_LTE | TCTL_RSQ; /* possibly we should add in support for PAUSE frames */ DBG(DPHY, "writing nar = 0x%x", nar); PUTCSR(mxfep, CSR_NAR, nar); DBG(DPHY, "writing tctl = 0x%x", tctl); PUTCSR(mxfep, CSR_TCTL, tctl); /* restart autonegotation */ DBG(DPHY, "writing tstat = 0x%x", TSTAT_ANS_START); PUTCSR(mxfep, CSR_TSTAT, TSTAT_ANS_START); /* restart tx/rx processes... */ if (restart != 0) mxfe_startmac(mxfep); /* Macronix initializations from Bolo Tsai */ PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000); PUTCSR(mxfep, CSR_ACOMP, 0x11000); mxfep->mxfe_nwaystate = MXFE_NWAYCHECK; } void mxfe_checklinknway(mxfe_t *mxfep) { unsigned tstat; uint16_t lpar; DBG(DPHY, "NWay check, state %x", mxfep->mxfe_nwaystate); tstat = GETCSR(mxfep, CSR_TSTAT); lpar = TSTAT_LPAR(tstat); mxfep->mxfe_anlpar = lpar; if (tstat & TSTAT_LPN) { mxfep->mxfe_aner |= MII_AN_EXP_LPCANAN; } else { mxfep->mxfe_aner &= ~(MII_AN_EXP_LPCANAN); } DBG(DPHY, "tstat(CSR12) = 0x%x", tstat); DBG(DPHY, "ANEG state = 0x%x", (tstat & TSTAT_ANS) >> 12); if ((tstat & TSTAT_ANS) != TSTAT_ANS_OK) { /* autoneg did not complete */ mxfep->mxfe_bmsr &= ~MII_STATUS_ANDONE; } else { mxfep->mxfe_bmsr |= ~MII_STATUS_ANDONE; } if ((tstat & TSTAT_100F) && (tstat & TSTAT_10F)) { mxfep->mxfe_linkup = LINK_STATE_DOWN; mxfep->mxfe_ifspeed = 0; mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; mxfep->mxfe_nwaystate = MXFE_NOLINK; mxfe_reportlink(mxfep); mxfe_startnway(mxfep); return; } /* * if the link is newly up, then we might need to set various * mode bits, or negotiate for parameters, etc. */ if (mxfep->mxfe_adv_aneg) { uint16_t anlpar; mxfep->mxfe_linkup = LINK_STATE_UP; anlpar = mxfep->mxfe_anlpar; if (tstat & TSTAT_LPN) { /* partner has NWay */ if ((anlpar & MII_ABILITY_100BASE_TX_FD) && mxfep->mxfe_adv_100fdx) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if ((anlpar & MII_ABILITY_100BASE_TX) && mxfep->mxfe_adv_100hdx) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else if ((anlpar & MII_ABILITY_10BASE_T_FD) && mxfep->mxfe_adv_10fdx) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if ((anlpar & MII_ABILITY_10BASE_T) && mxfep->mxfe_adv_10hdx) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else { mxfep->mxfe_ifspeed = 0; } } else { /* link partner does not have NWay */ /* just assume half duplex, since we can't detect */ mxfep->mxfe_duplex = LINK_DUPLEX_HALF; if (!(tstat & TSTAT_100F)) { DBG(DPHY, "Partner doesn't have NWAY"); mxfep->mxfe_ifspeed = 100000000; } else { mxfep->mxfe_ifspeed = 10000000; } } } else { /* forced modes */ mxfep->mxfe_linkup = LINK_STATE_UP; if (mxfep->mxfe_adv_100fdx) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if (mxfep->mxfe_adv_100hdx) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else if (mxfep->mxfe_adv_10fdx) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if (mxfep->mxfe_adv_10hdx) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else { mxfep->mxfe_ifspeed = 0; } } mxfe_reportlink(mxfep); mxfep->mxfe_nwaystate = MXFE_GOODLINK; } void mxfe_startphynway(mxfe_t *mxfep) { /* take NWay and PHY out of reset */ PUTCSR(mxfep, CSR_SIA, SIA_NRESET); drv_usecwait(500); mxfep->mxfe_nwaystate = MXFE_NOLINK; mxfep->mxfe_bmsr = MII_STATUS_CANAUTONEG | MII_STATUS_100_BASEX_FD | MII_STATUS_100_BASEX | MII_STATUS_10_FD | MII_STATUS_10; mxfep->mxfe_cap_aneg = mxfep->mxfe_cap_100fdx = mxfep->mxfe_cap_100hdx = mxfep->mxfe_cap_10fdx = mxfep->mxfe_cap_10hdx = 1; /* lie about the transceiver... its not really 802.3u compliant */ mxfep->mxfe_phyaddr = 0; mxfep->mxfe_phyinuse = XCVR_100X; mxfep->mxfe_phyid = 0; /* 100-T4 not supported with NWay */ mxfep->mxfe_adv_100T4 = 0; mxfep->mxfe_cap_100T4 = 0; /* make sure at least one valid mode is selected */ if ((!mxfep->mxfe_adv_100fdx) && (!mxfep->mxfe_adv_100hdx) && (!mxfep->mxfe_adv_10fdx) && (!mxfep->mxfe_adv_10hdx)) { mxfe_error(mxfep->mxfe_dip, "No valid link mode selected."); mxfe_error(mxfep->mxfe_dip, "Powering down PHY."); mxfe_stopphy(mxfep); mxfep->mxfe_linkup = LINK_STATE_DOWN; if (mxfep->mxfe_flags & MXFE_RUNNING) mxfe_reportlink(mxfep); return; } if (mxfep->mxfe_adv_aneg == 0) { /* forced mode */ unsigned nar; unsigned tctl; nar = GETCSR(mxfep, CSR_NAR); tctl = GETCSR(mxfep, CSR_TCTL); ASSERT((nar & (NAR_TX_ENABLE | NAR_RX_ENABLE)) == 0); nar &= ~(NAR_FDX | NAR_PORTSEL | NAR_SCR | NAR_SPEED); tctl &= ~TCTL_ANE; if (mxfep->mxfe_adv_100fdx) { nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR | NAR_FDX; } else if (mxfep->mxfe_adv_100hdx) { nar |= NAR_PORTSEL | NAR_PCS | NAR_SCR; } else if (mxfep->mxfe_adv_10fdx) { nar |= NAR_FDX | NAR_SPEED; } else { /* mxfep->mxfe_adv_10hdx */ nar |= NAR_SPEED; } PUTCSR(mxfep, CSR_NAR, nar); PUTCSR(mxfep, CSR_TCTL, tctl); /* Macronix initializations from Bolo Tsai */ PUTCSR(mxfep, CSR_MXMAGIC, 0x0b2c0000); PUTCSR(mxfep, CSR_ACOMP, 0x11000); } else { mxfe_startnway(mxfep); } PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP | (MXFE_LINKTIMER * 1000 / TIMER_USEC)); } /* * MII management. */ void mxfe_startphymii(mxfe_t *mxfep) { unsigned phyaddr; unsigned bmcr; unsigned bmsr; unsigned anar; unsigned phyidr1; unsigned phyidr2; int retries; int cnt; mxfep->mxfe_phyaddr = -1; /* search for first PHY we can find */ for (phyaddr = 0; phyaddr < 32; phyaddr++) { bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS); if ((bmsr != 0) && (bmsr != 0xffff)) { mxfep->mxfe_phyaddr = phyaddr; break; } } phyidr1 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDH); phyidr2 = mxfe_miiread(mxfep, phyaddr, MII_PHYIDL); mxfep->mxfe_phyid = (phyidr1 << 16) | (phyidr2); /* * Generally, all Macronix based devices use an internal * 100BASE-TX internal transceiver. If we ever run into a * variation on this, then the following logic will need to be * enhanced. * * One could question the value of the XCVR_INUSE field in the * MII statistics. */ if (bmsr & MII_STATUS_100_BASE_T4) { mxfep->mxfe_phyinuse = XCVR_100T4; } else { mxfep->mxfe_phyinuse = XCVR_100X; } /* assume we support everything to start */ mxfep->mxfe_cap_aneg = mxfep->mxfe_cap_100T4 = mxfep->mxfe_cap_100fdx = mxfep->mxfe_cap_100hdx = mxfep->mxfe_cap_10fdx = mxfep->mxfe_cap_10hdx = 1; DBG(DPHY, "phy at %d: %x,%x", phyaddr, phyidr1, phyidr2); DBG(DPHY, "bmsr = %x", mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS)); DBG(DPHY, "anar = %x", mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_ADVERT)); DBG(DPHY, "anlpar = %x", mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_LPABLE)); DBG(DPHY, "aner = %x", mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_EXPANSION)); DBG(DPHY, "resetting phy"); /* we reset the phy block */ mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, MII_CONTROL_RESET); /* * wait for it to complete -- 500usec is still to short to * bother getting the system clock involved. */ drv_usecwait(500); for (retries = 0; retries < 10; retries++) { if (mxfe_miiread(mxfep, phyaddr, MII_CONTROL) & MII_CONTROL_RESET) { drv_usecwait(500); continue; } break; } if (retries == 100) { mxfe_error(mxfep->mxfe_dip, "timeout waiting on phy to reset"); return; } DBG(DPHY, "phy reset complete"); bmsr = mxfe_miiread(mxfep, phyaddr, MII_STATUS); bmcr = mxfe_miiread(mxfep, phyaddr, MII_CONTROL); anar = mxfe_miiread(mxfep, phyaddr, MII_AN_ADVERT); anar &= ~(MII_ABILITY_100BASE_T4 | MII_ABILITY_100BASE_TX_FD | MII_ABILITY_100BASE_TX | MII_ABILITY_10BASE_T_FD | MII_ABILITY_10BASE_T); /* disable modes not supported in hardware */ if (!(bmsr & MII_STATUS_100_BASE_T4)) { mxfep->mxfe_adv_100T4 = 0; mxfep->mxfe_cap_100T4 = 0; } if (!(bmsr & MII_STATUS_100_BASEX_FD)) { mxfep->mxfe_adv_100fdx = 0; mxfep->mxfe_cap_100fdx = 0; } if (!(bmsr & MII_STATUS_100_BASEX)) { mxfep->mxfe_adv_100hdx = 0; mxfep->mxfe_cap_100hdx = 0; } if (!(bmsr & MII_STATUS_10_FD)) { mxfep->mxfe_adv_10fdx = 0; mxfep->mxfe_cap_10fdx = 0; } if (!(bmsr & MII_STATUS_10)) { mxfep->mxfe_adv_10hdx = 0; mxfep->mxfe_cap_10hdx = 0; } if (!(bmsr & MII_STATUS_CANAUTONEG)) { mxfep->mxfe_adv_aneg = 0; mxfep->mxfe_cap_aneg = 0; } cnt = 0; if (mxfep->mxfe_adv_100T4) { anar |= MII_ABILITY_100BASE_T4; cnt++; } if (mxfep->mxfe_adv_100fdx) { anar |= MII_ABILITY_100BASE_TX_FD; cnt++; } if (mxfep->mxfe_adv_100hdx) { anar |= MII_ABILITY_100BASE_TX; cnt++; } if (mxfep->mxfe_adv_10fdx) { anar |= MII_ABILITY_10BASE_T_FD; cnt++; } if (mxfep->mxfe_adv_10hdx) { anar |= MII_ABILITY_10BASE_T; cnt++; } /* * Make certain at least one valid link mode is selected. */ if (!cnt) { mxfe_error(mxfep->mxfe_dip, "No valid link mode selected."); mxfe_error(mxfep->mxfe_dip, "Powering down PHY."); mxfe_stopphy(mxfep); mxfep->mxfe_linkup = LINK_STATE_DOWN; if (mxfep->mxfe_flags & MXFE_RUNNING) mxfe_reportlink(mxfep); return; } if ((mxfep->mxfe_adv_aneg) && (bmsr & MII_STATUS_CANAUTONEG)) { DBG(DPHY, "using autoneg mode"); bmcr = (MII_CONTROL_ANE | MII_CONTROL_RSAN); } else { DBG(DPHY, "using forced mode"); if (mxfep->mxfe_adv_100fdx) { bmcr = (MII_CONTROL_100MB | MII_CONTROL_FDUPLEX); } else if (mxfep->mxfe_adv_100hdx) { bmcr = MII_CONTROL_100MB; } else if (mxfep->mxfe_adv_10fdx) { bmcr = MII_CONTROL_FDUPLEX; } else { /* 10HDX */ bmcr = 0; } } DBG(DPHY, "programming anar to 0x%x", anar); mxfe_miiwrite(mxfep, phyaddr, MII_AN_ADVERT, anar); DBG(DPHY, "programming bmcr to 0x%x", bmcr); mxfe_miiwrite(mxfep, phyaddr, MII_CONTROL, bmcr); /* * schedule a query of the link status */ PUTCSR(mxfep, CSR_TIMER, TIMER_LOOP | (MXFE_LINKTIMER * 1000 / TIMER_USEC)); } void mxfe_reportlink(mxfe_t *mxfep) { int changed = 0; if (mxfep->mxfe_ifspeed != mxfep->mxfe_lastifspeed) { mxfep->mxfe_lastifspeed = mxfep->mxfe_ifspeed; changed++; } if (mxfep->mxfe_duplex != mxfep->mxfe_lastduplex) { mxfep->mxfe_lastduplex = mxfep->mxfe_duplex; changed++; } if (mxfep->mxfe_linkup != mxfep->mxfe_lastlinkup) { mxfep->mxfe_lastlinkup = mxfep->mxfe_linkup; changed++; } if (changed) mac_link_update(mxfep->mxfe_mh, mxfep->mxfe_linkup); } void mxfe_checklink(mxfe_t *mxfep) { if ((mxfep->mxfe_flags & MXFE_RUNNING) == 0) return; if ((mxfep->mxfe_txstall_time != 0) && (gethrtime() > mxfep->mxfe_txstall_time) && (mxfep->mxfe_txavail != MXFE_TXRING)) { mxfep->mxfe_txstall_time = 0; mxfe_error(mxfep->mxfe_dip, "TX stall detected!"); mxfe_resetall(mxfep); return; } switch (MXFE_MODEL(mxfep)) { case MXFE_98713A: mxfe_checklinkmii(mxfep); break; default: mxfe_checklinknway(mxfep); } } void mxfe_checklinkmii(mxfe_t *mxfep) { /* read MII state registers */ uint16_t bmsr; uint16_t bmcr; uint16_t anar; uint16_t anlpar; uint16_t aner; /* read this twice, to clear latched link state */ bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS); bmsr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_STATUS); bmcr = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_CONTROL); anar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_ADVERT); anlpar = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_LPABLE); aner = mxfe_miiread(mxfep, mxfep->mxfe_phyaddr, MII_AN_EXPANSION); mxfep->mxfe_bmsr = bmsr; mxfep->mxfe_anlpar = anlpar; mxfep->mxfe_aner = aner; if (bmsr & MII_STATUS_REMFAULT) { mxfe_error(mxfep->mxfe_dip, "Remote fault detected."); } if (bmsr & MII_STATUS_JABBERING) { mxfe_error(mxfep->mxfe_dip, "Jabber condition detected."); } if ((bmsr & MII_STATUS_LINKUP) == 0) { /* no link */ mxfep->mxfe_ifspeed = 0; mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; mxfep->mxfe_linkup = LINK_STATE_DOWN; mxfe_reportlink(mxfep); return; } DBG(DCHATTY, "link up!"); mxfep->mxfe_linkup = LINK_STATE_UP; if (!(bmcr & MII_CONTROL_ANE)) { /* forced mode */ if (bmcr & MII_CONTROL_100MB) { mxfep->mxfe_ifspeed = 100000000; } else { mxfep->mxfe_ifspeed = 10000000; } if (bmcr & MII_CONTROL_FDUPLEX) { mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else { mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } } else if ((!(bmsr & MII_STATUS_CANAUTONEG)) || (!(bmsr & MII_STATUS_ANDONE))) { mxfep->mxfe_ifspeed = 0; mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; } else if (anar & anlpar & MII_ABILITY_100BASE_TX_FD) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if (anar & anlpar & MII_ABILITY_100BASE_T4) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else if (anar & anlpar & MII_ABILITY_100BASE_TX) { mxfep->mxfe_ifspeed = 100000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else if (anar & anlpar & MII_ABILITY_10BASE_T_FD) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_FULL; } else if (anar & anlpar & MII_ABILITY_10BASE_T) { mxfep->mxfe_ifspeed = 10000000; mxfep->mxfe_duplex = LINK_DUPLEX_HALF; } else { mxfep->mxfe_ifspeed = 0; mxfep->mxfe_duplex = LINK_DUPLEX_UNKNOWN; } mxfe_reportlink(mxfep); } void mxfe_miitristate(mxfe_t *mxfep) { unsigned val = SPR_SROM_WRITE | SPR_MII_CTRL; PUTCSR(mxfep, CSR_SPR, val); drv_usecwait(1); PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); drv_usecwait(1); } void mxfe_miiwritebit(mxfe_t *mxfep, uint8_t bit) { unsigned val = bit ? SPR_MII_DOUT : 0; PUTCSR(mxfep, CSR_SPR, val); drv_usecwait(1); PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); drv_usecwait(1); } uint8_t mxfe_miireadbit(mxfe_t *mxfep) { unsigned val = SPR_MII_CTRL | SPR_SROM_READ; uint8_t bit; PUTCSR(mxfep, CSR_SPR, val); drv_usecwait(1); bit = (GETCSR(mxfep, CSR_SPR) & SPR_MII_DIN) ? 1 : 0; PUTCSR(mxfep, CSR_SPR, val | SPR_MII_CLOCK); drv_usecwait(1); return (bit); } uint16_t mxfe_miiread(mxfe_t *mxfep, int phy, int reg) { switch (MXFE_MODEL(mxfep)) { case MXFE_98713A: return (mxfe_miiread98713(mxfep, phy, reg)); default: return (0xffff); } } uint16_t mxfe_miireadgeneral(mxfe_t *mxfep, int phy, int reg) { uint16_t value = 0; int i; /* send the 32 bit preamble */ for (i = 0; i < 32; i++) { mxfe_miiwritebit(mxfep, 1); } /* send the start code - 01b */ mxfe_miiwritebit(mxfep, 0); mxfe_miiwritebit(mxfep, 1); /* send the opcode for read, - 10b */ mxfe_miiwritebit(mxfep, 1); mxfe_miiwritebit(mxfep, 0); /* next we send the 5 bit phy address */ for (i = 0x10; i > 0; i >>= 1) { mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0); } /* the 5 bit register address goes next */ for (i = 0x10; i > 0; i >>= 1) { mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0); } /* turnaround - tristate followed by logic 0 */ mxfe_miitristate(mxfep); mxfe_miiwritebit(mxfep, 0); /* read the 16 bit register value */ for (i = 0x8000; i > 0; i >>= 1) { value <<= 1; value |= mxfe_miireadbit(mxfep); } mxfe_miitristate(mxfep); return (value); } uint16_t mxfe_miiread98713(mxfe_t *mxfep, int phy, int reg) { unsigned nar; uint16_t retval; /* * like an ordinary MII, but we have to turn off portsel while * we read it. */ nar = GETCSR(mxfep, CSR_NAR); PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL); retval = mxfe_miireadgeneral(mxfep, phy, reg); PUTCSR(mxfep, CSR_NAR, nar); return (retval); } void mxfe_miiwrite(mxfe_t *mxfep, int phy, int reg, uint16_t val) { switch (MXFE_MODEL(mxfep)) { case MXFE_98713A: mxfe_miiwrite98713(mxfep, phy, reg, val); break; default: break; } } void mxfe_miiwritegeneral(mxfe_t *mxfep, int phy, int reg, uint16_t val) { int i; /* send the 32 bit preamble */ for (i = 0; i < 32; i++) { mxfe_miiwritebit(mxfep, 1); } /* send the start code - 01b */ mxfe_miiwritebit(mxfep, 0); mxfe_miiwritebit(mxfep, 1); /* send the opcode for write, - 01b */ mxfe_miiwritebit(mxfep, 0); mxfe_miiwritebit(mxfep, 1); /* next we send the 5 bit phy address */ for (i = 0x10; i > 0; i >>= 1) { mxfe_miiwritebit(mxfep, (phy & i) ? 1 : 0); } /* the 5 bit register address goes next */ for (i = 0x10; i > 0; i >>= 1) { mxfe_miiwritebit(mxfep, (reg & i) ? 1 : 0); } /* turnaround - tristate followed by logic 0 */ mxfe_miitristate(mxfep); mxfe_miiwritebit(mxfep, 0); /* now write out our data (16 bits) */ for (i = 0x8000; i > 0; i >>= 1) { mxfe_miiwritebit(mxfep, (val & i) ? 1 : 0); } /* idle mode */ mxfe_miitristate(mxfep); } void mxfe_miiwrite98713(mxfe_t *mxfep, int phy, int reg, uint16_t val) { unsigned nar; /* * like an ordinary MII, but we have to turn off portsel while * we read it. */ nar = GETCSR(mxfep, CSR_NAR); PUTCSR(mxfep, CSR_NAR, nar & ~NAR_PORTSEL); mxfe_miiwritegeneral(mxfep, phy, reg, val); PUTCSR(mxfep, CSR_NAR, nar); } int mxfe_m_start(void *arg) { mxfe_t *mxfep = arg; /* grab exclusive access to the card */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); mxfe_startall(mxfep); mxfep->mxfe_flags |= MXFE_RUNNING; mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); return (0); } void mxfe_m_stop(void *arg) { mxfe_t *mxfep = arg; /* exclusive access to the hardware! */ mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); mxfe_stopall(mxfep); mxfep->mxfe_flags &= ~MXFE_RUNNING; mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); } void mxfe_startmac(mxfe_t *mxfep) { /* verify exclusive access to the card */ ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); /* start the card */ SETBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE); if (mxfep->mxfe_txavail != MXFE_TXRING) PUTCSR(mxfep, CSR_TDR, 0); /* tell the mac that we are ready to go! */ if (mxfep->mxfe_flags & MXFE_RUNNING) mac_tx_update(mxfep->mxfe_mh); } void mxfe_stopmac(mxfe_t *mxfep) { int i; /* exclusive access to the hardware! */ ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); CLRBIT(mxfep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE); /* * A 1518 byte frame at 10Mbps takes about 1.2 msec to drain. * We just add up to the nearest msec (2), which should be * plenty to complete. * * Note that some chips never seem to indicate the transition to * the stopped state properly. Experience shows that we can safely * proceed anyway, after waiting the requisite timeout. */ for (i = 2000; i != 0; i -= 10) { if ((GETCSR(mxfep, CSR_SR) & (SR_TX_STATE | SR_RX_STATE)) == 0) break; drv_usecwait(10); } /* prevent an interrupt */ PUTCSR(mxfep, CSR_SR, INT_RXSTOPPED | INT_TXSTOPPED); } void mxfe_resetrings(mxfe_t *mxfep) { int i; /* now we need to reset the pointers... */ PUTCSR(mxfep, CSR_RDB, 0); PUTCSR(mxfep, CSR_TDB, 0); /* reset the descriptor ring pointers */ mxfep->mxfe_rxhead = 0; mxfep->mxfe_txreclaim = 0; mxfep->mxfe_txsend = 0; mxfep->mxfe_txavail = MXFE_TXRING; /* set up transmit descriptor ring */ for (i = 0; i < MXFE_TXRING; i++) { mxfe_desc_t *tmdp = &mxfep->mxfe_txdescp[i]; unsigned control = 0; if (i == (MXFE_TXRING - 1)) { control |= TXCTL_ENDRING; } PUTTXDESC(mxfep, tmdp->desc_status, 0); PUTTXDESC(mxfep, tmdp->desc_control, control); PUTTXDESC(mxfep, tmdp->desc_buffer1, 0); PUTTXDESC(mxfep, tmdp->desc_buffer2, 0); SYNCTXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV); } PUTCSR(mxfep, CSR_TDB, mxfep->mxfe_txdesc_paddr); /* make the receive buffers available */ for (i = 0; i < MXFE_RXRING; i++) { mxfe_rxbuf_t *rxb = mxfep->mxfe_rxbufs[i]; mxfe_desc_t *rmdp = &mxfep->mxfe_rxdescp[i]; unsigned control; control = MXFE_BUFSZ & RXCTL_BUFLEN1; if (i == (MXFE_RXRING - 1)) { control |= RXCTL_ENDRING; } PUTRXDESC(mxfep, rmdp->desc_buffer1, rxb->rxb_paddr); PUTRXDESC(mxfep, rmdp->desc_buffer2, 0); PUTRXDESC(mxfep, rmdp->desc_control, control); PUTRXDESC(mxfep, rmdp->desc_status, RXSTAT_OWN); SYNCRXDESC(mxfep, i, DDI_DMA_SYNC_FORDEV); } PUTCSR(mxfep, CSR_RDB, mxfep->mxfe_rxdesc_paddr); } void mxfe_stopall(mxfe_t *mxfep) { mxfe_disableinterrupts(mxfep); mxfe_stopmac(mxfep); /* stop the phy */ mxfe_stopphy(mxfep); } void mxfe_startall(mxfe_t *mxfep) { ASSERT(mutex_owned(&mxfep->mxfe_intrlock)); ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); /* make sure interrupts are disabled to begin */ mxfe_disableinterrupts(mxfep); /* initialize the chip */ (void) mxfe_initialize(mxfep); /* now we can enable interrupts */ mxfe_enableinterrupts(mxfep); /* start up the phy */ mxfe_startphy(mxfep); /* start up the mac */ mxfe_startmac(mxfep); } void mxfe_resetall(mxfe_t *mxfep) { mxfep->mxfe_resetting = B_TRUE; mxfe_stopall(mxfep); mxfep->mxfe_resetting = B_FALSE; mxfe_startall(mxfep); } mxfe_txbuf_t * mxfe_alloctxbuf(mxfe_t *mxfep) { ddi_dma_cookie_t dmac; unsigned ncookies; mxfe_txbuf_t *txb; size_t len; txb = kmem_zalloc(sizeof (*txb), KM_SLEEP); if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_txattr, DDI_DMA_SLEEP, NULL, &txb->txb_dmah) != DDI_SUCCESS) { return (NULL); } if (ddi_dma_mem_alloc(txb->txb_dmah, MXFE_BUFSZ, &mxfe_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &txb->txb_buf, &len, &txb->txb_acch) != DDI_SUCCESS) { return (NULL); } if (ddi_dma_addr_bind_handle(txb->txb_dmah, NULL, txb->txb_buf, len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac, &ncookies) != DDI_DMA_MAPPED) { return (NULL); } txb->txb_paddr = dmac.dmac_address; return (txb); } void mxfe_destroytxbuf(mxfe_txbuf_t *txb) { if (txb != NULL) { if (txb->txb_paddr) (void) ddi_dma_unbind_handle(txb->txb_dmah); if (txb->txb_acch) ddi_dma_mem_free(&txb->txb_acch); if (txb->txb_dmah) ddi_dma_free_handle(&txb->txb_dmah); kmem_free(txb, sizeof (*txb)); } } mxfe_rxbuf_t * mxfe_allocrxbuf(mxfe_t *mxfep) { mxfe_rxbuf_t *rxb; size_t len; unsigned ccnt; ddi_dma_cookie_t dmac; rxb = kmem_zalloc(sizeof (*rxb), KM_SLEEP); if (ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, DDI_DMA_SLEEP, NULL, &rxb->rxb_dmah) != DDI_SUCCESS) { kmem_free(rxb, sizeof (*rxb)); return (NULL); } if (ddi_dma_mem_alloc(rxb->rxb_dmah, MXFE_BUFSZ, &mxfe_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &rxb->rxb_buf, &len, &rxb->rxb_acch) != DDI_SUCCESS) { ddi_dma_free_handle(&rxb->rxb_dmah); kmem_free(rxb, sizeof (*rxb)); return (NULL); } if (ddi_dma_addr_bind_handle(rxb->rxb_dmah, NULL, rxb->rxb_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac, &ccnt) != DDI_DMA_MAPPED) { ddi_dma_mem_free(&rxb->rxb_acch); ddi_dma_free_handle(&rxb->rxb_dmah); kmem_free(rxb, sizeof (*rxb)); return (NULL); } rxb->rxb_paddr = dmac.dmac_address; return (rxb); } void mxfe_destroyrxbuf(mxfe_rxbuf_t *rxb) { if (rxb != NULL) { (void) ddi_dma_unbind_handle(rxb->rxb_dmah); ddi_dma_mem_free(&rxb->rxb_acch); ddi_dma_free_handle(&rxb->rxb_dmah); kmem_free(rxb, sizeof (*rxb)); } } /* * Allocate receive resources. */ int mxfe_allocrxring(mxfe_t *mxfep) { int rval; int i; size_t size; size_t len; ddi_dma_cookie_t dmac; unsigned ncookies; caddr_t kaddr; size = MXFE_RXRING * sizeof (mxfe_desc_t); rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, DDI_DMA_SLEEP, NULL, &mxfep->mxfe_rxdesc_dmah); if (rval != DDI_SUCCESS) { mxfe_error(mxfep->mxfe_dip, "unable to allocate DMA handle for rx descriptors"); return (DDI_FAILURE); } rval = ddi_dma_mem_alloc(mxfep->mxfe_rxdesc_dmah, size, &mxfe_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len, &mxfep->mxfe_rxdesc_acch); if (rval != DDI_SUCCESS) { mxfe_error(mxfep->mxfe_dip, "unable to allocate DMA memory for rx descriptors"); return (DDI_FAILURE); } rval = ddi_dma_addr_bind_handle(mxfep->mxfe_rxdesc_dmah, NULL, kaddr, size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac, &ncookies); if (rval != DDI_DMA_MAPPED) { mxfe_error(mxfep->mxfe_dip, "unable to bind DMA for rx descriptors"); return (DDI_FAILURE); } /* because of mxfe_dma_attr */ ASSERT(ncookies == 1); /* we take the 32-bit physical address out of the cookie */ mxfep->mxfe_rxdesc_paddr = dmac.dmac_address; mxfep->mxfe_rxdescp = (void *)kaddr; /* allocate buffer pointers (not the buffers themselves, yet) */ mxfep->mxfe_rxbufs = kmem_zalloc(MXFE_RXRING * sizeof (mxfe_rxbuf_t *), KM_SLEEP); /* now allocate rx buffers */ for (i = 0; i < MXFE_RXRING; i++) { mxfe_rxbuf_t *rxb = mxfe_allocrxbuf(mxfep); if (rxb == NULL) return (DDI_FAILURE); mxfep->mxfe_rxbufs[i] = rxb; } return (DDI_SUCCESS); } /* * Allocate transmit resources. */ int mxfe_alloctxring(mxfe_t *mxfep) { int rval; int i; size_t size; size_t len; ddi_dma_cookie_t dmac; unsigned ncookies; caddr_t kaddr; size = MXFE_TXRING * sizeof (mxfe_desc_t); rval = ddi_dma_alloc_handle(mxfep->mxfe_dip, &mxfe_dma_attr, DDI_DMA_SLEEP, NULL, &mxfep->mxfe_txdesc_dmah); if (rval != DDI_SUCCESS) { mxfe_error(mxfep->mxfe_dip, "unable to allocate DMA handle for tx descriptors"); return (DDI_FAILURE); } rval = ddi_dma_mem_alloc(mxfep->mxfe_txdesc_dmah, size, &mxfe_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len, &mxfep->mxfe_txdesc_acch); if (rval != DDI_SUCCESS) { mxfe_error(mxfep->mxfe_dip, "unable to allocate DMA memory for tx descriptors"); return (DDI_FAILURE); } rval = ddi_dma_addr_bind_handle(mxfep->mxfe_txdesc_dmah, NULL, kaddr, size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac, &ncookies); if (rval != DDI_DMA_MAPPED) { mxfe_error(mxfep->mxfe_dip, "unable to bind DMA for tx descriptors"); return (DDI_FAILURE); } /* because of mxfe_dma_attr */ ASSERT(ncookies == 1); /* we take the 32-bit physical address out of the cookie */ mxfep->mxfe_txdesc_paddr = dmac.dmac_address; mxfep->mxfe_txdescp = (void *)kaddr; /* allocate buffer pointers (not the buffers themselves, yet) */ mxfep->mxfe_txbufs = kmem_zalloc(MXFE_TXRING * sizeof (mxfe_txbuf_t *), KM_SLEEP); /* now allocate tx buffers */ for (i = 0; i < MXFE_TXRING; i++) { mxfe_txbuf_t *txb = mxfe_alloctxbuf(mxfep); if (txb == NULL) return (DDI_FAILURE); /* stick it in the stack */ mxfep->mxfe_txbufs[i] = txb; } return (DDI_SUCCESS); } void mxfe_freerxring(mxfe_t *mxfep) { int i; if (mxfep->mxfe_rxbufs) { for (i = 0; i < MXFE_RXRING; i++) { mxfe_destroyrxbuf(mxfep->mxfe_rxbufs[i]); } kmem_free(mxfep->mxfe_rxbufs, MXFE_RXRING * sizeof (mxfe_rxbuf_t *)); } if (mxfep->mxfe_rxdesc_paddr) (void) ddi_dma_unbind_handle(mxfep->mxfe_rxdesc_dmah); if (mxfep->mxfe_rxdesc_acch) ddi_dma_mem_free(&mxfep->mxfe_rxdesc_acch); if (mxfep->mxfe_rxdesc_dmah) ddi_dma_free_handle(&mxfep->mxfe_rxdesc_dmah); } void mxfe_freetxring(mxfe_t *mxfep) { int i; if (mxfep->mxfe_txbufs) { for (i = 0; i < MXFE_TXRING; i++) { mxfe_destroytxbuf(mxfep->mxfe_txbufs[i]); } kmem_free(mxfep->mxfe_txbufs, MXFE_TXRING * sizeof (mxfe_txbuf_t *)); } if (mxfep->mxfe_txdesc_paddr) (void) ddi_dma_unbind_handle(mxfep->mxfe_txdesc_dmah); if (mxfep->mxfe_txdesc_acch) ddi_dma_mem_free(&mxfep->mxfe_txdesc_acch); if (mxfep->mxfe_txdesc_dmah) ddi_dma_free_handle(&mxfep->mxfe_txdesc_dmah); } /* * Interrupt service routine. */ unsigned mxfe_intr(caddr_t arg) { mxfe_t *mxfep = (void *)arg; uint32_t status; mblk_t *mp = NULL; boolean_t error = B_FALSE; mutex_enter(&mxfep->mxfe_intrlock); if (mxfep->mxfe_flags & MXFE_SUSPENDED) { /* we cannot receive interrupts! */ mutex_exit(&mxfep->mxfe_intrlock); return (DDI_INTR_UNCLAIMED); } /* check interrupt status bits, did we interrupt? */ status = GETCSR(mxfep, CSR_SR) & INT_ALL; if (status == 0) { KIOIP->intrs[KSTAT_INTR_SPURIOUS]++; mutex_exit(&mxfep->mxfe_intrlock); return (DDI_INTR_UNCLAIMED); } /* ack the interrupt */ PUTCSR(mxfep, CSR_SR, status); KIOIP->intrs[KSTAT_INTR_HARD]++; if (!(mxfep->mxfe_flags & MXFE_RUNNING)) { /* not running, don't touch anything */ mutex_exit(&mxfep->mxfe_intrlock); return (DDI_INTR_CLAIMED); } if (status & INT_RXOK) { /* receive packets */ if (mxfe_receive(mxfep, &mp)) { error = B_TRUE; } } if (status & INT_TXOK) { /* transmit completed */ mutex_enter(&mxfep->mxfe_xmtlock); mxfe_reclaim(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); } if (((status & (INT_TIMER|INT_ANEG)) != 0) || ((mxfep->mxfe_linkup == LINK_STATE_UP) && ((status & (INT_10LINK|INT_100LINK)) != 0))) { /* rescan the link */ mutex_enter(&mxfep->mxfe_xmtlock); mxfe_checklink(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); } if (status & (INT_RXSTOPPED|INT_TXSTOPPED|INT_RXNOBUF| INT_RXJABBER|INT_TXJABBER|INT_TXUNDERFLOW)) { if (status & (INT_RXJABBER | INT_TXJABBER)) { mxfep->mxfe_jabber++; } DBG(DWARN, "error interrupt: status %x", status); error = B_TRUE; } if (status & INT_BUSERR) { switch (status & SR_BERR_TYPE) { case SR_BERR_PARITY: mxfe_error(mxfep->mxfe_dip, "PCI parity error"); break; case SR_BERR_TARGET_ABORT: mxfe_error(mxfep->mxfe_dip, "PCI target abort"); break; case SR_BERR_MASTER_ABORT: mxfe_error(mxfep->mxfe_dip, "PCI master abort"); break; default: mxfe_error(mxfep->mxfe_dip, "Unknown PCI error"); break; } error = B_TRUE; } if (error) { /* reset the chip in an attempt to fix things */ mutex_enter(&mxfep->mxfe_xmtlock); mxfe_resetall(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); } mutex_exit(&mxfep->mxfe_intrlock); /* * Send up packets. We do this outside of the intrlock. */ if (mp) { mac_rx(mxfep->mxfe_mh, NULL, mp); } return (DDI_INTR_CLAIMED); } void mxfe_enableinterrupts(mxfe_t *mxfep) { unsigned mask = INT_WANTED; if (mxfep->mxfe_wantw) mask |= INT_TXOK; if (MXFE_MODEL(mxfep) != MXFE_98713A) mask |= INT_LINKSTATUS; DBG(DINTR, "setting int mask to 0x%x", mask); PUTCSR(mxfep, CSR_IER, mask); } void mxfe_disableinterrupts(mxfe_t *mxfep) { /* disable further interrupts */ PUTCSR(mxfep, CSR_IER, 0); /* clear any pending interrupts */ PUTCSR(mxfep, CSR_SR, INT_ALL); } void mxfe_send_setup(mxfe_t *mxfep) { mxfe_txbuf_t *txb; mxfe_desc_t *tmdp; ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); /* setup frame -- must be at head of list -- guaranteed by caller! */ ASSERT(mxfep->mxfe_txsend == 0); txb = mxfep->mxfe_txbufs[0]; tmdp = &mxfep->mxfe_txdescp[0]; bzero(txb->txb_buf, MXFE_SETUP_LEN); /* program the unicast address */ txb->txb_buf[156] = mxfep->mxfe_curraddr[0]; txb->txb_buf[157] = mxfep->mxfe_curraddr[1]; txb->txb_buf[160] = mxfep->mxfe_curraddr[2]; txb->txb_buf[161] = mxfep->mxfe_curraddr[3]; txb->txb_buf[164] = mxfep->mxfe_curraddr[4]; txb->txb_buf[165] = mxfep->mxfe_curraddr[5]; /* make sure that the hardware can see it */ SYNCTXBUF(txb, MXFE_SETUP_LEN, DDI_DMA_SYNC_FORDEV); PUTTXDESC(mxfep, tmdp->desc_control, TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE | TXCTL_HASHPERF | TXCTL_SETUP | MXFE_SETUP_LEN); PUTTXDESC(mxfep, tmdp->desc_buffer1, txb->txb_paddr); PUTTXDESC(mxfep, tmdp->desc_buffer2, 0); PUTTXDESC(mxfep, tmdp->desc_status, TXSTAT_OWN); /* sync the descriptor out to the device */ SYNCTXDESC(mxfep, 0, DDI_DMA_SYNC_FORDEV); /* * wake up the chip ... inside the lock to protect against DR suspend, * etc. */ PUTCSR(mxfep, CSR_TDR, 0); mxfep->mxfe_txsend++; mxfep->mxfe_txavail--; /* * Program promiscuous mode. */ if (mxfep->mxfe_promisc) { SETBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); } else { CLRBIT(mxfep, CSR_NAR, NAR_RX_PROMISC); } } boolean_t mxfe_send(mxfe_t *mxfep, mblk_t *mp) { size_t len; mxfe_txbuf_t *txb; mxfe_desc_t *tmd; uint32_t control; int txsend; ASSERT(mutex_owned(&mxfep->mxfe_xmtlock)); ASSERT(mp != NULL); len = msgsize(mp); if (len > ETHERVLANMTU) { DBG(DXMIT, "frame too long: %d", len); mxfep->mxfe_macxmt_errors++; freemsg(mp); return (B_TRUE); } if (mxfep->mxfe_txavail < MXFE_TXRECLAIM) mxfe_reclaim(mxfep); if (mxfep->mxfe_txavail == 0) { /* no more tmds */ mxfep->mxfe_wantw = B_TRUE; /* enable TX interrupt */ mxfe_enableinterrupts(mxfep); return (B_FALSE); } txsend = mxfep->mxfe_txsend; /* * For simplicity, we just do a copy into a preallocated * DMA buffer. */ txb = mxfep->mxfe_txbufs[txsend]; mcopymsg(mp, txb->txb_buf); /* frees mp! */ /* * Statistics. */ mxfep->mxfe_opackets++; mxfep->mxfe_obytes += len; if (txb->txb_buf[0] & 0x1) { if (bcmp(txb->txb_buf, mxfe_broadcast, ETHERADDRL) != 0) mxfep->mxfe_multixmt++; else mxfep->mxfe_brdcstxmt++; } /* note len is already known to be a small unsigned */ control = len | TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE; if (txsend == (MXFE_TXRING - 1)) control |= TXCTL_ENDRING; tmd = &mxfep->mxfe_txdescp[txsend]; SYNCTXBUF(txb, len, DDI_DMA_SYNC_FORDEV); PUTTXDESC(mxfep, tmd->desc_control, control); PUTTXDESC(mxfep, tmd->desc_buffer1, txb->txb_paddr); PUTTXDESC(mxfep, tmd->desc_buffer2, 0); PUTTXDESC(mxfep, tmd->desc_status, TXSTAT_OWN); /* sync the descriptor out to the device */ SYNCTXDESC(mxfep, txsend, DDI_DMA_SYNC_FORDEV); /* * Note the new values of txavail and txsend. */ mxfep->mxfe_txavail--; mxfep->mxfe_txsend = (txsend + 1) % MXFE_TXRING; /* * It should never, ever take more than 5 seconds to drain * the ring. If it happens, then we are stuck! */ mxfep->mxfe_txstall_time = gethrtime() + (5 * 1000000000ULL); /* * wake up the chip ... inside the lock to protect against DR suspend, * etc. */ PUTCSR(mxfep, CSR_TDR, 0); return (B_TRUE); } /* * Reclaim buffers that have completed transmission. */ void mxfe_reclaim(mxfe_t *mxfep) { mxfe_desc_t *tmdp; while (mxfep->mxfe_txavail != MXFE_TXRING) { uint32_t status; uint32_t control; int index = mxfep->mxfe_txreclaim; tmdp = &mxfep->mxfe_txdescp[index]; /* sync it before we read it */ SYNCTXDESC(mxfep, index, DDI_DMA_SYNC_FORKERNEL); control = GETTXDESC(mxfep, tmdp->desc_control); status = GETTXDESC(mxfep, tmdp->desc_status); if (status & TXSTAT_OWN) { /* chip is still working on it, we're done */ break; } mxfep->mxfe_txavail++; mxfep->mxfe_txreclaim = (index + 1) % MXFE_TXRING; /* in the most common successful case, all bits are clear */ if (status == 0) continue; if (((control & TXCTL_SETUP) != 0) || ((control & TXCTL_LAST) == 0)) { /* no interesting statistics here */ continue; } if (status & TXSTAT_TXERR) { mxfep->mxfe_errxmt++; if (status & TXSTAT_JABBER) { /* transmit jabber timeout */ mxfep->mxfe_macxmt_errors++; } if (status & (TXSTAT_CARRLOST | TXSTAT_NOCARR)) { mxfep->mxfe_carrier_errors++; } if (status & TXSTAT_UFLOW) { mxfep->mxfe_underflow++; } if (status & TXSTAT_LATECOL) { mxfep->mxfe_tx_late_collisions++; } if (status & TXSTAT_EXCOLL) { mxfep->mxfe_ex_collisions++; mxfep->mxfe_collisions += 16; } } if (status & TXSTAT_DEFER) { mxfep->mxfe_defer_xmts++; } /* collision counting */ if (TXCOLLCNT(status) == 1) { mxfep->mxfe_collisions++; mxfep->mxfe_first_collisions++; } else if (TXCOLLCNT(status)) { mxfep->mxfe_collisions += TXCOLLCNT(status); mxfep->mxfe_multi_collisions += TXCOLLCNT(status); } } if (mxfep->mxfe_txavail >= MXFE_TXRESCHED) { if (mxfep->mxfe_wantw) { /* * we were able to reclaim some packets, so * disable tx interrupts */ mxfep->mxfe_wantw = B_FALSE; mxfe_enableinterrupts(mxfep); mac_tx_update(mxfep->mxfe_mh); } } } boolean_t mxfe_receive(mxfe_t *mxfep, mblk_t **rxchain) { unsigned len; mxfe_rxbuf_t *rxb; mxfe_desc_t *rmd; uint32_t status; mblk_t *mpchain, **mpp, *mp; int head, cnt; boolean_t error = B_FALSE; mpchain = NULL; mpp = &mpchain; head = mxfep->mxfe_rxhead; /* limit the number of packets we process to a ring size */ for (cnt = 0; cnt < MXFE_RXRING; cnt++) { DBG(DRECV, "receive at index %d", head); rmd = &mxfep->mxfe_rxdescp[head]; rxb = mxfep->mxfe_rxbufs[head]; SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORKERNEL); status = GETRXDESC(mxfep, rmd->desc_status); if (status & RXSTAT_OWN) { /* chip is still chewing on it */ break; } /* discard the ethernet frame checksum */ len = RXLENGTH(status) - ETHERFCSL; DBG(DRECV, "recv length %d, status %x", len, status); if ((status & (RXSTAT_ERRS | RXSTAT_FIRST | RXSTAT_LAST)) != (RXSTAT_FIRST | RXSTAT_LAST)) { mxfep->mxfe_errrcv++; /* * Abnormal status bits detected, analyze further. */ if ((status & (RXSTAT_LAST|RXSTAT_FIRST)) != (RXSTAT_LAST|RXSTAT_FIRST)) { /* someone trying to send jumbo frames? */ DBG(DRECV, "rx packet overspill"); if (status & RXSTAT_FIRST) { mxfep->mxfe_toolong_errors++; } } else if (status & RXSTAT_DESCERR) { /* this should never occur! */ mxfep->mxfe_macrcv_errors++; error = B_TRUE; } else if (status & RXSTAT_RUNT) { mxfep->mxfe_runt++; } else if (status & RXSTAT_COLLSEEN) { /* this should really be rx_late_collisions */ mxfep->mxfe_macrcv_errors++; } else if (status & RXSTAT_DRIBBLE) { mxfep->mxfe_align_errors++; } else if (status & RXSTAT_CRCERR) { mxfep->mxfe_fcs_errors++; } else if (status & RXSTAT_OFLOW) { /* this is a MAC FIFO error, need to reset */ mxfep->mxfe_overflow++; error = B_TRUE; } } else if (len > ETHERVLANMTU) { mxfep->mxfe_errrcv++; mxfep->mxfe_toolong_errors++; } /* * At this point, the chip thinks the packet is OK. */ else { mp = allocb(len + MXFE_HEADROOM, 0); if (mp == NULL) { mxfep->mxfe_errrcv++; mxfep->mxfe_norcvbuf++; goto skip; } /* sync the buffer before we look at it */ SYNCRXBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL); mp->b_rptr += MXFE_HEADROOM; mp->b_wptr = mp->b_rptr + len; bcopy((char *)rxb->rxb_buf, mp->b_rptr, len); mxfep->mxfe_ipackets++; mxfep->mxfe_rbytes += len; if (status & RXSTAT_GROUP) { if (bcmp(mp->b_rptr, mxfe_broadcast, ETHERADDRL) == 0) mxfep->mxfe_brdcstrcv++; else mxfep->mxfe_multircv++; } *mpp = mp; mpp = &mp->b_next; } skip: /* return ring entry to the hardware */ PUTRXDESC(mxfep, rmd->desc_status, RXSTAT_OWN); SYNCRXDESC(mxfep, head, DDI_DMA_SYNC_FORDEV); /* advance to next RMD */ head = (head + 1) % MXFE_RXRING; } mxfep->mxfe_rxhead = head; *rxchain = mpchain; return (error); } int mxfe_m_stat(void *arg, uint_t stat, uint64_t *val) { mxfe_t *mxfep = arg; mutex_enter(&mxfep->mxfe_xmtlock); if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == MXFE_RUNNING) mxfe_reclaim(mxfep); mutex_exit(&mxfep->mxfe_xmtlock); switch (stat) { case MAC_STAT_IFSPEED: *val = mxfep->mxfe_ifspeed; break; case MAC_STAT_MULTIRCV: *val = mxfep->mxfe_multircv; break; case MAC_STAT_BRDCSTRCV: *val = mxfep->mxfe_brdcstrcv; break; case MAC_STAT_MULTIXMT: *val = mxfep->mxfe_multixmt; break; case MAC_STAT_BRDCSTXMT: *val = mxfep->mxfe_brdcstxmt; break; case MAC_STAT_IPACKETS: *val = mxfep->mxfe_ipackets; break; case MAC_STAT_RBYTES: *val = mxfep->mxfe_rbytes; break; case MAC_STAT_OPACKETS: *val = mxfep->mxfe_opackets; break; case MAC_STAT_OBYTES: *val = mxfep->mxfe_obytes; break; case MAC_STAT_NORCVBUF: *val = mxfep->mxfe_norcvbuf; break; case MAC_STAT_NOXMTBUF: *val = mxfep->mxfe_noxmtbuf; break; case MAC_STAT_COLLISIONS: *val = mxfep->mxfe_collisions; break; case MAC_STAT_IERRORS: *val = mxfep->mxfe_errrcv; break; case MAC_STAT_OERRORS: *val = mxfep->mxfe_errxmt; break; case ETHER_STAT_LINK_DUPLEX: *val = mxfep->mxfe_duplex; break; case ETHER_STAT_ALIGN_ERRORS: *val = mxfep->mxfe_align_errors; break; case ETHER_STAT_FCS_ERRORS: *val = mxfep->mxfe_fcs_errors; break; case ETHER_STAT_SQE_ERRORS: *val = mxfep->mxfe_sqe_errors; break; case ETHER_STAT_DEFER_XMTS: *val = mxfep->mxfe_defer_xmts; break; case ETHER_STAT_FIRST_COLLISIONS: *val = mxfep->mxfe_first_collisions; break; case ETHER_STAT_MULTI_COLLISIONS: *val = mxfep->mxfe_multi_collisions; break; case ETHER_STAT_TX_LATE_COLLISIONS: *val = mxfep->mxfe_tx_late_collisions; break; case ETHER_STAT_EX_COLLISIONS: *val = mxfep->mxfe_ex_collisions; break; case ETHER_STAT_MACXMT_ERRORS: *val = mxfep->mxfe_macxmt_errors; break; case ETHER_STAT_CARRIER_ERRORS: *val = mxfep->mxfe_carrier_errors; break; case ETHER_STAT_TOOLONG_ERRORS: *val = mxfep->mxfe_toolong_errors; break; case ETHER_STAT_MACRCV_ERRORS: *val = mxfep->mxfe_macrcv_errors; break; case MAC_STAT_OVERFLOWS: *val = mxfep->mxfe_overflow; break; case MAC_STAT_UNDERFLOWS: *val = mxfep->mxfe_underflow; break; case ETHER_STAT_TOOSHORT_ERRORS: *val = mxfep->mxfe_runt; break; case ETHER_STAT_JABBER_ERRORS: *val = mxfep->mxfe_jabber; break; case ETHER_STAT_ADV_CAP_100T4: *val = mxfep->mxfe_adv_100T4; break; case ETHER_STAT_LP_CAP_100T4: *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_T4) ? 1 : 0; break; case ETHER_STAT_CAP_100T4: *val = mxfep->mxfe_cap_100T4; break; case ETHER_STAT_CAP_100FDX: *val = mxfep->mxfe_cap_100fdx; break; case ETHER_STAT_CAP_100HDX: *val = mxfep->mxfe_cap_100hdx; break; case ETHER_STAT_CAP_10FDX: *val = mxfep->mxfe_cap_10fdx; break; case ETHER_STAT_CAP_10HDX: *val = mxfep->mxfe_cap_10hdx; break; case ETHER_STAT_CAP_AUTONEG: *val = mxfep->mxfe_cap_aneg; break; case ETHER_STAT_LINK_AUTONEG: *val = ((mxfep->mxfe_adv_aneg != 0) && ((mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) != 0)); break; case ETHER_STAT_ADV_CAP_100FDX: *val = mxfep->mxfe_adv_100fdx; break; case ETHER_STAT_ADV_CAP_100HDX: *val = mxfep->mxfe_adv_100hdx; break; case ETHER_STAT_ADV_CAP_10FDX: *val = mxfep->mxfe_adv_10fdx; break; case ETHER_STAT_ADV_CAP_10HDX: *val = mxfep->mxfe_adv_10hdx; break; case ETHER_STAT_ADV_CAP_AUTONEG: *val = mxfep->mxfe_adv_aneg; break; case ETHER_STAT_LP_CAP_100FDX: *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX_FD) ? 1 : 0; break; case ETHER_STAT_LP_CAP_100HDX: *val = (mxfep->mxfe_anlpar & MII_ABILITY_100BASE_TX) ? 1 : 0; break; case ETHER_STAT_LP_CAP_10FDX: *val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T_FD) ? 1 : 0; break; case ETHER_STAT_LP_CAP_10HDX: *val = (mxfep->mxfe_anlpar & MII_ABILITY_10BASE_T) ? 1 : 0; break; case ETHER_STAT_LP_CAP_AUTONEG: *val = (mxfep->mxfe_aner & MII_AN_EXP_LPCANAN) ? 1 : 0; break; case ETHER_STAT_XCVR_ADDR: *val = mxfep->mxfe_phyaddr; break; case ETHER_STAT_XCVR_ID: *val = mxfep->mxfe_phyid; break; case ETHER_STAT_XCVR_INUSE: *val = mxfep->mxfe_phyinuse; break; default: return (ENOTSUP); } return (0); } /*ARGSUSED*/ int mxfe_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz, void *val) { mxfe_t *mxfep = arg; int err = 0; switch (num) { case MAC_PROP_DUPLEX: ASSERT(sz >= sizeof (link_duplex_t)); bcopy(&mxfep->mxfe_duplex, val, sizeof (link_duplex_t)); break; case MAC_PROP_SPEED: ASSERT(sz >= sizeof (uint64_t)); bcopy(&mxfep->mxfe_ifspeed, val, sizeof (uint64_t)); break; case MAC_PROP_AUTONEG: *(uint8_t *)val = mxfep->mxfe_adv_aneg; break; case MAC_PROP_ADV_100FDX_CAP: case MAC_PROP_EN_100FDX_CAP: *(uint8_t *)val = mxfep->mxfe_adv_100fdx; break; case MAC_PROP_ADV_100HDX_CAP: case MAC_PROP_EN_100HDX_CAP: *(uint8_t *)val = mxfep->mxfe_adv_100hdx; break; case MAC_PROP_ADV_10FDX_CAP: case MAC_PROP_EN_10FDX_CAP: *(uint8_t *)val = mxfep->mxfe_adv_10fdx; break; case MAC_PROP_ADV_10HDX_CAP: case MAC_PROP_EN_10HDX_CAP: *(uint8_t *)val = mxfep->mxfe_adv_10hdx; break; case MAC_PROP_ADV_100T4_CAP: case MAC_PROP_EN_100T4_CAP: *(uint8_t *)val = mxfep->mxfe_adv_100T4; break; default: err = ENOTSUP; } return (err); } /*ARGSUSED*/ int mxfe_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz, const void *val) { mxfe_t *mxfep = arg; uint8_t *advp; uint8_t *capp; switch (num) { case MAC_PROP_EN_100FDX_CAP: advp = &mxfep->mxfe_adv_100fdx; capp = &mxfep->mxfe_cap_100fdx; break; case MAC_PROP_EN_100HDX_CAP: advp = &mxfep->mxfe_adv_100hdx; capp = &mxfep->mxfe_cap_100hdx; break; case MAC_PROP_EN_10FDX_CAP: advp = &mxfep->mxfe_adv_10fdx; capp = &mxfep->mxfe_cap_10fdx; break; case MAC_PROP_EN_10HDX_CAP: advp = &mxfep->mxfe_adv_10hdx; capp = &mxfep->mxfe_cap_10hdx; break; case MAC_PROP_EN_100T4_CAP: advp = &mxfep->mxfe_adv_100T4; capp = &mxfep->mxfe_cap_100T4; break; case MAC_PROP_AUTONEG: advp = &mxfep->mxfe_adv_aneg; capp = &mxfep->mxfe_cap_aneg; break; default: return (ENOTSUP); } if (*capp == 0) /* ensure phy can support value */ return (ENOTSUP); mutex_enter(&mxfep->mxfe_intrlock); mutex_enter(&mxfep->mxfe_xmtlock); if (*advp != *(const uint8_t *)val) { *advp = *(const uint8_t *)val; if ((mxfep->mxfe_flags & (MXFE_RUNNING|MXFE_SUSPENDED)) == MXFE_RUNNING) { /* * This re-initializes the phy, but it also * restarts transmit and receive rings. * Needless to say, changing the link * parameters is destructive to traffic in * progress. */ mxfe_resetall(mxfep); } } mutex_exit(&mxfep->mxfe_xmtlock); mutex_exit(&mxfep->mxfe_intrlock); return (0); } static void mxfe_m_propinfo(void *arg, const char *name, mac_prop_id_t num, mac_prop_info_handle_t mph) { mxfe_t *mxfep = arg; _NOTE(ARGUNUSED(name)); switch (num) { case MAC_PROP_DUPLEX: case MAC_PROP_SPEED: case MAC_PROP_ADV_100FDX_CAP: case MAC_PROP_ADV_100HDX_CAP: case MAC_PROP_ADV_10FDX_CAP: case MAC_PROP_ADV_10HDX_CAP: case MAC_PROP_ADV_100T4_CAP: mac_prop_info_set_perm(mph, MAC_PROP_PERM_READ); break; case MAC_PROP_AUTONEG: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_aneg); break; case MAC_PROP_EN_100FDX_CAP: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100fdx); break; case MAC_PROP_EN_100HDX_CAP: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100hdx); break; case MAC_PROP_EN_10FDX_CAP: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_10fdx); break; case MAC_PROP_EN_10HDX_CAP: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_10hdx); break; case MAC_PROP_EN_100T4_CAP: mac_prop_info_set_default_uint8(mph, mxfep->mxfe_cap_100T4); break; } } /* * Debugging and error reporting. */ void mxfe_error(dev_info_t *dip, char *fmt, ...) { va_list ap; char buf[256]; va_start(ap, fmt); (void) vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); if (dip) { cmn_err(CE_WARN, "%s%d: %s", ddi_driver_name(dip), ddi_get_instance(dip), buf); } else { cmn_err(CE_WARN, "mxfe: %s", buf); } } #ifdef DEBUG void mxfe_dprintf(mxfe_t *mxfep, const char *func, int level, char *fmt, ...) { va_list ap; va_start(ap, fmt); if (mxfe_debug & level) { char tag[64]; char buf[256]; if (mxfep && mxfep->mxfe_dip) { (void) snprintf(tag, sizeof (tag), "%s%d", ddi_driver_name(mxfep->mxfe_dip), ddi_get_instance(mxfep->mxfe_dip)); } else { (void) snprintf(tag, sizeof (tag), "mxfe"); } (void) snprintf(buf, sizeof (buf), "%s: %s: %s\n", tag, func, fmt); vcmn_err(CE_CONT, buf, ap); } va_end(ap); } #endif /* * Solaris driver for ethernet cards based on the Macronix 98715 * * Copyright (c) 2007 by Garrett D'Amore . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _MXFE_H #define _MXFE_H /* * These are conveniently defined to have the same values * as are used by the NDD utility, which is an undocumented * interface. YMMV. */ #define NDIOC ('N' << 8) #define NDIOC_GET (NDIOC|0) #define NDIOC_SET (NDIOC|1) /* * Registers and values are here, becuase they can be exported to userland * via the MXFEIOC_GETCSR and friends ioctls. These are private to this * driver and the bundled diagnostic utility, and should not be used by * end user application programs. */ /* * MXFE register definitions. */ /* PCI configuration registers */ #define PCI_VID 0x00 /* Loaded vendor ID */ #define PCI_DID 0x02 /* Loaded device ID */ #define PCI_CMD 0x04 /* Configuration command register */ #define PCI_STAT 0x06 /* Configuration status register */ #define PCI_RID 0x08 /* Revision ID */ #define PCI_CLS 0x0c /* Cache line size */ #define PCI_SVID 0x2c /* Subsystem vendor ID */ #define PCI_SSID 0x2e /* Subsystem ID */ #define PCI_MINGNT 0x3e /* Minimum Grant */ #define PCI_MAXLAT 0x3f /* Maximum latency */ /* * Bits for PCI command register. */ #define PCI_CMD_MWIE 0x0010 /* memory write-invalidate enable */ #define PCI_CMD_BME 0x0004 /* bus master enable */ #define PCI_CMD_MAE 0x0002 /* memory access enable */ #define PCI_CMD_IOE 0x0001 /* I/O access enable */ /* Ordinary control/status registers */ #define CSR_PAR 0x00 /* PCI access register */ #define CSR_TDR 0x08 /* Transmit demand register */ #define CSR_RDR 0x10 /* Receive demand register */ #define CSR_RDB 0x18 /* Receive descriptor base address */ #define CSR_TDB 0x20 /* Transmit descriptor base address */ #define CSR_SR 0x28 /* Status register */ #define CSR_NAR 0x30 /* Network access register */ #define CSR_IER 0x38 /* Interrupt enable register */ #define CSR_LPC 0x40 /* Lost packet counter */ #define CSR_SPR 0x48 /* Serial port register */ #define CSR_TIMER 0x58 /* Timer */ #define CSR_TSTAT 0x60 /* 10Base-T status */ #define CSR_SIA 0x68 /* SIA reset register */ #define CSR_TCTL 0x70 /* 10Base-T control */ #define CSR_WTMR 0x78 /* Watchdog timer */ #define CSR_MXMAGIC 0x80 /* MXIC magic register */ #define CSR_PMCSR 0x90 /* Power Management Command and Status */ #define CSR_TXBR 0x9c /* Transmit burst counter/time-out register */ #define CSR_FROM 0xa0 /* Flash(boot) ROM port */ #define CSR_ACOMP 0xa0 /* Autocompensation */ #define CSR_FLOW 0xa8 /* Flow control (newer parts only) */ /* * Bits for PCI access register. */ #define PAR_RESET 0x00000001U /* Reset the entire chip */ #define PAR_MWIE 0x01000000U /* PCI memory-write-invalidate */ #define PAR_MRLE 0x00800000U /* PCI memory-read-line */ #define PAR_MRME 0x00200000U /* PCI memory-read-multiple */ #define PAR_BAR 0x00000002U /* Bus arbitration */ #define PAR_DESCSKIP 0x0000007cU /* Descriptor skip length in DW */ #define PAR_BIGENDIAN 0x00000080U /* Use big endian data buffers */ #define PAR_TXAUTOPOLL 0x00060000U /* Programmable TX autopoll interval */ #define PAR_CALIGN_NONE 0x00000000U /* No cache alignment */ #define PAR_CALIGN_8 0x00004000U /* 8 DW cache alignment */ #define PAR_CALIGN_16 0x00008000U /* 16 DW cache alignment */ #define PAR_CALIGN_32 0x0000c000U /* 32 DW cache alignment */ #define PAR_BURSTLEN 0x00003F00U /* Programmable burst length */ #define PAR_BURSTUNL 0x00000000U /* Unlimited burst length */ #define PAR_BURST_1 0x00000100U /* 1 DW burst length */ #define PAR_BURST_2 0x00000200U /* 2 DW burst length */ #define PAR_BURST_4 0x00000400U /* 4 DW burst length */ #define PAR_BURST_8 0x00000800U /* 8 DW burst length */ #define PAR_BURST_16 0x00001000U /* 16 DW burst length */ #define PAR_BURST_32 0x00002000U /* 32 DW burst length */ /* * Bits for status register. Interrupt bits are also used by * the interrupt enable register. */ #define SR_BERR_TYPE 0x03800000U /* bus error type */ #define SR_BERR_PARITY 0x00000000U /* parity error */ #define SR_BERR_TARGET_ABORT 0x01000000U /* target abort */ #define SR_BERR_MASTER_ABORT 0x00800000U /* master abort */ #define SR_TX_STATE 0x00700000U /* transmit state */ #define SR_RX_STATE 0x000E0000U /* transmit state */ #define INT_100LINK 0x08000000U /* 100 Base-T link */ #define INT_NORMAL 0x00010000U /* normal interrupt */ #define INT_ABNORMAL 0x00008000U /* abnormal interrupt */ #define INT_EARLYRX 0x00004000U /* early receive interrupt */ #define INT_BUSERR 0x00002000U /* fatal bus error interrupt */ #define INT_10LINK 0x00001000U /* 10 Base-T link */ #define INT_TIMER 0x00000800U /* onboard timer interrupt */ #define INT_EARLYTX 0x00000400U /* early transmit interrupt */ #define INT_RXJABBER 0x00000200U /* receive watchdog timeout */ #define INT_RXSTOPPED 0x00000100U /* receive stopped */ #define INT_RXNOBUF 0x00000080U /* no rcv descriptor */ #define INT_RXOK 0x00000040U /* rcv complete interrupt */ #define INT_TXUNDERFLOW 0x00000020U /* transmit underflow */ #define INT_ANEG 0x00000010U /* autonegotiation */ #define INT_TXJABBER 0x00000008U /* transmit jabber timeout */ #define INT_TXNOBUF 0x00000004U /* no xmt descriptor */ #define INT_TXSTOPPED 0x00000002U /* transmit stopped */ #define INT_TXOK 0x00000001U /* transmit ok interrupt */ #define INT_NONE 0x00000000U /* no interrupts */ #define INT_WANTED (INT_BUSERR | INT_RXJABBER | \ INT_RXOK | INT_TXUNDERFLOW | \ INT_RXNOBUF | INT_TXJABBER | \ INT_RXSTOPPED | INT_TXSTOPPED | \ INT_TIMER | \ INT_ABNORMAL | INT_NORMAL) #define INT_LINKSTATUS (INT_ANEG | INT_100LINK | INT_10LINK) #define INT_ALL (INT_WANTED | INT_TXOK | \ INT_TXNOBUF | INT_LINKSTATUS) /* * Bits for network access register. */ #define NAR_TX_ENABLE 0x00002000U /* Enable transmit */ #define NAR_RX_MULTI 0x00000080U /* Receive all multicast packets */ #define NAR_RX_PROMISC 0x00000040U /* Receive any good packet */ #define NAR_RX_BAD 0x00000008U /* Pass bad packets */ #define NAR_RX_HO 0x00000004U /* Hash only receive */ #define NAR_RX_ENABLE 0x00000002U /* Enable receive */ #define NAR_RX_HP 0x00000001U /* Hash perfect receive */ #define NAR_TR 0x0000c000U /* Transmit threshold mask */ #define NAR_TR_72 0x00000000U /* 72 B (128 @ 100Mbps) tx thresh */ #define NAR_TR_96 0x00004000U /* 96 B (256 @ 100Mbps) tx thresh */ #define NAR_TR_128 0x00008000U /* 128 B (512 @ 100Mbps) tx thresh */ #define NAR_TR_160 0x0000c000U /* 160 B (1K @ 100Mbsp) tx thresh */ #define NAR_SCR 0x01000000U /* scrambler mode */ #define NAR_PCS 0x00800000U /* set for forced 100 mbit */ #define NAR_SPEED 0x00400000U /* transmit threshold, set for 10bt */ #define NAR_SF 0x00200000U /* store and forward */ #define NAR_HBD 0x00080000U /* Disable SQE heartbeat */ #define NAR_COE 0x00020000U /* collision offset enable */ #define NAR_PORTSEL 0x00040000U /* 1 = 100 mbit */ #define NAR_FDX 0x00000200U /* 1 = full duplex */ /* * Bits for lost packet counter. */ #define LPC_COUNT 0x0000FFFFU /* Count of missed frames */ #define LPC_OFLOW 0x00010000U /* Counter overflow bit */ /* * Bits for CSR_SPR (MII and SROM access) */ #define SPR_MII_DIN 0x00080000U /* MII data input */ #define SPR_MII_CTRL 0x00040000U /* MII management control, 1=read */ #define SPR_MII_DOUT 0x00020000U /* MII data output */ #define SPR_MII_CLOCK 0x00010000U /* MII data clock */ #define SPR_SROM_READ 0x00004000U /* Serial EEPROM read control */ #define SPR_SROM_WRITE 0x00002000U /* Serial EEPROM write control */ #define SPR_SROM_SEL 0x00000800U /* Serial EEPROM select */ #define SPR_SROM_DOUT 0x00000008U /* Serial EEPROM data out */ #define SPR_SROM_DIN 0x00000004U /* Serial EEPROM data in */ #define SPR_SROM_CLOCK 0x00000002U /* Serial EEPROM clock */ #define SPR_SROM_CHIP 0x00000001U /* Serial EEPROM chip select */ #define SROM_ENADDR 0x70 /* Ethernet address pointer! */ #define SROM_READCMD 0x6 /* command to read SROM */ /* * Bits for CSR_TIMER */ #define TIMER_LOOP 0x00010000U /* continuous operating mode */ #define TIMER_USEC 204 /* usecs per timer count */ /* * Bits for TSTAT */ #define TSTAT_LPC 0xFFFF0000U /* link partner's code word */ #define TSTAT_LPN 0x00008000U /* link partner supports nway */ #define TSTAT_ANS 0x00007000U /* autonegotiation state mask */ #define TSTAT_TRF 0x00000800U /* transmit remote fault */ #define TSTAT_APS 0x00000008U /* autopolarity state */ #define TSTAT_10F 0x00000004U /* 10Base-T link failure */ #define TSTAT_100F 0x00000002U /* 100Base-T link failure */ #define TSTAT_ANS_DIS 0x00000000U /* autonegotiation disabled */ #define TSTAT_ANS_OK 0x00005000U /* autonegotiation complete */ #define TSTAT_ANS_START 0x00001000U /* restart autonegotiation */ /* macro to convert TSTAT link partner's code word to MII equivalents */ #define TSTAT_LPAR(x) ((x & TSTAT_LPC) >> 16) /* * Bits for SIA reset */ #define SIA_RESET 0x00000001U /* reset 100 PHY */ #define SIA_NRESET 0x00000002U /* reset NWay */ /* * Bits for TCTL */ #define TCTL_PAUSE 0x00080000U /* Pause enable */ #define TCTL_100BT4 0x00040000U /* 100 BaseT4 enable */ #define TCTL_100FDX 0x00020000U /* 100 BaseT fdx enable */ #define TCTL_100HDX 0x00010000U /* 100 BaseT hdx enable */ #define TCTL_LTE 0x00001000U /* link test enable */ #define TCTL_RSQ 0x00000100U /* receive squelch enable */ #define TCTL_ANE 0x00000080U /* autoneg. enable */ #define TCTL_HDX 0x00000040U /* half-duplex enable */ #define TCTL_PWR 0x00000004U /* supply power to 10BaseT */ /* * Bits for flow control */ #define FLOW_TMVAL 0xffff0000U /* flow timer value */ #define FLOW_TEST 0x00008000U /* test flow control timer */ #define FLOW_RESTART 0x00004000U /* re-start mode */ #define FLOW_RESTOP 0x00002000U /* re-stop mode */ #define FLOW_TXFCEN 0x00001000U /* tx flow control enable */ #define FLOW_RXFCEN 0x00000800U /* rx flow control enable */ #define FLOW_RUFCEN 0x00000400U /* send pause when rxnobuf */ #define FLOW_STOPTX 0x00000200U /* tx flow status */ #define FLOW_REJECTFC 0x00000100U /* abort rx flow when set */ #define FLOW_RXFCTH1 0x00000080U /* rx flow threshold 1 */ #define FLOW_RXFCTH0 0x00000040U /* rx flow threshold 0 */ #define FLOW_NFCEN 0x00000020U /* accept nway flow control */ #endif /* _MXFE_H */ /* * Solaris driver for ethernet cards based on the Macronix 98715 * * Copyright (c) 2007 by Garrett D'Amore . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MXFEIMPL_H #define _MXFEIMPL_H /* * This entire file is private to the MXFE driver. */ #ifdef _KERNEL #include /* * Compile time tunables. */ #define MXFE_TXRING 128 /* number of xmt buffers */ #define MXFE_RXRING 256 /* number of rcv buffers */ #define MXFE_TXRECLAIM 32 /* when to reclaim tx buffers (txavail) */ #define MXFE_TXRESCHED 120 /* when to resched (txavail) */ #define MXFE_LINKTIMER 5000 /* how often we check link state (msec) */ #define MXFE_HEADROOM 34 /* headroom in packet (should be 2 modulo 4) */ /* * Constants, do not change. The bufsize is setup to make sure it comes * in at a whole number of cache lines, even for 32-long-word aligned * caches. */ #define MXFE_BUFSZ (1664) /* big enough for a vlan frame */ #define MXFE_SETUP_LEN 192 /* size of a setup frame */ typedef struct mxfe mxfe_t; typedef struct mxfe_card mxfe_card_t; typedef struct mxfe_rxbuf mxfe_rxbuf_t; typedef struct mxfe_txbuf mxfe_txbuf_t; typedef struct mxfe_desc mxfe_desc_t; struct mxfe_card { uint16_t card_venid; /* PCI vendor id */ uint16_t card_devid; /* PCI device id */ uint16_t card_revid; /* PCI revision id */ uint16_t card_revmask; char *card_cardname; /* Description of the card */ unsigned card_model; /* Card specific flags */ }; /* * Device instance structure, one per PCI card. */ struct mxfe { dev_info_t *mxfe_dip; mac_handle_t mxfe_mh; mxfe_card_t *mxfe_cardp; ushort_t mxfe_cachesize; ushort_t mxfe_sromwidth; int mxfe_flags; kmutex_t mxfe_xmtlock; kmutex_t mxfe_intrlock; ddi_iblock_cookie_t mxfe_icookie; /* * Register access. */ uint32_t *mxfe_regs; ddi_acc_handle_t mxfe_regshandle; /* * Receive descriptors. */ int mxfe_rxhead; struct mxfe_desc *mxfe_rxdescp; ddi_dma_handle_t mxfe_rxdesc_dmah; ddi_acc_handle_t mxfe_rxdesc_acch; uint32_t mxfe_rxdesc_paddr; struct mxfe_rxbuf **mxfe_rxbufs; /* * Transmit descriptors. */ int mxfe_txreclaim; int mxfe_txsend; int mxfe_txavail; struct mxfe_desc *mxfe_txdescp; ddi_dma_handle_t mxfe_txdesc_dmah; ddi_acc_handle_t mxfe_txdesc_acch; uint32_t mxfe_txdesc_paddr; struct mxfe_txbuf **mxfe_txbufs; hrtime_t mxfe_txstall_time; boolean_t mxfe_wantw; /* * Address management. */ uchar_t mxfe_curraddr[ETHERADDRL]; boolean_t mxfe_promisc; /* * Link state. */ int mxfe_nwaystate; uint64_t mxfe_lastifspeed; link_duplex_t mxfe_lastduplex; link_state_t mxfe_lastlinkup; link_state_t mxfe_linkup; link_duplex_t mxfe_duplex; uint64_t mxfe_ifspeed; boolean_t mxfe_resetting; /* no link warning */ /* * Transceiver stuff. */ int mxfe_phyaddr; int mxfe_phyid; int mxfe_phyinuse; uint8_t mxfe_adv_aneg; uint8_t mxfe_adv_100T4; uint8_t mxfe_adv_100fdx; uint8_t mxfe_adv_100hdx; uint8_t mxfe_adv_10fdx; uint8_t mxfe_adv_10hdx; uint8_t mxfe_cap_aneg; uint8_t mxfe_cap_100T4; uint8_t mxfe_cap_100fdx; uint8_t mxfe_cap_100hdx; uint8_t mxfe_cap_10fdx; uint8_t mxfe_cap_10hdx; int mxfe_forcephy; uint16_t mxfe_bmsr; uint16_t mxfe_anlpar; uint16_t mxfe_aner; /* * Kstats. */ kstat_t *mxfe_intrstat; uint64_t mxfe_ipackets; uint64_t mxfe_opackets; uint64_t mxfe_rbytes; uint64_t mxfe_obytes; uint64_t mxfe_brdcstrcv; uint64_t mxfe_multircv; uint64_t mxfe_brdcstxmt; uint64_t mxfe_multixmt; unsigned mxfe_norcvbuf; unsigned mxfe_noxmtbuf; unsigned mxfe_errrcv; unsigned mxfe_errxmt; unsigned mxfe_missed; unsigned mxfe_underflow; unsigned mxfe_overflow; unsigned mxfe_align_errors; unsigned mxfe_fcs_errors; unsigned mxfe_carrier_errors; unsigned mxfe_collisions; unsigned mxfe_ex_collisions; unsigned mxfe_tx_late_collisions; unsigned mxfe_defer_xmts; unsigned mxfe_first_collisions; unsigned mxfe_multi_collisions; unsigned mxfe_sqe_errors; unsigned mxfe_macxmt_errors; unsigned mxfe_macrcv_errors; unsigned mxfe_toolong_errors; unsigned mxfe_runt; unsigned mxfe_jabber; }; struct mxfe_rxbuf { caddr_t rxb_buf; ddi_dma_handle_t rxb_dmah; ddi_acc_handle_t rxb_acch; uint32_t rxb_paddr; }; struct mxfe_txbuf { /* bcopy version of tx */ caddr_t txb_buf; uint32_t txb_paddr; ddi_dma_handle_t txb_dmah; ddi_acc_handle_t txb_acch; }; /* * Descriptor. We use rings rather than chains. */ struct mxfe_desc { unsigned desc_status; unsigned desc_control; unsigned desc_buffer1; unsigned desc_buffer2; }; #define PUTTXDESC(mxfep, member, val) \ ddi_put32(mxfep->mxfe_txdesc_acch, &member, val) #define PUTRXDESC(mxfep, member, val) \ ddi_put32(mxfep->mxfe_rxdesc_acch, &member, val) #define GETTXDESC(mxfep, member) \ ddi_get32(mxfep->mxfe_txdesc_acch, &member) #define GETRXDESC(mxfep, member) \ ddi_get32(mxfep->mxfe_rxdesc_acch, &member) /* * Receive descriptor fields. */ #define RXSTAT_OWN 0x80000000U /* ownership */ #define RXSTAT_RXLEN 0x3FFF0000U /* frame length, incl. crc */ #define RXSTAT_RXERR 0x00008000U /* error summary */ #define RXSTAT_DESCERR 0x00004000U /* descriptor error */ #define RXSTAT_RXTYPE 0x00003000U /* data type */ #define RXSTAT_RUNT 0x00000800U /* runt frame */ #define RXSTAT_GROUP 0x00000400U /* multicast/brdcast frame */ #define RXSTAT_FIRST 0x00000200U /* first descriptor */ #define RXSTAT_LAST 0x00000100U /* last descriptor */ #define RXSTAT_TOOLONG 0x00000080U /* frame too long */ #define RXSTAT_COLLSEEN 0x00000040U /* late collision seen */ #define RXSTAT_FRTYPE 0x00000020U /* frame type */ #define RXSTAT_WATCHDOG 0x00000010U /* receive watchdog */ #define RXSTAT_DRIBBLE 0x00000004U /* dribbling bit */ #define RXSTAT_CRCERR 0x00000002U /* crc error */ #define RXSTAT_OFLOW 0x00000001U /* fifo overflow */ #define RXSTAT_ERRS (RXSTAT_DESCERR | RXSTAT_RUNT | \ RXSTAT_COLLSEEN | RXSTAT_DRIBBLE | \ RXSTAT_CRCERR | RXSTAT_OFLOW) #define RXLENGTH(x) ((x & RXSTAT_RXLEN) >> 16) #define RXCTL_ENDRING 0x02000000U /* end of ring */ #define RXCTL_CHAIN 0x01000000U /* chained descriptors */ #define RXCTL_BUFLEN2 0x003FF800U /* buffer 2 length */ #define RXCTL_BUFLEN1 0x000007FFU /* buffer 1 length */ /* * Transmit descriptor fields. */ #define TXSTAT_OWN 0x80000000U /* ownership */ #define TXSTAT_URCNT 0x00C00000U /* underrun count */ #define TXSTAT_TXERR 0x00008000U /* error summary */ #define TXSTAT_JABBER 0x00004000U /* jabber timeout */ #define TXSTAT_CARRLOST 0x00000800U /* lost carrier */ #define TXSTAT_NOCARR 0x00000400U /* no carrier */ #define TXSTAT_LATECOL 0x00000200U /* late collision */ #define TXSTAT_EXCOLL 0x00000100U /* excessive collisions */ #define TXSTAT_SQE 0x00000080U /* heartbeat failure */ #define TXSTAT_COLLCNT 0x00000078U /* collision count */ #define TXSTAT_UFLOW 0x00000002U /* underflow */ #define TXSTAT_DEFER 0x00000001U /* deferred */ #define TXCOLLCNT(x) ((x & TXSTAT_COLLCNT) >> 3) #define TXUFLOWCNT(x) ((x & TXSTAT_URCNT) >> 22) #define TXCTL_INTCMPLTE 0x80000000U /* interrupt completed */ #define TXCTL_LAST 0x40000000U /* last descriptor */ #define TXCTL_FIRST 0x20000000U /* first descriptor */ #define TXCTL_NOCRC 0x04000000U /* disable crc */ #define TXCTL_SETUP 0x08000000U /* setup frame */ #define TXCTL_ENDRING 0x02000000U /* end of ring */ #define TXCTL_CHAIN 0x01000000U /* chained descriptors */ #define TXCTL_NOPAD 0x00800000U /* disable padding */ #define TXCTL_HASHPERF 0x00400000U /* hash perfect mode */ #define TXCTL_BUFLEN2 0x003FF800U /* buffer length 2 */ #define TXCTL_BUFLEN1 0x000007FFU /* buffer length 1 */ /* * Interface flags. */ #define MXFE_RUNNING 0x1 /* chip is initialized */ #define MXFE_SUSPENDED 0x2 /* interface is suspended */ #define MXFE_SYMBOL 0x8 /* use symbol mode */ /* * Link flags... */ #define MXFE_NOLINK 0x0 /* initial link state, no timer */ #define MXFE_NWAYCHECK 0x2 /* checking for NWay support */ #define MXFE_NWAYRENEG 0x3 /* renegotiating NWay mode */ #define MXFE_GOODLINK 0x4 /* detected link is good */ /* * Card models. */ #define MXFE_MODEL(mxfep) ((mxfep)->mxfe_cardp->card_model) #define MXFE_98715 0x1 #define MXFE_98715A 0x2 #define MXFE_98715AEC 0x3 #define MXFE_98715B 0x4 #define MXFE_98725 0x5 #define MXFE_98713 0x6 #define MXFE_98713A 0x7 #define MXFE_PNICII 0x8 /* * Register definitions located in mxfe.h exported header file. */ /* * Macros to simplify hardware access. Note that the reg/4 is used to * help with pointer arithmetic. */ #define GETCSR(mxfep, reg) \ ddi_get32(mxfep->mxfe_regshandle, mxfep->mxfe_regs + (reg/4)) #define PUTCSR(mxfep, reg, val) \ ddi_put32(mxfep->mxfe_regshandle, mxfep->mxfe_regs + (reg/4), val) #define SETBIT(mxfep, reg, val) \ PUTCSR(mxfep, reg, GETCSR(mxfep, reg) | (val)) #define CLRBIT(mxfep, reg, val) \ PUTCSR(mxfep, reg, GETCSR(mxfep, reg) & ~(val)) #define SYNCTXDESC(mxfep, index, who) \ (void) ddi_dma_sync(mxfep->mxfe_txdesc_dmah, \ (index * sizeof (mxfe_desc_t)), sizeof (mxfe_desc_t), who) #define SYNCTXBUF(txb, len, who) \ (void) (ddi_dma_sync(txb->txb_dmah, 0, len, who)) #define SYNCRXDESC(mxfep, index, who) \ (void) ddi_dma_sync(mxfep->mxfe_rxdesc_dmah, \ (index * sizeof (mxfe_desc_t)), sizeof (mxfe_desc_t), who) #define SYNCRXBUF(rxb, len, who) \ (void) (ddi_dma_sync(rxb->rxb_dmah, 0, len, who)) /* * Debugging flags. */ #define DWARN 0x0001 #define DINTR 0x0002 #define DWSRV 0x0004 #define DMACID 0x0008 #define DDLPI 0x0010 #define DPHY 0x0020 #define DPCI 0x0040 #define DCHATTY 0x0080 #define DDMA 0x0100 #define DLINK 0x0200 #define DSROM 0x0400 #define DRECV 0x0800 #define DXMIT 0x1000 #ifdef DEBUG #define DBG(lvl, ...) mxfe_dprintf(mxfep, __func__, lvl, __VA_ARGS__); #else #define DBG(lvl, ...) #endif #endif /* _KERNEL */ #endif /* _MXFEIMPL_H */