182743679SGarrett D'Amore /* 282743679SGarrett D'Amore * This file and its contents are supplied under the terms of the 382743679SGarrett D'Amore * Common Development and Distribution License ("CDDL"), version 1.0. 482743679SGarrett D'Amore * You may only use this file in accordance with the terms of version 582743679SGarrett D'Amore * 1.0 of the CDDL. 682743679SGarrett D'Amore * 782743679SGarrett D'Amore * A full copy of the text of the CDDL should have accompanied this 882743679SGarrett D'Amore * source. A copy of the CDDL is also available via the Internet at 982743679SGarrett D'Amore * http://www.illumos.org/license/CDDL. 1082743679SGarrett D'Amore */ 1182743679SGarrett D'Amore 1282743679SGarrett D'Amore /* 13197c9523SMarcel Telka * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 1482743679SGarrett D'Amore */ 1582743679SGarrett D'Amore 1682743679SGarrett D'Amore /* 1782743679SGarrett D'Amore * Intel Pro/100B Ethernet Driver 1882743679SGarrett D'Amore */ 1982743679SGarrett D'Amore 2082743679SGarrett D'Amore #include <sys/types.h> 2182743679SGarrett D'Amore #include <sys/modctl.h> 2282743679SGarrett D'Amore #include <sys/conf.h> 2382743679SGarrett D'Amore #include <sys/kmem.h> 2482743679SGarrett D'Amore #include <sys/ksynch.h> 2582743679SGarrett D'Amore #include <sys/cmn_err.h> 2682743679SGarrett D'Amore #include <sys/note.h> 2782743679SGarrett D'Amore #include <sys/pci.h> 2882743679SGarrett D'Amore #include <sys/pci_cap.h> 2982743679SGarrett D'Amore #include <sys/ethernet.h> 3082743679SGarrett D'Amore #include <sys/mii.h> 3182743679SGarrett D'Amore #include <sys/miiregs.h> 3282743679SGarrett D'Amore #include <sys/mac.h> 3382743679SGarrett D'Amore #include <sys/mac_ether.h> 3482743679SGarrett D'Amore #include <sys/ethernet.h> 3582743679SGarrett D'Amore #include <sys/vlan.h> 3682743679SGarrett D'Amore #include <sys/list.h> 3782743679SGarrett D'Amore #include <sys/sysmacros.h> 3882743679SGarrett D'Amore #include <sys/varargs.h> 3982743679SGarrett D'Amore #include <sys/stream.h> 4082743679SGarrett D'Amore #include <sys/strsun.h> 4182743679SGarrett D'Amore #include <sys/ddi.h> 4282743679SGarrett D'Amore #include <sys/sunddi.h> 4382743679SGarrett D'Amore 4482743679SGarrett D'Amore #include "iprb.h" 4582743679SGarrett D'Amore #include "rcvbundl.h" 4682743679SGarrett D'Amore 4782743679SGarrett D'Amore /* 4882743679SGarrett D'Amore * Intel has openly documented the programming interface for these 4982743679SGarrett D'Amore * parts in the "Intel 8255x 10/100 Mbps Ethernet Controller Family 5082743679SGarrett D'Amore * Open Source Software Developer Manual". 5182743679SGarrett D'Amore * 5282743679SGarrett D'Amore * While some open source systems have utilized many of the features 5382743679SGarrett D'Amore * of some models in this family (especially scatter gather and IP 5482743679SGarrett D'Amore * checksum support), we have elected to offer only the basic 5582743679SGarrett D'Amore * functionality. These are only 10/100 parts, and the additional 5682743679SGarrett D'Amore * complexity is not justified by the minimal performance benefit. 5782743679SGarrett D'Amore * KISS. So, we are only supporting the simple 82557 features. 5882743679SGarrett D'Amore */ 5982743679SGarrett D'Amore 6082743679SGarrett D'Amore static uint16_t iprb_mii_read(void *, uint8_t, uint8_t); 6182743679SGarrett D'Amore static void iprb_mii_write(void *, uint8_t, uint8_t, uint16_t); 6282743679SGarrett D'Amore static void iprb_mii_notify(void *, link_state_t); 6382743679SGarrett D'Amore static int iprb_attach(dev_info_t *); 6482743679SGarrett D'Amore static int iprb_detach(dev_info_t *); 6582743679SGarrett D'Amore static int iprb_quiesce(dev_info_t *); 6682743679SGarrett D'Amore static int iprb_suspend(dev_info_t *); 6782743679SGarrett D'Amore static int iprb_resume(dev_info_t *); 6882743679SGarrett D'Amore static int iprb_m_stat(void *, uint_t, uint64_t *); 6982743679SGarrett D'Amore static int iprb_m_start(void *); 7082743679SGarrett D'Amore static void iprb_m_stop(void *); 7182743679SGarrett D'Amore static int iprb_m_promisc(void *, boolean_t); 7282743679SGarrett D'Amore static int iprb_m_multicst(void *, boolean_t, const uint8_t *); 7382743679SGarrett D'Amore static int iprb_m_unicst(void *, const uint8_t *); 7482743679SGarrett D'Amore static mblk_t *iprb_m_tx(void *, mblk_t *); 7582743679SGarrett D'Amore static void iprb_m_ioctl(void *, queue_t *, mblk_t *); 7682743679SGarrett D'Amore static int iprb_m_setprop(void *, const char *, mac_prop_id_t, uint_t, 7782743679SGarrett D'Amore const void *); 7882743679SGarrett D'Amore static int iprb_m_getprop(void *, const char *, mac_prop_id_t, uint_t, 7982743679SGarrett D'Amore void *); 8082743679SGarrett D'Amore static void iprb_m_propinfo(void *, const char *, mac_prop_id_t, 8182743679SGarrett D'Amore mac_prop_info_handle_t); 8282743679SGarrett D'Amore static void iprb_destroy(iprb_t *); 8382743679SGarrett D'Amore static int iprb_configure(iprb_t *); 8482743679SGarrett D'Amore static void iprb_eeprom_sendbits(iprb_t *, uint32_t, uint8_t); 8582743679SGarrett D'Amore static uint16_t iprb_eeprom_read(iprb_t *, uint16_t); 8682743679SGarrett D'Amore static void iprb_identify(iprb_t *); 8782743679SGarrett D'Amore static int iprb_cmd_submit(iprb_t *, uint16_t); 8882743679SGarrett D'Amore static void iprb_cmd_reclaim(iprb_t *); 8982743679SGarrett D'Amore static int iprb_cmd_ready(iprb_t *); 9082743679SGarrett D'Amore static int iprb_cmd_drain(iprb_t *); 9182743679SGarrett D'Amore static void iprb_rx_add(iprb_t *); 9282743679SGarrett D'Amore static void iprb_rx_init(iprb_t *); 9382743679SGarrett D'Amore static mblk_t *iprb_rx(iprb_t *); 9482743679SGarrett D'Amore static mblk_t *iprb_send(iprb_t *, mblk_t *); 9582743679SGarrett D'Amore static uint_t iprb_intr(caddr_t, caddr_t); 9682743679SGarrett D'Amore static void iprb_periodic(void *); 9782743679SGarrett D'Amore static int iprb_add_intr(iprb_t *); 9882743679SGarrett D'Amore static int iprb_dma_alloc(iprb_t *, iprb_dma_t *, size_t); 9982743679SGarrett D'Amore static void iprb_dma_free(iprb_dma_t *); 10082743679SGarrett D'Amore static iprb_dma_t *iprb_cmd_next(iprb_t *); 10182743679SGarrett D'Amore static int iprb_set_config(iprb_t *); 10282743679SGarrett D'Amore static int iprb_set_unicast(iprb_t *); 10382743679SGarrett D'Amore static int iprb_set_multicast(iprb_t *); 10482743679SGarrett D'Amore static int iprb_set_ucode(iprb_t *); 10582743679SGarrett D'Amore static void iprb_update_stats(iprb_t *); 10682743679SGarrett D'Amore static int iprb_start(iprb_t *); 10782743679SGarrett D'Amore static void iprb_stop(iprb_t *); 10882743679SGarrett D'Amore static int iprb_ddi_attach(dev_info_t *, ddi_attach_cmd_t); 10982743679SGarrett D'Amore static int iprb_ddi_detach(dev_info_t *, ddi_detach_cmd_t); 11082743679SGarrett D'Amore static void iprb_error(iprb_t *, const char *, ...); 11182743679SGarrett D'Amore 11282743679SGarrett D'Amore static mii_ops_t iprb_mii_ops = { 11382743679SGarrett D'Amore MII_OPS_VERSION, 11482743679SGarrett D'Amore iprb_mii_read, 11582743679SGarrett D'Amore iprb_mii_write, 11682743679SGarrett D'Amore iprb_mii_notify, 11782743679SGarrett D'Amore NULL, /* reset */ 11882743679SGarrett D'Amore }; 11982743679SGarrett D'Amore 12082743679SGarrett D'Amore static mac_callbacks_t iprb_m_callbacks = { 12182743679SGarrett D'Amore MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 12282743679SGarrett D'Amore iprb_m_stat, 12382743679SGarrett D'Amore iprb_m_start, 12482743679SGarrett D'Amore iprb_m_stop, 12582743679SGarrett D'Amore iprb_m_promisc, 12682743679SGarrett D'Amore iprb_m_multicst, 12782743679SGarrett D'Amore iprb_m_unicst, 12882743679SGarrett D'Amore iprb_m_tx, 12982743679SGarrett D'Amore NULL, 13082743679SGarrett D'Amore iprb_m_ioctl, /* mc_ioctl */ 13182743679SGarrett D'Amore NULL, /* mc_getcapab */ 13282743679SGarrett D'Amore NULL, /* mc_open */ 13382743679SGarrett D'Amore NULL, /* mc_close */ 13482743679SGarrett D'Amore iprb_m_setprop, 13582743679SGarrett D'Amore iprb_m_getprop, 13682743679SGarrett D'Amore iprb_m_propinfo 13782743679SGarrett D'Amore }; 13882743679SGarrett D'Amore 13982743679SGarrett D'Amore 14082743679SGarrett D'Amore /* 14182743679SGarrett D'Amore * Stream information 14282743679SGarrett D'Amore */ 14382743679SGarrett D'Amore DDI_DEFINE_STREAM_OPS(iprb_devops, nulldev, nulldev, 14482743679SGarrett D'Amore iprb_ddi_attach, iprb_ddi_detach, nodev, NULL, D_MP, NULL, iprb_quiesce); 14582743679SGarrett D'Amore 14682743679SGarrett D'Amore static struct modldrv iprb_modldrv = { 14782743679SGarrett D'Amore &mod_driverops, /* drv_modops */ 14882743679SGarrett D'Amore "Intel 8255x Ethernet", /* drv_linkinfo */ 14982743679SGarrett D'Amore &iprb_devops /* drv_dev_ops */ 15082743679SGarrett D'Amore }; 15182743679SGarrett D'Amore 15282743679SGarrett D'Amore static struct modlinkage iprb_modlinkage = { 15382743679SGarrett D'Amore MODREV_1, /* ml_rev */ 15482743679SGarrett D'Amore { &iprb_modldrv, NULL } /* ml_linkage */ 15582743679SGarrett D'Amore }; 15682743679SGarrett D'Amore 15782743679SGarrett D'Amore 15882743679SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = { 15982743679SGarrett D'Amore DDI_DEVICE_ATTR_V0, 16082743679SGarrett D'Amore DDI_STRUCTURE_LE_ACC, 16182743679SGarrett D'Amore DDI_STRICTORDER_ACC 16282743679SGarrett D'Amore }; 16382743679SGarrett D'Amore 16482743679SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = { 16582743679SGarrett D'Amore DDI_DEVICE_ATTR_V0, 16682743679SGarrett D'Amore DDI_NEVERSWAP_ACC, 16782743679SGarrett D'Amore DDI_STORECACHING_OK_ACC 16882743679SGarrett D'Amore }; 16982743679SGarrett D'Amore 17082743679SGarrett D'Amore /* 17182743679SGarrett D'Amore * The 8225x is a 32-bit addressing engine, but it can only address up 17282743679SGarrett D'Amore * to 31 bits on a single transaction. (Far less in reality it turns 17382743679SGarrett D'Amore * out.) Statistics buffers have to be 16-byte aligned, and as we 17482743679SGarrett D'Amore * allocate individual data pieces for other things, there is no 17582743679SGarrett D'Amore * compelling reason to use another attribute with support for less 17682743679SGarrett D'Amore * strict alignment. 17782743679SGarrett D'Amore */ 17882743679SGarrett D'Amore static ddi_dma_attr_t dma_attr = { 17982743679SGarrett D'Amore DMA_ATTR_V0, /* dma_attr_version */ 18082743679SGarrett D'Amore 0, /* dma_attr_addr_lo */ 18182743679SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_addr_hi */ 18282743679SGarrett D'Amore 0x7FFFFFFFU, /* dma_attr_count_max */ 18382743679SGarrett D'Amore 16, /* dma_attr_align */ 18482743679SGarrett D'Amore 0x100, /* dma_attr_burstsizes */ 18582743679SGarrett D'Amore 1, /* dma_attr_minxfer */ 18682743679SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_maxxfer */ 18782743679SGarrett D'Amore 0xFFFFFFFFU, /* dma_attr_seg */ 18882743679SGarrett D'Amore 1, /* dma_attr_sgllen */ 18982743679SGarrett D'Amore 1, /* dma_attr_granular */ 19082743679SGarrett D'Amore 0 /* dma_attr_flags */ 19182743679SGarrett D'Amore }; 19282743679SGarrett D'Amore 19382743679SGarrett D'Amore #define DECL_UCODE(x) \ 19482743679SGarrett D'Amore static const uint32_t x ## _WORDS[] = x ## _RCVBUNDLE_UCODE 19582743679SGarrett D'Amore DECL_UCODE(D101_A); 19682743679SGarrett D'Amore DECL_UCODE(D101_B0); 19782743679SGarrett D'Amore DECL_UCODE(D101M_B); 19882743679SGarrett D'Amore DECL_UCODE(D101S); 19982743679SGarrett D'Amore DECL_UCODE(D102_B); 20082743679SGarrett D'Amore DECL_UCODE(D102_C); 20182743679SGarrett D'Amore DECL_UCODE(D102_E); 20282743679SGarrett D'Amore 20382743679SGarrett D'Amore static uint8_t iprb_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 20482743679SGarrett D'Amore 20582743679SGarrett D'Amore /* 20682743679SGarrett D'Amore * We don't bother allowing for tuning of the CPU saver algorithm. 20782743679SGarrett D'Amore * The ucode has reasonable defaults built-in. However, some variants 20882743679SGarrett D'Amore * apparently have bug fixes delivered via this ucode, so we still 20982743679SGarrett D'Amore * need to support the ucode upload. 21082743679SGarrett D'Amore */ 21182743679SGarrett D'Amore typedef struct { 21282743679SGarrett D'Amore uint8_t rev; 21382743679SGarrett D'Amore uint8_t length; 21482743679SGarrett D'Amore const uint32_t *ucode; 21582743679SGarrett D'Amore } iprb_ucode_t; 21682743679SGarrett D'Amore 21782743679SGarrett D'Amore #define UCODE(x) \ 21882743679SGarrett D'Amore sizeof (x ## _WORDS) / sizeof (uint32_t), x ## _WORDS 21982743679SGarrett D'Amore 22082743679SGarrett D'Amore static const iprb_ucode_t iprb_ucode[] = { 22182743679SGarrett D'Amore { REV_82558_A4, UCODE(D101_A) }, 22282743679SGarrett D'Amore { REV_82558_B0, UCODE(D101_B0) }, 22382743679SGarrett D'Amore { REV_82559_A0, UCODE(D101M_B) }, 22482743679SGarrett D'Amore { REV_82559S_A, UCODE(D101S) }, 22582743679SGarrett D'Amore { REV_82550, UCODE(D102_B) }, 22682743679SGarrett D'Amore { REV_82550_C, UCODE(D102_C) }, 22782743679SGarrett D'Amore { REV_82551_F, UCODE(D102_E) }, 22882743679SGarrett D'Amore { 0 }, 22982743679SGarrett D'Amore }; 23082743679SGarrett D'Amore 23182743679SGarrett D'Amore int 23282743679SGarrett D'Amore _init(void) 23382743679SGarrett D'Amore { 23482743679SGarrett D'Amore int rv; 23582743679SGarrett D'Amore mac_init_ops(&iprb_devops, "iprb"); 23682743679SGarrett D'Amore if ((rv = mod_install(&iprb_modlinkage)) != DDI_SUCCESS) { 23782743679SGarrett D'Amore mac_fini_ops(&iprb_devops); 23882743679SGarrett D'Amore } 23982743679SGarrett D'Amore return (rv); 24082743679SGarrett D'Amore } 24182743679SGarrett D'Amore 24282743679SGarrett D'Amore int 24382743679SGarrett D'Amore _fini(void) 24482743679SGarrett D'Amore { 24582743679SGarrett D'Amore int rv; 24682743679SGarrett D'Amore if ((rv = mod_remove(&iprb_modlinkage)) == DDI_SUCCESS) { 24782743679SGarrett D'Amore mac_fini_ops(&iprb_devops); 24882743679SGarrett D'Amore } 24982743679SGarrett D'Amore return (rv); 25082743679SGarrett D'Amore } 25182743679SGarrett D'Amore 25282743679SGarrett D'Amore int 25382743679SGarrett D'Amore _info(struct modinfo *modinfop) 25482743679SGarrett D'Amore { 25582743679SGarrett D'Amore return (mod_info(&iprb_modlinkage, modinfop)); 25682743679SGarrett D'Amore } 25782743679SGarrett D'Amore 25882743679SGarrett D'Amore int 25982743679SGarrett D'Amore iprb_attach(dev_info_t *dip) 26082743679SGarrett D'Amore { 26182743679SGarrett D'Amore iprb_t *ip; 26282743679SGarrett D'Amore uint16_t w; 26382743679SGarrett D'Amore int i; 26482743679SGarrett D'Amore mac_register_t *macp; 26582743679SGarrett D'Amore 26682743679SGarrett D'Amore ip = kmem_zalloc(sizeof (*ip), KM_SLEEP); 26782743679SGarrett D'Amore ddi_set_driver_private(dip, ip); 26882743679SGarrett D'Amore ip->dip = dip; 26982743679SGarrett D'Amore 27082743679SGarrett D'Amore list_create(&ip->mcast, sizeof (struct iprb_mcast), 27182743679SGarrett D'Amore offsetof(struct iprb_mcast, node)); 27282743679SGarrett D'Amore 27382743679SGarrett D'Amore /* we don't support high level interrupts, so we don't need cookies */ 27482743679SGarrett D'Amore mutex_init(&ip->culock, NULL, MUTEX_DRIVER, NULL); 27582743679SGarrett D'Amore mutex_init(&ip->rulock, NULL, MUTEX_DRIVER, NULL); 27682743679SGarrett D'Amore 27782743679SGarrett D'Amore if (pci_config_setup(dip, &ip->pcih) != DDI_SUCCESS) { 27882743679SGarrett D'Amore iprb_error(ip, "unable to map configuration space"); 27982743679SGarrett D'Amore iprb_destroy(ip); 28082743679SGarrett D'Amore return (DDI_FAILURE); 28182743679SGarrett D'Amore } 28282743679SGarrett D'Amore 28382743679SGarrett D'Amore if (ddi_regs_map_setup(dip, 1, &ip->regs, 0, 0, &acc_attr, 28482743679SGarrett D'Amore &ip->regsh) != DDI_SUCCESS) { 28582743679SGarrett D'Amore iprb_error(ip, "unable to map device registers"); 28682743679SGarrett D'Amore iprb_destroy(ip); 28782743679SGarrett D'Amore return (DDI_FAILURE); 28882743679SGarrett D'Amore } 28982743679SGarrett D'Amore 29082743679SGarrett D'Amore /* Reset, but first go into idle state */ 29182743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SEL_RESET); 29282743679SGarrett D'Amore drv_usecwait(10); 29382743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SW_RESET); 29482743679SGarrett D'Amore drv_usecwait(10); 29582743679SGarrett D'Amore PUT8(ip, CSR_INTCTL, INTCTL_MASK); 29682743679SGarrett D'Amore (void) GET8(ip, CSR_INTCTL); 29782743679SGarrett D'Amore 29882743679SGarrett D'Amore /* 29982743679SGarrett D'Amore * Precalculate watchdog times. 30082743679SGarrett D'Amore */ 301*0529d5c6SJosef 'Jeff' Sipek ip->tx_timeout = TX_WATCHDOG; 302*0529d5c6SJosef 'Jeff' Sipek ip->rx_timeout = RX_WATCHDOG; 30382743679SGarrett D'Amore 30482743679SGarrett D'Amore iprb_identify(ip); 30582743679SGarrett D'Amore 30682743679SGarrett D'Amore /* Obtain our factory MAC address */ 30782743679SGarrett D'Amore w = iprb_eeprom_read(ip, 0); 30882743679SGarrett D'Amore ip->factaddr[0] = w & 0xff; 30982743679SGarrett D'Amore ip->factaddr[1] = w >> 8; 31082743679SGarrett D'Amore w = iprb_eeprom_read(ip, 1); 31182743679SGarrett D'Amore ip->factaddr[2] = w & 0xff; 31282743679SGarrett D'Amore ip->factaddr[3] = w >> 8; 31382743679SGarrett D'Amore w = iprb_eeprom_read(ip, 2); 31482743679SGarrett D'Amore ip->factaddr[4] = w & 0xff; 31582743679SGarrett D'Amore ip->factaddr[5] = w >> 8; 31682743679SGarrett D'Amore bcopy(ip->factaddr, ip->curraddr, 6); 31782743679SGarrett D'Amore 31882743679SGarrett D'Amore if (ip->resumebug) { 31982743679SGarrett D'Amore /* 32082743679SGarrett D'Amore * Generally, most devices we will ever see will 32182743679SGarrett D'Amore * already have fixed firmware. Since I can't verify 32282743679SGarrett D'Amore * the validity of the fix (no suitably downrev 32382743679SGarrett D'Amore * hardware), we'll just do our best to avoid it for 32482743679SGarrett D'Amore * devices that exhibit this behavior. 32582743679SGarrett D'Amore */ 32682743679SGarrett D'Amore if ((iprb_eeprom_read(ip, 10) & 0x02) == 0) { 32782743679SGarrett D'Amore /* EEPROM fix was already applied, assume safe. */ 32882743679SGarrett D'Amore ip->resumebug = B_FALSE; 32982743679SGarrett D'Amore } 33082743679SGarrett D'Amore } 33182743679SGarrett D'Amore 33282743679SGarrett D'Amore if ((iprb_eeprom_read(ip, 3) & 0x3) != 0x3) { 33382743679SGarrett D'Amore cmn_err(CE_CONT, "?Enabling RX errata workaround.\n"); 33482743679SGarrett D'Amore ip->rxhangbug = B_TRUE; 33582743679SGarrett D'Amore } 33682743679SGarrett D'Amore 33782743679SGarrett D'Amore /* Determine whether we have an MII or a legacy 80c24 */ 33882743679SGarrett D'Amore w = iprb_eeprom_read(ip, 6); 33982743679SGarrett D'Amore if ((w & 0x3f00) != 0x0600) { 34082743679SGarrett D'Amore if ((ip->miih = mii_alloc(ip, dip, &iprb_mii_ops)) == NULL) { 34182743679SGarrett D'Amore iprb_error(ip, "unable to allocate MII ops vector"); 34282743679SGarrett D'Amore iprb_destroy(ip); 34382743679SGarrett D'Amore return (DDI_FAILURE); 34482743679SGarrett D'Amore } 34582743679SGarrett D'Amore if (ip->canpause) { 34682743679SGarrett D'Amore mii_set_pauseable(ip->miih, B_TRUE, B_FALSE); 34782743679SGarrett D'Amore } 34882743679SGarrett D'Amore } 34982743679SGarrett D'Amore 35082743679SGarrett D'Amore /* Allocate cmds and tx region */ 35182743679SGarrett D'Amore for (i = 0; i < NUM_TX; i++) { 35282743679SGarrett D'Amore /* Command blocks */ 35382743679SGarrett D'Amore if (iprb_dma_alloc(ip, &ip->cmds[i], CB_SIZE) != DDI_SUCCESS) { 35482743679SGarrett D'Amore iprb_destroy(ip); 35582743679SGarrett D'Amore return (DDI_FAILURE); 35682743679SGarrett D'Amore } 35782743679SGarrett D'Amore } 35882743679SGarrett D'Amore 35982743679SGarrett D'Amore for (i = 0; i < NUM_TX; i++) { 36082743679SGarrett D'Amore iprb_dma_t *cb = &ip->cmds[i]; 36182743679SGarrett D'Amore /* Link the command blocks into a ring */ 36282743679SGarrett D'Amore PUTCB32(cb, CB_LNK_OFFSET, (ip->cmds[(i + 1) % NUM_TX].paddr)); 36382743679SGarrett D'Amore } 36482743679SGarrett D'Amore 36582743679SGarrett D'Amore for (i = 0; i < NUM_RX; i++) { 36682743679SGarrett D'Amore /* Rx packet buffers */ 36782743679SGarrett D'Amore if (iprb_dma_alloc(ip, &ip->rxb[i], RFD_SIZE) != DDI_SUCCESS) { 36882743679SGarrett D'Amore iprb_destroy(ip); 36982743679SGarrett D'Amore return (DDI_FAILURE); 37082743679SGarrett D'Amore } 37182743679SGarrett D'Amore } 37282743679SGarrett D'Amore if (iprb_dma_alloc(ip, &ip->stats, STATS_SIZE) != DDI_SUCCESS) { 37382743679SGarrett D'Amore iprb_destroy(ip); 37482743679SGarrett D'Amore return (DDI_FAILURE); 37582743679SGarrett D'Amore } 37682743679SGarrett D'Amore 37782743679SGarrett D'Amore if (iprb_add_intr(ip) != DDI_SUCCESS) { 37882743679SGarrett D'Amore iprb_destroy(ip); 37982743679SGarrett D'Amore return (DDI_FAILURE); 38082743679SGarrett D'Amore } 38182743679SGarrett D'Amore 38282743679SGarrett D'Amore if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 38382743679SGarrett D'Amore iprb_error(ip, "unable to allocate mac structure"); 38482743679SGarrett D'Amore iprb_destroy(ip); 38582743679SGarrett D'Amore return (DDI_FAILURE); 38682743679SGarrett D'Amore } 38782743679SGarrett D'Amore 38882743679SGarrett D'Amore macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 38982743679SGarrett D'Amore macp->m_driver = ip; 39082743679SGarrett D'Amore macp->m_dip = dip; 39182743679SGarrett D'Amore macp->m_src_addr = ip->curraddr; 39282743679SGarrett D'Amore macp->m_callbacks = &iprb_m_callbacks; 39382743679SGarrett D'Amore macp->m_min_sdu = 0; 39482743679SGarrett D'Amore macp->m_max_sdu = ETHERMTU; 39582743679SGarrett D'Amore macp->m_margin = VLAN_TAGSZ; 39682743679SGarrett D'Amore if (mac_register(macp, &ip->mach) != 0) { 39782743679SGarrett D'Amore iprb_error(ip, "unable to register mac with framework"); 39882743679SGarrett D'Amore mac_free(macp); 39982743679SGarrett D'Amore iprb_destroy(ip); 40082743679SGarrett D'Amore return (DDI_FAILURE); 40182743679SGarrett D'Amore } 40282743679SGarrett D'Amore 40382743679SGarrett D'Amore mac_free(macp); 40482743679SGarrett D'Amore return (DDI_SUCCESS); 40582743679SGarrett D'Amore } 40682743679SGarrett D'Amore 40782743679SGarrett D'Amore int 40882743679SGarrett D'Amore iprb_detach(dev_info_t *dip) 40982743679SGarrett D'Amore { 41082743679SGarrett D'Amore iprb_t *ip; 41182743679SGarrett D'Amore 41282743679SGarrett D'Amore ip = ddi_get_driver_private(dip); 41382743679SGarrett D'Amore ASSERT(ip != NULL); 41482743679SGarrett D'Amore 41582743679SGarrett D'Amore if (mac_disable(ip->mach) != 0) 41682743679SGarrett D'Amore return (DDI_FAILURE); 41782743679SGarrett D'Amore 41882743679SGarrett D'Amore (void) mac_unregister(ip->mach); 41982743679SGarrett D'Amore iprb_destroy(ip); 42082743679SGarrett D'Amore return (DDI_SUCCESS); 42182743679SGarrett D'Amore } 42282743679SGarrett D'Amore 42382743679SGarrett D'Amore int 42482743679SGarrett D'Amore iprb_add_intr(iprb_t *ip) 42582743679SGarrett D'Amore { 42682743679SGarrett D'Amore int actual; 42782743679SGarrett D'Amore 42882743679SGarrett D'Amore if (ddi_intr_alloc(ip->dip, &ip->intrh, DDI_INTR_TYPE_FIXED, 0, 1, 42982743679SGarrett D'Amore &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) { 43082743679SGarrett D'Amore iprb_error(ip, "failed allocating interrupt handle"); 43182743679SGarrett D'Amore return (DDI_FAILURE); 43282743679SGarrett D'Amore } 43382743679SGarrett D'Amore 43482743679SGarrett D'Amore if (ddi_intr_add_handler(ip->intrh, iprb_intr, ip, NULL) != 43582743679SGarrett D'Amore DDI_SUCCESS) { 43682743679SGarrett D'Amore (void) ddi_intr_free(ip->intrh); 43782743679SGarrett D'Amore ip->intrh = NULL; 43882743679SGarrett D'Amore iprb_error(ip, "failed adding interrupt handler"); 43982743679SGarrett D'Amore return (DDI_FAILURE); 44082743679SGarrett D'Amore } 44182743679SGarrett D'Amore if (ddi_intr_enable(ip->intrh) != DDI_SUCCESS) { 44282743679SGarrett D'Amore (void) ddi_intr_remove_handler(ip->intrh); 44382743679SGarrett D'Amore (void) ddi_intr_free(ip->intrh); 44482743679SGarrett D'Amore ip->intrh = NULL; 44582743679SGarrett D'Amore iprb_error(ip, "failed enabling interrupt"); 44682743679SGarrett D'Amore return (DDI_FAILURE); 44782743679SGarrett D'Amore } 44882743679SGarrett D'Amore return (DDI_SUCCESS); 44982743679SGarrett D'Amore } 45082743679SGarrett D'Amore 45182743679SGarrett D'Amore int 45282743679SGarrett D'Amore iprb_dma_alloc(iprb_t *ip, iprb_dma_t *h, size_t size) 45382743679SGarrett D'Amore { 45482743679SGarrett D'Amore size_t rlen; 45582743679SGarrett D'Amore ddi_dma_cookie_t dmac; 45682743679SGarrett D'Amore uint_t ndmac; 45782743679SGarrett D'Amore 45882743679SGarrett D'Amore if (ddi_dma_alloc_handle(ip->dip, &dma_attr, DDI_DMA_SLEEP, NULL, 45982743679SGarrett D'Amore &h->dmah) != DDI_SUCCESS) { 46082743679SGarrett D'Amore iprb_error(ip, "unable to allocate dma handle"); 46182743679SGarrett D'Amore return (DDI_FAILURE); 46282743679SGarrett D'Amore } 46382743679SGarrett D'Amore if (ddi_dma_mem_alloc(h->dmah, size, &buf_attr, DDI_DMA_CONSISTENT, 46482743679SGarrett D'Amore DDI_DMA_SLEEP, NULL, &h->vaddr, &rlen, &h->acch) != DDI_SUCCESS) { 46582743679SGarrett D'Amore iprb_error(ip, "unable to allocate dma memory"); 46682743679SGarrett D'Amore return (DDI_FAILURE); 46782743679SGarrett D'Amore } 46882743679SGarrett D'Amore bzero(h->vaddr, size); 46982743679SGarrett D'Amore if (ddi_dma_addr_bind_handle(h->dmah, NULL, h->vaddr, size, 47082743679SGarrett D'Amore DDI_DMA_CONSISTENT | DDI_DMA_RDWR, DDI_DMA_SLEEP, NULL, 47182743679SGarrett D'Amore &dmac, &ndmac) != DDI_DMA_MAPPED) { 47282743679SGarrett D'Amore iprb_error(ip, "unable to map command memory"); 47382743679SGarrett D'Amore return (DDI_FAILURE); 47482743679SGarrett D'Amore } 47582743679SGarrett D'Amore h->paddr = dmac.dmac_address; 47682743679SGarrett D'Amore return (DDI_SUCCESS); 47782743679SGarrett D'Amore } 47882743679SGarrett D'Amore 47982743679SGarrett D'Amore void 48082743679SGarrett D'Amore iprb_dma_free(iprb_dma_t *h) 48182743679SGarrett D'Amore { 48282743679SGarrett D'Amore if (h->paddr != 0) 48382743679SGarrett D'Amore (void) ddi_dma_unbind_handle(h->dmah); 48482743679SGarrett D'Amore h->paddr = 0; 48582743679SGarrett D'Amore if (h->acch != NULL) 48682743679SGarrett D'Amore ddi_dma_mem_free(&h->acch); 48782743679SGarrett D'Amore h->acch = NULL; 48882743679SGarrett D'Amore if (h->dmah != NULL) 48982743679SGarrett D'Amore ddi_dma_free_handle(&h->dmah); 49082743679SGarrett D'Amore h->dmah = NULL; 49182743679SGarrett D'Amore } 49282743679SGarrett D'Amore 49382743679SGarrett D'Amore void 49482743679SGarrett D'Amore iprb_destroy(iprb_t *ip) 49582743679SGarrett D'Amore { 49682743679SGarrett D'Amore int i; 49782743679SGarrett D'Amore iprb_mcast_t *mc; 49882743679SGarrett D'Amore 49982743679SGarrett D'Amore /* shut down interrupts */ 50082743679SGarrett D'Amore if (ip->intrh != NULL) { 50182743679SGarrett D'Amore (void) ddi_intr_disable(ip->intrh); 50282743679SGarrett D'Amore (void) ddi_intr_remove_handler(ip->intrh); 50382743679SGarrett D'Amore (void) ddi_intr_free(ip->intrh); 50482743679SGarrett D'Amore } 50582743679SGarrett D'Amore /* release DMA resources */ 50682743679SGarrett D'Amore for (i = 0; i < NUM_TX; i++) { 50782743679SGarrett D'Amore iprb_dma_free(&ip->cmds[i]); 50882743679SGarrett D'Amore } 50982743679SGarrett D'Amore for (i = 0; i < NUM_RX; i++) { 51082743679SGarrett D'Amore iprb_dma_free(&ip->rxb[i]); 51182743679SGarrett D'Amore } 51282743679SGarrett D'Amore iprb_dma_free(&ip->stats); 51382743679SGarrett D'Amore 51482743679SGarrett D'Amore if (ip->miih) 51582743679SGarrett D'Amore mii_free(ip->miih); 51682743679SGarrett D'Amore 51782743679SGarrett D'Amore /* clean up the multicast list */ 51882743679SGarrett D'Amore while ((mc = list_head(&ip->mcast)) != NULL) { 51982743679SGarrett D'Amore list_remove(&ip->mcast, mc); 52082743679SGarrett D'Amore kmem_free(mc, sizeof (*mc)); 52182743679SGarrett D'Amore } 52282743679SGarrett D'Amore 52382743679SGarrett D'Amore /* tear down register mappings */ 52482743679SGarrett D'Amore if (ip->pcih) 52582743679SGarrett D'Amore pci_config_teardown(&ip->pcih); 52682743679SGarrett D'Amore if (ip->regsh) 52782743679SGarrett D'Amore ddi_regs_map_free(&ip->regsh); 52882743679SGarrett D'Amore 52982743679SGarrett D'Amore /* clean the dip */ 53082743679SGarrett D'Amore ddi_set_driver_private(ip->dip, NULL); 53182743679SGarrett D'Amore 53282743679SGarrett D'Amore list_destroy(&ip->mcast); 53382743679SGarrett D'Amore mutex_destroy(&ip->culock); 53482743679SGarrett D'Amore mutex_destroy(&ip->rulock); 53582743679SGarrett D'Amore 53682743679SGarrett D'Amore /* and finally toss the structure itself */ 53782743679SGarrett D'Amore kmem_free(ip, sizeof (*ip)); 53882743679SGarrett D'Amore } 53982743679SGarrett D'Amore 54082743679SGarrett D'Amore void 54182743679SGarrett D'Amore iprb_identify(iprb_t *ip) 54282743679SGarrett D'Amore { 54382743679SGarrett D'Amore ip->devid = pci_config_get16(ip->pcih, PCI_CONF_DEVID); 54482743679SGarrett D'Amore ip->revid = pci_config_get8(ip->pcih, PCI_CONF_REVID); 54582743679SGarrett D'Amore 54682743679SGarrett D'Amore switch (ip->devid) { 54782743679SGarrett D'Amore case 0x1229: /* 8255x family */ 54882743679SGarrett D'Amore case 0x1030: /* Intel InBusiness */ 54982743679SGarrett D'Amore 55082743679SGarrett D'Amore if (ip->revid >= REV_82558_A4) { 55182743679SGarrett D'Amore ip->canpause = B_TRUE; 55282743679SGarrett D'Amore ip->canmwi = B_TRUE; 55382743679SGarrett D'Amore } else { 55482743679SGarrett D'Amore ip->is557 = B_TRUE; 55582743679SGarrett D'Amore } 55682743679SGarrett D'Amore if (ip->revid >= REV_82559_A0) 55782743679SGarrett D'Amore ip->resumebug = B_TRUE; 55882743679SGarrett D'Amore break; 55982743679SGarrett D'Amore 56082743679SGarrett D'Amore case 0x1209: /* Embedded 82559ER */ 56182743679SGarrett D'Amore ip->canpause = B_TRUE; 56282743679SGarrett D'Amore ip->resumebug = B_TRUE; 56382743679SGarrett D'Amore ip->canmwi = B_TRUE; 56482743679SGarrett D'Amore break; 56582743679SGarrett D'Amore 56682743679SGarrett D'Amore case 0x2449: /* ICH2 */ 56782743679SGarrett D'Amore case 0x1031: /* Pro/100 VE (ICH3) */ 56882743679SGarrett D'Amore case 0x1032: /* Pro/100 VE (ICH3) */ 56982743679SGarrett D'Amore case 0x1033: /* Pro/100 VM (ICH3) */ 57082743679SGarrett D'Amore case 0x1034: /* Pro/100 VM (ICH3) */ 57182743679SGarrett D'Amore case 0x1038: /* Pro/100 VM (ICH3) */ 57282743679SGarrett D'Amore ip->resumebug = B_TRUE; 57382743679SGarrett D'Amore if (ip->revid >= REV_82558_A4) 57482743679SGarrett D'Amore ip->canpause = B_TRUE; 57582743679SGarrett D'Amore break; 57682743679SGarrett D'Amore 57782743679SGarrett D'Amore default: 57882743679SGarrett D'Amore if (ip->revid >= REV_82558_A4) 57982743679SGarrett D'Amore ip->canpause = B_TRUE; 58082743679SGarrett D'Amore break; 58182743679SGarrett D'Amore } 58282743679SGarrett D'Amore 58382743679SGarrett D'Amore /* Allow property override MWI support - not normally needed. */ 58482743679SGarrett D'Amore if (ddi_prop_get_int(DDI_DEV_T_ANY, ip->dip, 0, "MWIEnable", 1) == 0) { 58582743679SGarrett D'Amore ip->canmwi = B_FALSE; 58682743679SGarrett D'Amore } 58782743679SGarrett D'Amore } 58882743679SGarrett D'Amore 58982743679SGarrett D'Amore void 59082743679SGarrett D'Amore iprb_eeprom_sendbits(iprb_t *ip, uint32_t val, uint8_t nbits) 59182743679SGarrett D'Amore { 59282743679SGarrett D'Amore uint32_t mask; 59382743679SGarrett D'Amore uint16_t x; 59482743679SGarrett D'Amore 59582743679SGarrett D'Amore mask = 1U << (nbits - 1); 59682743679SGarrett D'Amore while (mask) { 59782743679SGarrett D'Amore x = (mask & val) ? EEPROM_EEDI : 0; 59882743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EECS); 59982743679SGarrett D'Amore drv_usecwait(100); 60082743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS); 60182743679SGarrett D'Amore drv_usecwait(100); 60282743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EECS); 60382743679SGarrett D'Amore drv_usecwait(100); 60482743679SGarrett D'Amore mask >>= 1; 60582743679SGarrett D'Amore } 60682743679SGarrett D'Amore } 60782743679SGarrett D'Amore 60882743679SGarrett D'Amore uint16_t 60982743679SGarrett D'Amore iprb_eeprom_read(iprb_t *ip, uint16_t address) 61082743679SGarrett D'Amore { 61182743679SGarrett D'Amore uint16_t val; 61282743679SGarrett D'Amore int mask; 61382743679SGarrett D'Amore uint16_t n; 61482743679SGarrett D'Amore uint16_t bits; 61582743679SGarrett D'Amore 61682743679SGarrett D'Amore /* if we don't know the address size yet call again to determine it */ 61782743679SGarrett D'Amore if ((address != 0) && (ip->eeprom_bits == 0)) 61882743679SGarrett D'Amore (void) iprb_eeprom_read(ip, 0); 61982743679SGarrett D'Amore 62082743679SGarrett D'Amore if ((bits = ip->eeprom_bits) == 0) { 62182743679SGarrett D'Amore bits = 8; 62282743679SGarrett D'Amore ASSERT(address == 0); 62382743679SGarrett D'Amore } 62482743679SGarrett D'Amore /* enable the EEPROM chip select */ 62582743679SGarrett D'Amore PUT16(ip, CSR_EECTL, EEPROM_EECS); 62682743679SGarrett D'Amore drv_usecwait(100); 62782743679SGarrett D'Amore 62882743679SGarrett D'Amore /* send a read command */ 62982743679SGarrett D'Amore iprb_eeprom_sendbits(ip, 6, 3); 63082743679SGarrett D'Amore n = 0; 63182743679SGarrett D'Amore for (mask = (1U << (bits - 1)); mask != 0; mask >>= 1) { 63282743679SGarrett D'Amore uint16_t x = (mask & address) ? EEPROM_EEDI : 0; 63382743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EECS); 63482743679SGarrett D'Amore drv_usecwait(100); 63582743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS); 63682743679SGarrett D'Amore drv_usecwait(100); 63782743679SGarrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EECS); 63882743679SGarrett D'Amore drv_usecwait(100); 63982743679SGarrett D'Amore 64082743679SGarrett D'Amore n++; 64182743679SGarrett D'Amore /* check the dummy 0 bit */ 64282743679SGarrett D'Amore if ((GET16(ip, CSR_EECTL) & EEPROM_EEDO) == 0) { 64382743679SGarrett D'Amore if (ip->eeprom_bits == 0) { 64482743679SGarrett D'Amore ip->eeprom_bits = n; 64582743679SGarrett D'Amore cmn_err(CE_CONT, "?EEPROM size %d words.\n", 64682743679SGarrett D'Amore 1U << ip->eeprom_bits); 64782743679SGarrett D'Amore } 64882743679SGarrett D'Amore break; 64982743679SGarrett D'Amore } 65082743679SGarrett D'Amore } 65182743679SGarrett D'Amore if (n != ip->eeprom_bits) { 65282743679SGarrett D'Amore iprb_error(ip, "cannot determine EEPROM size (%d, %d)", 65382743679SGarrett D'Amore ip->eeprom_bits, n); 65482743679SGarrett D'Amore } 65582743679SGarrett D'Amore 65682743679SGarrett D'Amore /* shift out a 16-bit word */ 65782743679SGarrett D'Amore val = 0; 65882743679SGarrett D'Amore for (mask = 0x8000; mask; mask >>= 1) { 65982743679SGarrett D'Amore PUT16(ip, CSR_EECTL, EEPROM_EECS | EEPROM_EESK); 66082743679SGarrett D'Amore drv_usecwait(100); 66182743679SGarrett D'Amore if (GET16(ip, CSR_EECTL) & EEPROM_EEDO) 66282743679SGarrett D'Amore val |= mask; 66382743679SGarrett D'Amore drv_usecwait(100); 66482743679SGarrett D'Amore PUT16(ip, CSR_EECTL, EEPROM_EECS); 66582743679SGarrett D'Amore drv_usecwait(100); 66682743679SGarrett D'Amore } 66782743679SGarrett D'Amore 66882743679SGarrett D'Amore /* and disable the eeprom */ 66982743679SGarrett D'Amore PUT16(ip, CSR_EECTL, 0); 67082743679SGarrett D'Amore drv_usecwait(100); 67182743679SGarrett D'Amore 67282743679SGarrett D'Amore return (val); 67382743679SGarrett D'Amore } 67482743679SGarrett D'Amore 67582743679SGarrett D'Amore int 67682743679SGarrett D'Amore iprb_cmd_ready(iprb_t *ip) 67782743679SGarrett D'Amore { 67882743679SGarrett D'Amore /* wait for pending SCB commands to be accepted */ 67982743679SGarrett D'Amore for (int cnt = 1000000; cnt != 0; cnt -= 10) { 68082743679SGarrett D'Amore if (GET8(ip, CSR_CMD) == 0) { 68182743679SGarrett D'Amore return (DDI_SUCCESS); 68282743679SGarrett D'Amore } 68382743679SGarrett D'Amore drv_usecwait(10); 68482743679SGarrett D'Amore } 68582743679SGarrett D'Amore iprb_error(ip, "timeout waiting for chip to become ready"); 68682743679SGarrett D'Amore return (DDI_FAILURE); 68782743679SGarrett D'Amore } 68882743679SGarrett D'Amore 68982743679SGarrett D'Amore void 69082743679SGarrett D'Amore iprb_cmd_reclaim(iprb_t *ip) 69182743679SGarrett D'Amore { 69282743679SGarrett D'Amore while (ip->cmd_count) { 69382743679SGarrett D'Amore iprb_dma_t *cb = &ip->cmds[ip->cmd_tail]; 69482743679SGarrett D'Amore 69582743679SGarrett D'Amore SYNCCB(cb, CB_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL); 69682743679SGarrett D'Amore if ((GETCB16(cb, CB_STS_OFFSET) & CB_STS_C) == 0) { 69782743679SGarrett D'Amore break; 69882743679SGarrett D'Amore } 69982743679SGarrett D'Amore 70082743679SGarrett D'Amore ip->cmd_tail++; 70182743679SGarrett D'Amore ip->cmd_tail %= NUM_TX; 70282743679SGarrett D'Amore ip->cmd_count--; 70382743679SGarrett D'Amore if (ip->cmd_count == 0) { 70482743679SGarrett D'Amore ip->tx_wdog = 0; 70582743679SGarrett D'Amore } else { 706*0529d5c6SJosef 'Jeff' Sipek ip->tx_wdog = gethrtime(); 70782743679SGarrett D'Amore } 70882743679SGarrett D'Amore } 70982743679SGarrett D'Amore } 71082743679SGarrett D'Amore 71182743679SGarrett D'Amore int 71282743679SGarrett D'Amore iprb_cmd_drain(iprb_t *ip) 71382743679SGarrett D'Amore { 71482743679SGarrett D'Amore for (int i = 1000000; i; i -= 10) { 71582743679SGarrett D'Amore iprb_cmd_reclaim(ip); 71682743679SGarrett D'Amore if (ip->cmd_count == 0) 71782743679SGarrett D'Amore return (DDI_SUCCESS); 71882743679SGarrett D'Amore drv_usecwait(10); 71982743679SGarrett D'Amore } 72082743679SGarrett D'Amore iprb_error(ip, "time out waiting for commands to drain"); 72182743679SGarrett D'Amore return (DDI_FAILURE); 72282743679SGarrett D'Amore } 72382743679SGarrett D'Amore 72482743679SGarrett D'Amore int 72582743679SGarrett D'Amore iprb_cmd_submit(iprb_t *ip, uint16_t cmd) 72682743679SGarrett D'Amore { 72782743679SGarrett D'Amore iprb_dma_t *ncb = &ip->cmds[ip->cmd_head]; 72882743679SGarrett D'Amore iprb_dma_t *lcb = &ip->cmds[ip->cmd_last]; 72982743679SGarrett D'Amore 73082743679SGarrett D'Amore /* If this command will consume the last CB, interrupt when done */ 73182743679SGarrett D'Amore ASSERT((ip->cmd_count) < NUM_TX); 73282743679SGarrett D'Amore if (ip->cmd_count == (NUM_TX - 1)) { 73382743679SGarrett D'Amore cmd |= CB_CMD_I; 73482743679SGarrett D'Amore } 73582743679SGarrett D'Amore 73682743679SGarrett D'Amore /* clear the status entry */ 73782743679SGarrett D'Amore PUTCB16(ncb, CB_STS_OFFSET, 0); 73882743679SGarrett D'Amore 73982743679SGarrett D'Amore /* suspend upon completion of this new command */ 74082743679SGarrett D'Amore cmd |= CB_CMD_S; 74182743679SGarrett D'Amore PUTCB16(ncb, CB_CMD_OFFSET, cmd); 74282743679SGarrett D'Amore SYNCCB(ncb, 0, 0, DDI_DMA_SYNC_FORDEV); 74382743679SGarrett D'Amore 74482743679SGarrett D'Amore /* clear the suspend flag from the last submitted command */ 74582743679SGarrett D'Amore SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL); 74682743679SGarrett D'Amore PUTCB16(lcb, CB_CMD_OFFSET, GETCB16(lcb, CB_CMD_OFFSET) & ~CB_CMD_S); 74782743679SGarrett D'Amore SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORDEV); 74882743679SGarrett D'Amore 74982743679SGarrett D'Amore 75082743679SGarrett D'Amore /* 75182743679SGarrett D'Amore * If the chip has a resume bug, then we need to try this as a work 75282743679SGarrett D'Amore * around. Some anecdotal evidence is that this will help solve 75382743679SGarrett D'Amore * the resume bug. Its a performance hit, but only if the EEPROM 75482743679SGarrett D'Amore * is not updated. (In theory we could do this only for 10Mbps HDX, 75582743679SGarrett D'Amore * but since it should just about never get used, we keep it simple.) 75682743679SGarrett D'Amore */ 75782743679SGarrett D'Amore if (ip->resumebug) { 75882743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 75982743679SGarrett D'Amore return (DDI_FAILURE); 76082743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_NOP); 76182743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); 76282743679SGarrett D'Amore drv_usecwait(1); 76382743679SGarrett D'Amore } 76482743679SGarrett D'Amore 76582743679SGarrett D'Amore /* wait for the SCB to be ready to accept a new command */ 76682743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 76782743679SGarrett D'Amore return (DDI_FAILURE); 76882743679SGarrett D'Amore 76982743679SGarrett D'Amore /* 77082743679SGarrett D'Amore * Finally we can resume the CU. Note that if this the first 77182743679SGarrett D'Amore * command in the sequence (i.e. if the CU is IDLE), or if the 77282743679SGarrett D'Amore * CU is already busy working, then this CU resume command 77382743679SGarrett D'Amore * will not have any effect. 77482743679SGarrett D'Amore */ 77582743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_RESUME); 77682743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); /* flush CSR */ 77782743679SGarrett D'Amore 778*0529d5c6SJosef 'Jeff' Sipek ip->tx_wdog = gethrtime(); 77982743679SGarrett D'Amore ip->cmd_last = ip->cmd_head; 78082743679SGarrett D'Amore ip->cmd_head++; 78182743679SGarrett D'Amore ip->cmd_head %= NUM_TX; 78282743679SGarrett D'Amore ip->cmd_count++; 78382743679SGarrett D'Amore 78482743679SGarrett D'Amore return (DDI_SUCCESS); 78582743679SGarrett D'Amore } 78682743679SGarrett D'Amore 78782743679SGarrett D'Amore iprb_dma_t * 78882743679SGarrett D'Amore iprb_cmd_next(iprb_t *ip) 78982743679SGarrett D'Amore { 79082743679SGarrett D'Amore if (ip->cmd_count == NUM_TX) { 79182743679SGarrett D'Amore return (NULL); 79282743679SGarrett D'Amore } 79382743679SGarrett D'Amore ASSERT(ip->cmd_count < NUM_TX); 79482743679SGarrett D'Amore return (&ip->cmds[ip->cmd_head]); 79582743679SGarrett D'Amore } 79682743679SGarrett D'Amore 79782743679SGarrett D'Amore int 79882743679SGarrett D'Amore iprb_set_unicast(iprb_t *ip) 79982743679SGarrett D'Amore { 80082743679SGarrett D'Amore iprb_dma_t *cb; 80182743679SGarrett D'Amore 80282743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 80382743679SGarrett D'Amore 80482743679SGarrett D'Amore if ((cb = iprb_cmd_next(ip)) == NULL) 80582743679SGarrett D'Amore return (DDI_FAILURE); 80682743679SGarrett D'Amore 80782743679SGarrett D'Amore PUTCBEA(cb, CB_IAS_ADR_OFFSET, ip->curraddr); 80882743679SGarrett D'Amore return (iprb_cmd_submit(ip, CB_CMD_IAS)); 80982743679SGarrett D'Amore } 81082743679SGarrett D'Amore 81182743679SGarrett D'Amore int 81282743679SGarrett D'Amore iprb_set_multicast(iprb_t *ip) 81382743679SGarrett D'Amore { 81482743679SGarrett D'Amore iprb_dma_t *cb; 81582743679SGarrett D'Amore iprb_mcast_t *mc; 81682743679SGarrett D'Amore int i; 81782743679SGarrett D'Amore list_t *l; 81882743679SGarrett D'Amore 81982743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 82082743679SGarrett D'Amore 82182743679SGarrett D'Amore if ((ip->nmcast <= 0) || (ip->nmcast > CB_MCS_CNT_MAX)) { 82282743679SGarrett D'Amore /* 82382743679SGarrett D'Amore * Only send the list if the total number of multicast 82482743679SGarrett D'Amore * address is nonzero and small enough to fit. We 82582743679SGarrett D'Amore * don't error out if it is too big, because in that 82682743679SGarrett D'Amore * case we will use the "allmulticast" support 82782743679SGarrett D'Amore * via iprb_set_config instead. 82882743679SGarrett D'Amore */ 82982743679SGarrett D'Amore return (DDI_SUCCESS); 83082743679SGarrett D'Amore } 83182743679SGarrett D'Amore 83282743679SGarrett D'Amore if ((cb = iprb_cmd_next(ip)) == NULL) { 83382743679SGarrett D'Amore return (DDI_FAILURE); 83482743679SGarrett D'Amore } 83582743679SGarrett D'Amore 83682743679SGarrett D'Amore l = &ip->mcast; 83782743679SGarrett D'Amore for (mc = list_head(l), i = 0; mc; mc = list_next(l, mc), i++) { 83882743679SGarrett D'Amore PUTCBEA(cb, CB_MCS_ADR_OFFSET + (i * 6), mc->addr); 83982743679SGarrett D'Amore } 84082743679SGarrett D'Amore ASSERT(i == ip->nmcast); 84182743679SGarrett D'Amore PUTCB16(cb, CB_MCS_CNT_OFFSET, i); 84282743679SGarrett D'Amore return (iprb_cmd_submit(ip, CB_CMD_MCS)); 84382743679SGarrett D'Amore } 84482743679SGarrett D'Amore 84582743679SGarrett D'Amore int 84682743679SGarrett D'Amore iprb_set_config(iprb_t *ip) 84782743679SGarrett D'Amore { 84882743679SGarrett D'Amore iprb_dma_t *cb; 84982743679SGarrett D'Amore 85082743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 85182743679SGarrett D'Amore if ((cb = iprb_cmd_next(ip)) == NULL) { 85282743679SGarrett D'Amore return (DDI_FAILURE); 85382743679SGarrett D'Amore } 85482743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 0, 0x16); 85582743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 1, 0x8); 85682743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 2, 0); 85782743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 3, (ip->canmwi ? 1 : 0)); 85882743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 4, 0); 85982743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 5, 0); 86082743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 6, (ip->promisc ? 0x80 : 0) | 0x3a); 86182743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 7, (ip->promisc ? 0 : 0x1) | 2); 86282743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 8, (ip->miih ? 0x1 : 0)); 86382743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 9, 0); 86482743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 10, 0x2e); 86582743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 11, 0); 86682743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 12, (ip->is557 ? 0 : 1) | 0x60); 86782743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 13, 0); 86882743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 14, 0xf2); 86982743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 15, 87082743679SGarrett D'Amore (ip->miih ? 0x80 : 0) | (ip->promisc ? 0x1 : 0) | 0x48); 87182743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 16, 0); 87282743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 17, (ip->canpause ? 0x40 : 0)); 87382743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 18, (ip->is557 ? 0 : 0x8) | 0xf2); 87482743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 19, 87582743679SGarrett D'Amore ((ip->revid < REV_82558_B0) ? 0 : 0x80) | 87682743679SGarrett D'Amore (ip->canpause ? 0x18 : 0)); 87782743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 20, 0x3f); 87882743679SGarrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 21, 87982743679SGarrett D'Amore ((ip->nmcast >= CB_MCS_CNT_MAX) ? 0x8 : 0) | 0x5); 88082743679SGarrett D'Amore 88182743679SGarrett D'Amore return (iprb_cmd_submit(ip, CB_CMD_CONFIG)); 88282743679SGarrett D'Amore } 88382743679SGarrett D'Amore 88482743679SGarrett D'Amore int 88582743679SGarrett D'Amore iprb_set_ucode(iprb_t *ip) 88682743679SGarrett D'Amore { 88782743679SGarrett D'Amore iprb_dma_t *cb; 88882743679SGarrett D'Amore const iprb_ucode_t *uc = NULL; 88982743679SGarrett D'Amore int i; 89082743679SGarrett D'Amore 89182743679SGarrett D'Amore for (i = 0; iprb_ucode[i].length; i++) { 89282743679SGarrett D'Amore if (iprb_ucode[i].rev == ip->revid) { 89382743679SGarrett D'Amore uc = &iprb_ucode[i]; 89482743679SGarrett D'Amore break; 89582743679SGarrett D'Amore } 89682743679SGarrett D'Amore } 89782743679SGarrett D'Amore if (uc == NULL) { 89882743679SGarrett D'Amore /* no matching firmware found, assume success */ 89982743679SGarrett D'Amore return (DDI_SUCCESS); 90082743679SGarrett D'Amore } 90182743679SGarrett D'Amore 90282743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 90382743679SGarrett D'Amore if ((cb = iprb_cmd_next(ip)) == NULL) { 90482743679SGarrett D'Amore return (DDI_FAILURE); 90582743679SGarrett D'Amore } 90682743679SGarrett D'Amore for (i = 0; i < uc->length; i++) { 90782743679SGarrett D'Amore PUTCB32(cb, (CB_UCODE_OFFSET + i * 4), uc->ucode[i]); 90882743679SGarrett D'Amore } 90982743679SGarrett D'Amore return (iprb_cmd_submit(ip, CB_CMD_UCODE)); 91082743679SGarrett D'Amore } 91182743679SGarrett D'Amore 91282743679SGarrett D'Amore int 91382743679SGarrett D'Amore iprb_configure(iprb_t *ip) 91482743679SGarrett D'Amore { 91582743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 91682743679SGarrett D'Amore 91782743679SGarrett D'Amore if (iprb_cmd_drain(ip) != DDI_SUCCESS) 91882743679SGarrett D'Amore return (DDI_FAILURE); 91982743679SGarrett D'Amore 92082743679SGarrett D'Amore if (iprb_set_config(ip) != DDI_SUCCESS) 92182743679SGarrett D'Amore return (DDI_FAILURE); 92282743679SGarrett D'Amore if (iprb_set_unicast(ip) != DDI_SUCCESS) 92382743679SGarrett D'Amore return (DDI_FAILURE); 92482743679SGarrett D'Amore if (iprb_set_multicast(ip) != DDI_SUCCESS) 92582743679SGarrett D'Amore return (DDI_FAILURE); 92682743679SGarrett D'Amore 92782743679SGarrett D'Amore return (DDI_SUCCESS); 92882743679SGarrett D'Amore } 92982743679SGarrett D'Amore 93082743679SGarrett D'Amore void 93182743679SGarrett D'Amore iprb_stop(iprb_t *ip) 93282743679SGarrett D'Amore { 93382743679SGarrett D'Amore /* go idle */ 93482743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SEL_RESET); 93582743679SGarrett D'Amore (void) GET32(ip, CSR_PORT); 93682743679SGarrett D'Amore drv_usecwait(50); 93782743679SGarrett D'Amore 93882743679SGarrett D'Amore /* shut off device interrupts */ 93982743679SGarrett D'Amore PUT8(ip, CSR_INTCTL, INTCTL_MASK); 94082743679SGarrett D'Amore } 94182743679SGarrett D'Amore 94282743679SGarrett D'Amore int 94382743679SGarrett D'Amore iprb_start(iprb_t *ip) 94482743679SGarrett D'Amore { 94582743679SGarrett D'Amore iprb_dma_t *cb; 94682743679SGarrett D'Amore 94782743679SGarrett D'Amore ASSERT(mutex_owned(&ip->rulock)); 94882743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 94982743679SGarrett D'Amore 95082743679SGarrett D'Amore /* Reset, but first go into idle state */ 95182743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SEL_RESET); 95282743679SGarrett D'Amore (void) GET32(ip, CSR_PORT); 95382743679SGarrett D'Amore drv_usecwait(50); 95482743679SGarrett D'Amore 95582743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SW_RESET); 95682743679SGarrett D'Amore (void) GET32(ip, CSR_PORT); 95782743679SGarrett D'Amore drv_usecwait(10); 95882743679SGarrett D'Amore PUT8(ip, CSR_INTCTL, INTCTL_MASK); 95982743679SGarrett D'Amore 96082743679SGarrett D'Amore /* Reset pointers */ 96182743679SGarrett D'Amore ip->cmd_head = ip->cmd_tail = 0; 96282743679SGarrett D'Amore ip->cmd_last = NUM_TX - 1; 96382743679SGarrett D'Amore 96482743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 96582743679SGarrett D'Amore return (DDI_FAILURE); 96682743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, 0); 96782743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_CUBASE); 96882743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); 96982743679SGarrett D'Amore 97082743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 97182743679SGarrett D'Amore return (DDI_FAILURE); 97282743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, 0); 97382743679SGarrett D'Amore PUT8(ip, CSR_CMD, RUC_RUBASE); 97482743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); 97582743679SGarrett D'Amore 97682743679SGarrett D'Amore /* Send a NOP. This will be the first command seen by the device. */ 97782743679SGarrett D'Amore cb = iprb_cmd_next(ip); 97882743679SGarrett D'Amore ASSERT(cb); 97982743679SGarrett D'Amore if (iprb_cmd_submit(ip, CB_CMD_NOP) != DDI_SUCCESS) 98082743679SGarrett D'Amore return (DDI_FAILURE); 98182743679SGarrett D'Amore 98282743679SGarrett D'Amore /* as that was the first command, go ahead and submit a CU start */ 98382743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 98482743679SGarrett D'Amore return (DDI_FAILURE); 98582743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, cb->paddr); 98682743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_START); 98782743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); 98882743679SGarrett D'Amore 98982743679SGarrett D'Amore /* Upload firmware. */ 99082743679SGarrett D'Amore if (iprb_set_ucode(ip) != DDI_SUCCESS) 99182743679SGarrett D'Amore return (DDI_FAILURE); 99282743679SGarrett D'Amore 99382743679SGarrett D'Amore /* Set up RFDs */ 99482743679SGarrett D'Amore iprb_rx_init(ip); 99582743679SGarrett D'Amore 99682743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, ip->rxb[0].paddr); 99782743679SGarrett D'Amore /* wait for the SCB */ 99882743679SGarrett D'Amore (void) iprb_cmd_ready(ip); 99982743679SGarrett D'Amore PUT8(ip, CSR_CMD, RUC_START); 100082743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); /* flush CSR */ 100182743679SGarrett D'Amore 100282743679SGarrett D'Amore /* Enable device interrupts */ 100382743679SGarrett D'Amore PUT8(ip, CSR_INTCTL, 0); 100482743679SGarrett D'Amore (void) GET8(ip, CSR_INTCTL); 100582743679SGarrett D'Amore 100682743679SGarrett D'Amore return (DDI_SUCCESS); 100782743679SGarrett D'Amore } 100882743679SGarrett D'Amore 100982743679SGarrett D'Amore void 101082743679SGarrett D'Amore iprb_update_stats(iprb_t *ip) 101182743679SGarrett D'Amore { 101282743679SGarrett D'Amore iprb_dma_t *sp = &ip->stats; 1013*0529d5c6SJosef 'Jeff' Sipek hrtime_t tstamp; 101482743679SGarrett D'Amore int i; 101582743679SGarrett D'Amore 101682743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 101782743679SGarrett D'Amore 101882743679SGarrett D'Amore /* Collect the hardware stats, but don't keep redoing it */ 1019*0529d5c6SJosef 'Jeff' Sipek tstamp = gethrtime(); 1020*0529d5c6SJosef 'Jeff' Sipek if (tstamp / NANOSEC == ip->stats_time / NANOSEC) 102182743679SGarrett D'Amore return; 102282743679SGarrett D'Amore 102382743679SGarrett D'Amore PUTSTAT(sp, STATS_DONE_OFFSET, 0); 102482743679SGarrett D'Amore SYNCSTATS(sp, 0, 0, DDI_DMA_SYNC_FORDEV); 102582743679SGarrett D'Amore 102682743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 102782743679SGarrett D'Amore return; 102882743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, sp->paddr); 102982743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_STATSBASE); 103082743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); 103182743679SGarrett D'Amore 103282743679SGarrett D'Amore if (iprb_cmd_ready(ip) != DDI_SUCCESS) 103382743679SGarrett D'Amore return; 103482743679SGarrett D'Amore PUT8(ip, CSR_CMD, CUC_STATS_RST); 103582743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); /* flush wb */ 103682743679SGarrett D'Amore 103782743679SGarrett D'Amore for (i = 10000; i; i -= 10) { 103882743679SGarrett D'Amore SYNCSTATS(sp, 0, 0, DDI_DMA_SYNC_FORKERNEL); 103982743679SGarrett D'Amore if (GETSTAT(sp, STATS_DONE_OFFSET) == STATS_RST_DONE) { 104082743679SGarrett D'Amore /* yay stats are updated */ 104182743679SGarrett D'Amore break; 104282743679SGarrett D'Amore } 104382743679SGarrett D'Amore drv_usecwait(10); 104482743679SGarrett D'Amore } 104582743679SGarrett D'Amore if (i == 0) { 104682743679SGarrett D'Amore iprb_error(ip, "time out acquiring hardware statistics"); 104782743679SGarrett D'Amore return; 104882743679SGarrett D'Amore } 104982743679SGarrett D'Amore 105082743679SGarrett D'Amore ip->ex_coll += GETSTAT(sp, STATS_TX_MAXCOL_OFFSET); 105182743679SGarrett D'Amore ip->late_coll += GETSTAT(sp, STATS_TX_LATECOL_OFFSET); 105282743679SGarrett D'Amore ip->uflo += GETSTAT(sp, STATS_TX_UFLO_OFFSET); 105382743679SGarrett D'Amore ip->defer_xmt += GETSTAT(sp, STATS_TX_DEFER_OFFSET); 105482743679SGarrett D'Amore ip->one_coll += GETSTAT(sp, STATS_TX_ONECOL_OFFSET); 105582743679SGarrett D'Amore ip->multi_coll += GETSTAT(sp, STATS_TX_MULTCOL_OFFSET); 105682743679SGarrett D'Amore ip->collisions += GETSTAT(sp, STATS_TX_TOTCOL_OFFSET); 105782743679SGarrett D'Amore ip->fcs_errs += GETSTAT(sp, STATS_RX_FCS_OFFSET); 105882743679SGarrett D'Amore ip->align_errs += GETSTAT(sp, STATS_RX_ALIGN_OFFSET); 105982743679SGarrett D'Amore ip->norcvbuf += GETSTAT(sp, STATS_RX_NOBUF_OFFSET); 106082743679SGarrett D'Amore ip->oflo += GETSTAT(sp, STATS_RX_OFLO_OFFSET); 106182743679SGarrett D'Amore ip->runt += GETSTAT(sp, STATS_RX_SHORT_OFFSET); 106282743679SGarrett D'Amore 106382743679SGarrett D'Amore ip->stats_time = tstamp; 106482743679SGarrett D'Amore } 106582743679SGarrett D'Amore 106682743679SGarrett D'Amore mblk_t * 106782743679SGarrett D'Amore iprb_send(iprb_t *ip, mblk_t *mp) 106882743679SGarrett D'Amore { 106982743679SGarrett D'Amore iprb_dma_t *cb; 107082743679SGarrett D'Amore size_t sz; 107182743679SGarrett D'Amore 107282743679SGarrett D'Amore ASSERT(mutex_owned(&ip->culock)); 107382743679SGarrett D'Amore 107482743679SGarrett D'Amore /* possibly reclaim some CBs */ 107582743679SGarrett D'Amore iprb_cmd_reclaim(ip); 107682743679SGarrett D'Amore 107782743679SGarrett D'Amore cb = iprb_cmd_next(ip); 107882743679SGarrett D'Amore 107982743679SGarrett D'Amore if (cb == NULL) { 108082743679SGarrett D'Amore /* flow control */ 108182743679SGarrett D'Amore ip->wantw = B_TRUE; 108282743679SGarrett D'Amore return (mp); 108382743679SGarrett D'Amore } 108482743679SGarrett D'Amore 108582743679SGarrett D'Amore if ((sz = msgsize(mp)) > (ETHERMAX + VLAN_TAGSZ)) { 108682743679SGarrett D'Amore /* Generally this should never occur */ 108782743679SGarrett D'Amore ip->macxmt_errs++; 108882743679SGarrett D'Amore freemsg(mp); 108982743679SGarrett D'Amore return (NULL); 109082743679SGarrett D'Amore } 109182743679SGarrett D'Amore 109282743679SGarrett D'Amore ip->opackets++; 109382743679SGarrett D'Amore ip->obytes += sz; 109482743679SGarrett D'Amore 109582743679SGarrett D'Amore PUTCB32(cb, CB_TX_TBD_OFFSET, 0xffffffffU); 109682743679SGarrett D'Amore PUTCB16(cb, CB_TX_COUNT_OFFSET, (sz & 0x3fff) | CB_TX_EOF); 109782743679SGarrett D'Amore PUTCB8(cb, CB_TX_THRESH_OFFSET, (sz / 8) & 0xff); 109882743679SGarrett D'Amore PUTCB8(cb, CB_TX_NUMBER_OFFSET, 0); 109982743679SGarrett D'Amore mcopymsg(mp, cb->vaddr + CB_TX_DATA_OFFSET); 110082743679SGarrett D'Amore if (cb->vaddr[CB_TX_DATA_OFFSET] & 0x1) { 110182743679SGarrett D'Amore if (bcmp(cb->vaddr + CB_TX_DATA_OFFSET, &iprb_bcast, 6) != 0) { 110282743679SGarrett D'Amore ip->multixmt++; 110382743679SGarrett D'Amore } else { 110482743679SGarrett D'Amore ip->brdcstxmt++; 110582743679SGarrett D'Amore } 110682743679SGarrett D'Amore } 110782743679SGarrett D'Amore SYNCCB(cb, 0, CB_TX_DATA_OFFSET + sz, DDI_DMA_SYNC_FORDEV); 110882743679SGarrett D'Amore 110982743679SGarrett D'Amore if (iprb_cmd_submit(ip, CB_CMD_TX) != DDI_SUCCESS) { 111082743679SGarrett D'Amore ip->macxmt_errs++; 111182743679SGarrett D'Amore } 111282743679SGarrett D'Amore 111382743679SGarrett D'Amore return (NULL); 111482743679SGarrett D'Amore } 111582743679SGarrett D'Amore 111682743679SGarrett D'Amore void 111782743679SGarrett D'Amore iprb_rx_add(iprb_t *ip) 111882743679SGarrett D'Amore { 111982743679SGarrett D'Amore uint16_t last, curr, next; 112082743679SGarrett D'Amore iprb_dma_t *rfd, *nfd, *lfd; 112182743679SGarrett D'Amore 112282743679SGarrett D'Amore ASSERT(mutex_owned(&ip->rulock)); 112382743679SGarrett D'Amore 112482743679SGarrett D'Amore curr = ip->rx_index; 112582743679SGarrett D'Amore last = ip->rx_last; 112682743679SGarrett D'Amore next = (curr + 1) % NUM_RX; 112782743679SGarrett D'Amore 112882743679SGarrett D'Amore ip->rx_last = curr; 112982743679SGarrett D'Amore ip->rx_index = next; 113082743679SGarrett D'Amore 113182743679SGarrett D'Amore lfd = &ip->rxb[last]; 113282743679SGarrett D'Amore rfd = &ip->rxb[curr]; 113382743679SGarrett D'Amore nfd = &ip->rxb[next]; 113482743679SGarrett D'Amore 113582743679SGarrett D'Amore PUTRFD32(rfd, RFD_LNK_OFFSET, nfd->paddr); 113682743679SGarrett D'Amore PUTRFD16(rfd, RFD_CTL_OFFSET, RFD_CTL_EL); 113782743679SGarrett D'Amore PUTRFD16(rfd, RFD_SIZ_OFFSET, RFD_SIZE - RFD_PKT_OFFSET); 113882743679SGarrett D'Amore PUTRFD16(rfd, RFD_CNT_OFFSET, 0); 113982743679SGarrett D'Amore SYNCRFD(rfd, 0, RFD_PKT_OFFSET, DDI_DMA_SYNC_FORDEV); 114082743679SGarrett D'Amore /* clear the suspend & EL bits from the previous RFD */ 114182743679SGarrett D'Amore PUTRFD16(lfd, RFD_CTL_OFFSET, 0); 114282743679SGarrett D'Amore SYNCRFD(rfd, RFD_CTL_OFFSET, 2, DDI_DMA_SYNC_FORDEV); 114382743679SGarrett D'Amore } 114482743679SGarrett D'Amore 114582743679SGarrett D'Amore void 114682743679SGarrett D'Amore iprb_rx_init(iprb_t *ip) 114782743679SGarrett D'Amore { 114882743679SGarrett D'Amore ip->rx_index = 0; 114982743679SGarrett D'Amore ip->rx_last = NUM_RX - 1; 115082743679SGarrett D'Amore for (int i = 0; i < NUM_RX; i++) 115182743679SGarrett D'Amore iprb_rx_add(ip); 115282743679SGarrett D'Amore ip->rx_index = 0; 115382743679SGarrett D'Amore ip->rx_last = NUM_RX - 1; 115482743679SGarrett D'Amore } 115582743679SGarrett D'Amore 115682743679SGarrett D'Amore mblk_t * 115782743679SGarrett D'Amore iprb_rx(iprb_t *ip) 115882743679SGarrett D'Amore { 115982743679SGarrett D'Amore iprb_dma_t *rfd; 116082743679SGarrett D'Amore uint16_t cnt; 116182743679SGarrett D'Amore uint16_t sts; 116282743679SGarrett D'Amore int i; 116382743679SGarrett D'Amore mblk_t *mplist; 116482743679SGarrett D'Amore mblk_t **mpp; 116582743679SGarrett D'Amore mblk_t *mp; 116682743679SGarrett D'Amore 116782743679SGarrett D'Amore mplist = NULL; 116882743679SGarrett D'Amore mpp = &mplist; 116982743679SGarrett D'Amore 117082743679SGarrett D'Amore for (i = 0; i < NUM_RX; i++) { 117182743679SGarrett D'Amore rfd = &ip->rxb[ip->rx_index]; 117282743679SGarrett D'Amore SYNCRFD(rfd, RFD_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL); 117382743679SGarrett D'Amore if ((GETRFD16(rfd, RFD_STS_OFFSET) & RFD_STS_C) == 0) { 117482743679SGarrett D'Amore break; 117582743679SGarrett D'Amore } 117682743679SGarrett D'Amore 1177*0529d5c6SJosef 'Jeff' Sipek ip->rx_wdog = gethrtime(); 117882743679SGarrett D'Amore 117982743679SGarrett D'Amore SYNCRFD(rfd, 0, 0, DDI_DMA_SYNC_FORKERNEL); 118082743679SGarrett D'Amore cnt = GETRFD16(rfd, RFD_CNT_OFFSET); 118182743679SGarrett D'Amore cnt &= ~(RFD_CNT_EOF | RFD_CNT_F); 118282743679SGarrett D'Amore sts = GETRFD16(rfd, RFD_STS_OFFSET); 118382743679SGarrett D'Amore 118482743679SGarrett D'Amore if (cnt > (ETHERMAX + VLAN_TAGSZ)) { 118582743679SGarrett D'Amore ip->toolong++; 118682743679SGarrett D'Amore iprb_rx_add(ip); 118782743679SGarrett D'Amore continue; 118882743679SGarrett D'Amore } 118982743679SGarrett D'Amore if (((sts & RFD_STS_OK) == 0) && (sts & RFD_STS_ERRS)) { 119082743679SGarrett D'Amore iprb_rx_add(ip); 119182743679SGarrett D'Amore continue; 119282743679SGarrett D'Amore } 119382743679SGarrett D'Amore if ((mp = allocb(cnt, BPRI_MED)) == NULL) { 119482743679SGarrett D'Amore ip->norcvbuf++; 119582743679SGarrett D'Amore iprb_rx_add(ip); 119682743679SGarrett D'Amore continue; 119782743679SGarrett D'Amore } 119882743679SGarrett D'Amore bcopy(rfd->vaddr + RFD_PKT_OFFSET, mp->b_wptr, cnt); 119982743679SGarrett D'Amore 120082743679SGarrett D'Amore /* return it to the RFD list */ 120182743679SGarrett D'Amore iprb_rx_add(ip); 120282743679SGarrett D'Amore 120382743679SGarrett D'Amore mp->b_wptr += cnt; 120482743679SGarrett D'Amore ip->ipackets++; 120582743679SGarrett D'Amore ip->rbytes += cnt; 120682743679SGarrett D'Amore if (mp->b_rptr[0] & 0x1) { 120782743679SGarrett D'Amore if (bcmp(mp->b_rptr, &iprb_bcast, 6) != 0) { 120882743679SGarrett D'Amore ip->multircv++; 120982743679SGarrett D'Amore } else { 121082743679SGarrett D'Amore ip->brdcstrcv++; 121182743679SGarrett D'Amore } 121282743679SGarrett D'Amore } 121382743679SGarrett D'Amore *mpp = mp; 121482743679SGarrett D'Amore mpp = &mp->b_next; 121582743679SGarrett D'Amore } 121682743679SGarrett D'Amore return (mplist); 121782743679SGarrett D'Amore } 121882743679SGarrett D'Amore 121982743679SGarrett D'Amore int 122082743679SGarrett D'Amore iprb_m_promisc(void *arg, boolean_t on) 122182743679SGarrett D'Amore { 122282743679SGarrett D'Amore iprb_t *ip = arg; 122382743679SGarrett D'Amore 122482743679SGarrett D'Amore mutex_enter(&ip->culock); 122582743679SGarrett D'Amore ip->promisc = on; 122682743679SGarrett D'Amore if (ip->running && !ip->suspended) 122782743679SGarrett D'Amore (void) iprb_configure(ip); 122882743679SGarrett D'Amore mutex_exit(&ip->culock); 122982743679SGarrett D'Amore return (0); 123082743679SGarrett D'Amore } 123182743679SGarrett D'Amore 123282743679SGarrett D'Amore int 123382743679SGarrett D'Amore iprb_m_unicst(void *arg, const uint8_t *macaddr) 123482743679SGarrett D'Amore { 123582743679SGarrett D'Amore iprb_t *ip = arg; 123682743679SGarrett D'Amore 123782743679SGarrett D'Amore mutex_enter(&ip->culock); 123882743679SGarrett D'Amore bcopy(macaddr, ip->curraddr, 6); 123982743679SGarrett D'Amore if (ip->running && !ip->suspended) 124082743679SGarrett D'Amore (void) iprb_configure(ip); 124182743679SGarrett D'Amore mutex_exit(&ip->culock); 124282743679SGarrett D'Amore return (0); 124382743679SGarrett D'Amore } 124482743679SGarrett D'Amore 124582743679SGarrett D'Amore int 124682743679SGarrett D'Amore iprb_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr) 124782743679SGarrett D'Amore { 124882743679SGarrett D'Amore iprb_t *ip = arg; 124982743679SGarrett D'Amore list_t *l = &ip->mcast; 125082743679SGarrett D'Amore iprb_mcast_t *mc; 125182743679SGarrett D'Amore 125282743679SGarrett D'Amore if (add) { 125382743679SGarrett D'Amore mc = kmem_alloc(sizeof (*mc), KM_NOSLEEP); 125482743679SGarrett D'Amore if (mc == NULL) { 125582743679SGarrett D'Amore return (ENOMEM); 125682743679SGarrett D'Amore } 125782743679SGarrett D'Amore bcopy(macaddr, mc->addr, 6); 125882743679SGarrett D'Amore mutex_enter(&ip->culock); 125982743679SGarrett D'Amore list_insert_head(l, mc); 126082743679SGarrett D'Amore ip->nmcast++; 126182743679SGarrett D'Amore if (ip->running && !ip->suspended) 126282743679SGarrett D'Amore (void) iprb_configure(ip); 126382743679SGarrett D'Amore mutex_exit(&ip->culock); 126482743679SGarrett D'Amore } else { 126582743679SGarrett D'Amore mutex_enter(&ip->culock); 126682743679SGarrett D'Amore for (mc = list_head(l); mc != NULL; mc = list_next(l, mc)) { 126782743679SGarrett D'Amore if (bcmp(macaddr, mc->addr, 6) == 0) { 126882743679SGarrett D'Amore list_remove(&ip->mcast, mc); 126982743679SGarrett D'Amore ip->nmcast--; 127082743679SGarrett D'Amore if (ip->running && !ip->suspended) 127182743679SGarrett D'Amore (void) iprb_configure(ip); 127282743679SGarrett D'Amore break; 127382743679SGarrett D'Amore } 127482743679SGarrett D'Amore } 127582743679SGarrett D'Amore mutex_exit(&ip->culock); 127682743679SGarrett D'Amore if (mc) 127782743679SGarrett D'Amore kmem_free(mc, sizeof (*mc)); 127882743679SGarrett D'Amore } 127982743679SGarrett D'Amore return (0); 128082743679SGarrett D'Amore } 128182743679SGarrett D'Amore 128282743679SGarrett D'Amore int 128382743679SGarrett D'Amore iprb_m_start(void *arg) 128482743679SGarrett D'Amore { 128582743679SGarrett D'Amore int rv; 128682743679SGarrett D'Amore iprb_t *ip = arg; 128782743679SGarrett D'Amore 128882743679SGarrett D'Amore mutex_enter(&ip->rulock); 128982743679SGarrett D'Amore mutex_enter(&ip->culock); 129082743679SGarrett D'Amore rv = ip->suspended ? 0 : iprb_start(ip); 129182743679SGarrett D'Amore if (rv == 0) 129282743679SGarrett D'Amore ip->running = B_TRUE; 129382743679SGarrett D'Amore ip->perh = ddi_periodic_add(iprb_periodic, ip, 5000000000, 0); 129482743679SGarrett D'Amore mutex_exit(&ip->culock); 129582743679SGarrett D'Amore mutex_exit(&ip->rulock); 129682743679SGarrett D'Amore if (rv == 0) { 129782743679SGarrett D'Amore if (ip->miih) 129882743679SGarrett D'Amore mii_start(ip->miih); 129982743679SGarrett D'Amore else 130082743679SGarrett D'Amore /* might be a lie. */ 130182743679SGarrett D'Amore mac_link_update(ip->mach, LINK_STATE_UP); 130282743679SGarrett D'Amore } 130382743679SGarrett D'Amore return (rv ? EIO : 0); 130482743679SGarrett D'Amore } 130582743679SGarrett D'Amore 130682743679SGarrett D'Amore void 130782743679SGarrett D'Amore iprb_m_stop(void *arg) 130882743679SGarrett D'Amore { 130982743679SGarrett D'Amore iprb_t *ip = arg; 131082743679SGarrett D'Amore 131182743679SGarrett D'Amore if (ip->miih) { 131282743679SGarrett D'Amore mii_stop(ip->miih); 131382743679SGarrett D'Amore } else { 131482743679SGarrett D'Amore mac_link_update(ip->mach, LINK_STATE_DOWN); 131582743679SGarrett D'Amore } 1316197c9523SMarcel Telka 1317197c9523SMarcel Telka ddi_periodic_delete(ip->perh); 1318197c9523SMarcel Telka ip->perh = 0; 1319197c9523SMarcel Telka 132082743679SGarrett D'Amore mutex_enter(&ip->rulock); 132182743679SGarrett D'Amore mutex_enter(&ip->culock); 132282743679SGarrett D'Amore 132382743679SGarrett D'Amore if (!ip->suspended) { 132482743679SGarrett D'Amore iprb_update_stats(ip); 132582743679SGarrett D'Amore iprb_stop(ip); 132682743679SGarrett D'Amore } 132782743679SGarrett D'Amore ip->running = B_FALSE; 132882743679SGarrett D'Amore mutex_exit(&ip->culock); 132982743679SGarrett D'Amore mutex_exit(&ip->rulock); 133082743679SGarrett D'Amore } 133182743679SGarrett D'Amore 133282743679SGarrett D'Amore int 133382743679SGarrett D'Amore iprb_m_stat(void *arg, uint_t stat, uint64_t *val) 133482743679SGarrett D'Amore { 133582743679SGarrett D'Amore iprb_t *ip = arg; 133682743679SGarrett D'Amore 133782743679SGarrett D'Amore if (ip->miih && (mii_m_getstat(ip->miih, stat, val) == 0)) { 133882743679SGarrett D'Amore return (0); 133982743679SGarrett D'Amore } 134082743679SGarrett D'Amore 134182743679SGarrett D'Amore mutex_enter(&ip->culock); 134282743679SGarrett D'Amore if ((!ip->suspended) && (ip->running)) { 134382743679SGarrett D'Amore iprb_update_stats(ip); 134482743679SGarrett D'Amore } 134582743679SGarrett D'Amore mutex_exit(&ip->culock); 134682743679SGarrett D'Amore 134782743679SGarrett D'Amore switch (stat) { 134882743679SGarrett D'Amore case MAC_STAT_IFSPEED: 134982743679SGarrett D'Amore if (ip->miih == NULL) { 135082743679SGarrett D'Amore *val = 10000000; /* 10 Mbps */ 135182743679SGarrett D'Amore } 135282743679SGarrett D'Amore break; 135382743679SGarrett D'Amore case ETHER_STAT_LINK_DUPLEX: 135482743679SGarrett D'Amore if (ip->miih == NULL) { 135582743679SGarrett D'Amore *val = LINK_DUPLEX_UNKNOWN; 135682743679SGarrett D'Amore } 135782743679SGarrett D'Amore break; 135882743679SGarrett D'Amore case MAC_STAT_MULTIRCV: 135982743679SGarrett D'Amore *val = ip->multircv; 136082743679SGarrett D'Amore break; 136182743679SGarrett D'Amore case MAC_STAT_BRDCSTRCV: 136282743679SGarrett D'Amore *val = ip->brdcstrcv; 136382743679SGarrett D'Amore break; 136482743679SGarrett D'Amore case MAC_STAT_MULTIXMT: 136582743679SGarrett D'Amore *val = ip->multixmt; 136682743679SGarrett D'Amore break; 136782743679SGarrett D'Amore case MAC_STAT_BRDCSTXMT: 136882743679SGarrett D'Amore *val = ip->brdcstxmt; 136982743679SGarrett D'Amore break; 137082743679SGarrett D'Amore case MAC_STAT_IPACKETS: 137182743679SGarrett D'Amore * val = ip->ipackets; 137282743679SGarrett D'Amore break; 137382743679SGarrett D'Amore case MAC_STAT_RBYTES: 137482743679SGarrett D'Amore *val = ip->rbytes; 137582743679SGarrett D'Amore break; 137682743679SGarrett D'Amore case MAC_STAT_OPACKETS: 137782743679SGarrett D'Amore *val = ip->opackets; 137882743679SGarrett D'Amore break; 137982743679SGarrett D'Amore case MAC_STAT_OBYTES: 138082743679SGarrett D'Amore *val = ip->obytes; 138182743679SGarrett D'Amore break; 138282743679SGarrett D'Amore case MAC_STAT_NORCVBUF: 138382743679SGarrett D'Amore *val = ip->norcvbuf; 138482743679SGarrett D'Amore break; 138582743679SGarrett D'Amore case MAC_STAT_COLLISIONS: 138682743679SGarrett D'Amore *val = ip->collisions; 138782743679SGarrett D'Amore break; 138882743679SGarrett D'Amore case MAC_STAT_IERRORS: 138982743679SGarrett D'Amore *val = ip->align_errs + 139082743679SGarrett D'Amore ip->fcs_errs + 139182743679SGarrett D'Amore ip->norcvbuf + 139282743679SGarrett D'Amore ip->runt + 139382743679SGarrett D'Amore ip->toolong + 139482743679SGarrett D'Amore ip->macrcv_errs; 139582743679SGarrett D'Amore break; 139682743679SGarrett D'Amore case MAC_STAT_OERRORS: 139782743679SGarrett D'Amore *val = ip->ex_coll + 139882743679SGarrett D'Amore ip->late_coll + 139982743679SGarrett D'Amore ip->uflo + 140082743679SGarrett D'Amore ip->macxmt_errs + 140182743679SGarrett D'Amore ip->nocarrier; 140282743679SGarrett D'Amore break; 140382743679SGarrett D'Amore case ETHER_STAT_ALIGN_ERRORS: 140482743679SGarrett D'Amore *val = ip->align_errs; 140582743679SGarrett D'Amore break; 140682743679SGarrett D'Amore case ETHER_STAT_FCS_ERRORS: 140782743679SGarrett D'Amore *val = ip->fcs_errs; 140882743679SGarrett D'Amore break; 140982743679SGarrett D'Amore case ETHER_STAT_DEFER_XMTS: 141082743679SGarrett D'Amore *val = ip->defer_xmt; 141182743679SGarrett D'Amore break; 141282743679SGarrett D'Amore case ETHER_STAT_FIRST_COLLISIONS: 141382743679SGarrett D'Amore *val = ip->one_coll + ip->multi_coll + ip->ex_coll; 141482743679SGarrett D'Amore break; 141582743679SGarrett D'Amore case ETHER_STAT_MULTI_COLLISIONS: 141682743679SGarrett D'Amore *val = ip->multi_coll; 141782743679SGarrett D'Amore break; 141882743679SGarrett D'Amore case ETHER_STAT_TX_LATE_COLLISIONS: 141982743679SGarrett D'Amore *val = ip->late_coll; 142082743679SGarrett D'Amore break; 142182743679SGarrett D'Amore case ETHER_STAT_EX_COLLISIONS: 142282743679SGarrett D'Amore *val = ip->ex_coll; 142382743679SGarrett D'Amore break; 142482743679SGarrett D'Amore case MAC_STAT_OVERFLOWS: 142582743679SGarrett D'Amore *val = ip->oflo; 142682743679SGarrett D'Amore break; 142782743679SGarrett D'Amore case MAC_STAT_UNDERFLOWS: 142882743679SGarrett D'Amore *val = ip->uflo; 142982743679SGarrett D'Amore break; 143082743679SGarrett D'Amore case ETHER_STAT_TOOSHORT_ERRORS: 143182743679SGarrett D'Amore *val = ip->runt; 143282743679SGarrett D'Amore break; 143382743679SGarrett D'Amore case ETHER_STAT_TOOLONG_ERRORS: 143482743679SGarrett D'Amore *val = ip->toolong; 143582743679SGarrett D'Amore break; 143682743679SGarrett D'Amore case ETHER_STAT_CARRIER_ERRORS: 143782743679SGarrett D'Amore *val = ip->nocarrier; /* reported only for "suspend" */ 143882743679SGarrett D'Amore break; 143982743679SGarrett D'Amore case ETHER_STAT_MACXMT_ERRORS: 144082743679SGarrett D'Amore *val = ip->macxmt_errs; 144182743679SGarrett D'Amore break; 144282743679SGarrett D'Amore case ETHER_STAT_MACRCV_ERRORS: 144382743679SGarrett D'Amore *val = ip->macrcv_errs; 144482743679SGarrett D'Amore break; 144582743679SGarrett D'Amore default: 144682743679SGarrett D'Amore return (ENOTSUP); 144782743679SGarrett D'Amore } 144882743679SGarrett D'Amore return (0); 144982743679SGarrett D'Amore } 145082743679SGarrett D'Amore 145182743679SGarrett D'Amore void 145282743679SGarrett D'Amore iprb_m_propinfo(void *arg, const char *name, mac_prop_id_t id, 145382743679SGarrett D'Amore mac_prop_info_handle_t pih) 145482743679SGarrett D'Amore { 145582743679SGarrett D'Amore iprb_t *ip = arg; 145682743679SGarrett D'Amore 145782743679SGarrett D'Amore if (ip->miih != NULL) { 145882743679SGarrett D'Amore mii_m_propinfo(ip->miih, name, id, pih); 145982743679SGarrett D'Amore return; 146082743679SGarrett D'Amore } 146182743679SGarrett D'Amore switch (id) { 146282743679SGarrett D'Amore case MAC_PROP_DUPLEX: 146382743679SGarrett D'Amore case MAC_PROP_SPEED: 146482743679SGarrett D'Amore mac_prop_info_set_perm(pih, MAC_PROP_PERM_READ); 146582743679SGarrett D'Amore break; 146682743679SGarrett D'Amore } 146782743679SGarrett D'Amore } 146882743679SGarrett D'Amore 146982743679SGarrett D'Amore int 147082743679SGarrett D'Amore iprb_m_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz, 147182743679SGarrett D'Amore void *val) 147282743679SGarrett D'Amore { 147382743679SGarrett D'Amore iprb_t *ip = arg; 147482743679SGarrett D'Amore uint64_t x; 147582743679SGarrett D'Amore 147682743679SGarrett D'Amore if (ip->miih != NULL) { 147782743679SGarrett D'Amore return (mii_m_getprop(ip->miih, name, id, sz, val)); 147882743679SGarrett D'Amore } 147982743679SGarrett D'Amore switch (id) { 148082743679SGarrett D'Amore case MAC_PROP_SPEED: 148182743679SGarrett D'Amore x = 10000000; 148282743679SGarrett D'Amore bcopy(&x, val, sizeof (x)); 148382743679SGarrett D'Amore return (0); 148482743679SGarrett D'Amore 148582743679SGarrett D'Amore case MAC_PROP_DUPLEX: 148682743679SGarrett D'Amore x = LINK_DUPLEX_UNKNOWN; 148782743679SGarrett D'Amore bcopy(&x, val, sizeof (x)); 148882743679SGarrett D'Amore return (0); 148982743679SGarrett D'Amore } 149082743679SGarrett D'Amore 149182743679SGarrett D'Amore return (ENOTSUP); 149282743679SGarrett D'Amore } 149382743679SGarrett D'Amore 149482743679SGarrett D'Amore int 149582743679SGarrett D'Amore iprb_m_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz, 149682743679SGarrett D'Amore const void *val) 149782743679SGarrett D'Amore { 149882743679SGarrett D'Amore iprb_t *ip = arg; 149982743679SGarrett D'Amore 150082743679SGarrett D'Amore if (ip->miih != NULL) { 150182743679SGarrett D'Amore return (mii_m_setprop(ip->miih, name, id, sz, val)); 150282743679SGarrett D'Amore } 150382743679SGarrett D'Amore return (ENOTSUP); 150482743679SGarrett D'Amore } 150582743679SGarrett D'Amore 150682743679SGarrett D'Amore mblk_t * 150782743679SGarrett D'Amore iprb_m_tx(void *arg, mblk_t *mp) 150882743679SGarrett D'Amore { 150982743679SGarrett D'Amore iprb_t *ip = arg; 151082743679SGarrett D'Amore mblk_t *nmp; 151182743679SGarrett D'Amore 151282743679SGarrett D'Amore mutex_enter(&ip->culock); 151382743679SGarrett D'Amore 151482743679SGarrett D'Amore while (mp != NULL) { 151582743679SGarrett D'Amore nmp = mp->b_next; 151682743679SGarrett D'Amore mp->b_next = NULL; 151782743679SGarrett D'Amore if (ip->suspended) { 151882743679SGarrett D'Amore freemsg(mp); 151982743679SGarrett D'Amore ip->nocarrier++; 152082743679SGarrett D'Amore mp = nmp; 152182743679SGarrett D'Amore continue; 152282743679SGarrett D'Amore } 152382743679SGarrett D'Amore if ((mp = iprb_send(ip, mp)) != NULL) { 152482743679SGarrett D'Amore mp->b_next = nmp; 152582743679SGarrett D'Amore break; 152682743679SGarrett D'Amore } 152782743679SGarrett D'Amore mp = nmp; 152882743679SGarrett D'Amore } 152982743679SGarrett D'Amore mutex_exit(&ip->culock); 153082743679SGarrett D'Amore return (mp); 153182743679SGarrett D'Amore } 153282743679SGarrett D'Amore 153382743679SGarrett D'Amore void 153482743679SGarrett D'Amore iprb_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 153582743679SGarrett D'Amore { 153682743679SGarrett D'Amore iprb_t *ip = arg; 153782743679SGarrett D'Amore 153882743679SGarrett D'Amore if ((ip->miih != NULL) && (mii_m_loop_ioctl(ip->miih, wq, mp))) 153982743679SGarrett D'Amore return; 154082743679SGarrett D'Amore 154182743679SGarrett D'Amore miocnak(wq, mp, 0, EINVAL); 154282743679SGarrett D'Amore } 154382743679SGarrett D'Amore 154482743679SGarrett D'Amore uint16_t 154582743679SGarrett D'Amore iprb_mii_read(void *arg, uint8_t phy, uint8_t reg) 154682743679SGarrett D'Amore { 154782743679SGarrett D'Amore iprb_t *ip = arg; 154882743679SGarrett D'Amore uint32_t mdi; 154982743679SGarrett D'Amore 155082743679SGarrett D'Amore /* 155182743679SGarrett D'Amore * NB: we are guaranteed by the MII layer not to be suspended. 155282743679SGarrett D'Amore * Furthermore, we have an independent MII register. 155382743679SGarrett D'Amore */ 155482743679SGarrett D'Amore 155582743679SGarrett D'Amore mdi = MDI_OP_RD | 155682743679SGarrett D'Amore ((uint32_t)phy << MDI_PHYAD_SHIFT) | 155782743679SGarrett D'Amore ((uint32_t)reg << MDI_REGAD_SHIFT); 155882743679SGarrett D'Amore 155982743679SGarrett D'Amore PUT32(ip, CSR_MDICTL, mdi); 156082743679SGarrett D'Amore for (int i = 0; i < 100; i++) { 156182743679SGarrett D'Amore mdi = GET32(ip, CSR_MDICTL); 156282743679SGarrett D'Amore if (mdi & MDI_R) { 156382743679SGarrett D'Amore return (mdi & 0xffff); 156482743679SGarrett D'Amore } 156582743679SGarrett D'Amore drv_usecwait(1); 156682743679SGarrett D'Amore } 156782743679SGarrett D'Amore return (0xffff); 156882743679SGarrett D'Amore } 156982743679SGarrett D'Amore 157082743679SGarrett D'Amore void 157182743679SGarrett D'Amore iprb_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data) 157282743679SGarrett D'Amore { 157382743679SGarrett D'Amore iprb_t *ip = arg; 157482743679SGarrett D'Amore uint32_t mdi; 157582743679SGarrett D'Amore 157682743679SGarrett D'Amore mdi = MDI_OP_WR | 157782743679SGarrett D'Amore ((uint32_t)phy << MDI_PHYAD_SHIFT) | 157882743679SGarrett D'Amore ((uint32_t)reg << MDI_REGAD_SHIFT) | 157982743679SGarrett D'Amore (data); 158082743679SGarrett D'Amore 158182743679SGarrett D'Amore PUT32(ip, CSR_MDICTL, mdi); 158282743679SGarrett D'Amore for (int i = 0; i < 100; i++) { 158382743679SGarrett D'Amore if (GET32(ip, CSR_MDICTL) & MDI_R) 158482743679SGarrett D'Amore break; 158582743679SGarrett D'Amore } 158682743679SGarrett D'Amore } 158782743679SGarrett D'Amore 158882743679SGarrett D'Amore void 158982743679SGarrett D'Amore iprb_mii_notify(void *arg, link_state_t link) 159082743679SGarrett D'Amore { 159182743679SGarrett D'Amore iprb_t *ip = arg; 159282743679SGarrett D'Amore 159382743679SGarrett D'Amore mac_link_update(ip->mach, link); 159482743679SGarrett D'Amore } 159582743679SGarrett D'Amore 159682743679SGarrett D'Amore uint_t 159782743679SGarrett D'Amore iprb_intr(caddr_t arg1, caddr_t arg2) 159882743679SGarrett D'Amore { 159982743679SGarrett D'Amore iprb_t *ip = (void *)arg1; 160082743679SGarrett D'Amore uint8_t sts; 160182743679SGarrett D'Amore mblk_t *mp = NULL; 160282743679SGarrett D'Amore 160382743679SGarrett D'Amore _NOTE(ARGUNUSED(arg2)); 160482743679SGarrett D'Amore 160582743679SGarrett D'Amore mutex_enter(&ip->rulock); 160682743679SGarrett D'Amore if (ip->suspended) { 160782743679SGarrett D'Amore mutex_exit(&ip->rulock); 160882743679SGarrett D'Amore return (DDI_INTR_UNCLAIMED); 160982743679SGarrett D'Amore } 161082743679SGarrett D'Amore sts = GET8(ip, CSR_STS); 161182743679SGarrett D'Amore if (sts == 0) { 161282743679SGarrett D'Amore /* No interrupt status! */ 161382743679SGarrett D'Amore mutex_exit(&ip->rulock); 161482743679SGarrett D'Amore return (DDI_INTR_UNCLAIMED); 161582743679SGarrett D'Amore } 161682743679SGarrett D'Amore /* acknowledge the interrupts */ 161782743679SGarrett D'Amore PUT8(ip, CSR_STS, sts); 161882743679SGarrett D'Amore 161982743679SGarrett D'Amore if (sts & (STS_RNR | STS_FR)) { 162082743679SGarrett D'Amore mp = iprb_rx(ip); 162182743679SGarrett D'Amore 162282743679SGarrett D'Amore if ((sts & STS_RNR) && 162382743679SGarrett D'Amore ((GET8(ip, CSR_STATE) & STATE_RUS) == STATE_RUS_NORES)) { 162482743679SGarrett D'Amore iprb_rx_init(ip); 162582743679SGarrett D'Amore 162682743679SGarrett D'Amore mutex_enter(&ip->culock); 162782743679SGarrett D'Amore PUT32(ip, CSR_GEN_PTR, ip->rxb[0].paddr); 162882743679SGarrett D'Amore /* wait for the SCB */ 162982743679SGarrett D'Amore (void) iprb_cmd_ready(ip); 163082743679SGarrett D'Amore PUT8(ip, CSR_CMD, RUC_START); 163182743679SGarrett D'Amore (void) GET8(ip, CSR_CMD); /* flush CSR */ 163282743679SGarrett D'Amore mutex_exit(&ip->culock); 163382743679SGarrett D'Amore } 163482743679SGarrett D'Amore } 163582743679SGarrett D'Amore mutex_exit(&ip->rulock); 163682743679SGarrett D'Amore 163782743679SGarrett D'Amore if (mp) { 163882743679SGarrett D'Amore mac_rx(ip->mach, NULL, mp); 163982743679SGarrett D'Amore } 164082743679SGarrett D'Amore if ((sts & (STS_CNA | STS_CX)) && ip->wantw) { 164182743679SGarrett D'Amore ip->wantw = B_FALSE; 164282743679SGarrett D'Amore mac_tx_update(ip->mach); 164382743679SGarrett D'Amore } 164482743679SGarrett D'Amore return (DDI_INTR_CLAIMED); 164582743679SGarrett D'Amore } 164682743679SGarrett D'Amore 164782743679SGarrett D'Amore void 164882743679SGarrett D'Amore iprb_periodic(void *arg) 164982743679SGarrett D'Amore { 165082743679SGarrett D'Amore iprb_t *ip = arg; 165182743679SGarrett D'Amore boolean_t reset = B_FALSE; 165282743679SGarrett D'Amore 165382743679SGarrett D'Amore mutex_enter(&ip->rulock); 165482743679SGarrett D'Amore if (ip->suspended || !ip->running) { 165582743679SGarrett D'Amore mutex_exit(&ip->rulock); 165682743679SGarrett D'Amore return; 165782743679SGarrett D'Amore } 165882743679SGarrett D'Amore 165982743679SGarrett D'Amore /* 166082743679SGarrett D'Amore * If we haven't received a packet in a while, and if the link 166182743679SGarrett D'Amore * is up, then it might be a hung chip. This problem 166282743679SGarrett D'Amore * reportedly only occurs at 10 Mbps. 166382743679SGarrett D'Amore */ 166482743679SGarrett D'Amore if (ip->rxhangbug && 166582743679SGarrett D'Amore ((ip->miih == NULL) || (mii_get_speed(ip->miih) == 10000000)) && 1666*0529d5c6SJosef 'Jeff' Sipek ((gethrtime() - ip->rx_wdog) > ip->rx_timeout)) { 166782743679SGarrett D'Amore cmn_err(CE_CONT, "?Possible RU hang, resetting.\n"); 166882743679SGarrett D'Amore reset = B_TRUE; 166982743679SGarrett D'Amore } 167082743679SGarrett D'Amore 167182743679SGarrett D'Amore /* update the statistics */ 167282743679SGarrett D'Amore mutex_enter(&ip->culock); 167382743679SGarrett D'Amore 1674*0529d5c6SJosef 'Jeff' Sipek if (ip->tx_wdog && ((gethrtime() - ip->tx_wdog) > ip->tx_timeout)) { 167582743679SGarrett D'Amore /* transmit/CU hang? */ 167682743679SGarrett D'Amore cmn_err(CE_CONT, "?CU stalled, resetting.\n"); 167782743679SGarrett D'Amore reset = B_TRUE; 167882743679SGarrett D'Amore } 167982743679SGarrett D'Amore 168082743679SGarrett D'Amore if (reset) { 168182743679SGarrett D'Amore /* We want to reconfigure */ 168282743679SGarrett D'Amore iprb_stop(ip); 168382743679SGarrett D'Amore if (iprb_start(ip) != DDI_SUCCESS) { 168482743679SGarrett D'Amore iprb_error(ip, "unable to restart chip"); 168582743679SGarrett D'Amore } 168682743679SGarrett D'Amore } 168782743679SGarrett D'Amore 168882743679SGarrett D'Amore iprb_update_stats(ip); 168982743679SGarrett D'Amore 169082743679SGarrett D'Amore mutex_exit(&ip->culock); 169182743679SGarrett D'Amore mutex_exit(&ip->rulock); 169282743679SGarrett D'Amore } 169382743679SGarrett D'Amore 169482743679SGarrett D'Amore int 169582743679SGarrett D'Amore iprb_quiesce(dev_info_t *dip) 169682743679SGarrett D'Amore { 169782743679SGarrett D'Amore iprb_t *ip = ddi_get_driver_private(dip); 169882743679SGarrett D'Amore 169982743679SGarrett D'Amore /* Reset, but first go into idle state */ 170082743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SEL_RESET); 170182743679SGarrett D'Amore drv_usecwait(50); 170282743679SGarrett D'Amore PUT32(ip, CSR_PORT, PORT_SW_RESET); 170382743679SGarrett D'Amore drv_usecwait(10); 170482743679SGarrett D'Amore PUT8(ip, CSR_INTCTL, INTCTL_MASK); 170582743679SGarrett D'Amore 170682743679SGarrett D'Amore return (DDI_SUCCESS); 170782743679SGarrett D'Amore } 170882743679SGarrett D'Amore 170982743679SGarrett D'Amore int 171082743679SGarrett D'Amore iprb_suspend(dev_info_t *dip) 171182743679SGarrett D'Amore { 171282743679SGarrett D'Amore iprb_t *ip = ddi_get_driver_private(dip); 171382743679SGarrett D'Amore 171482743679SGarrett D'Amore if (ip->miih) 171582743679SGarrett D'Amore mii_suspend(ip->miih); 171682743679SGarrett D'Amore 171782743679SGarrett D'Amore mutex_enter(&ip->rulock); 171882743679SGarrett D'Amore mutex_enter(&ip->culock); 171982743679SGarrett D'Amore if (!ip->suspended) { 172082743679SGarrett D'Amore ip->suspended = B_TRUE; 172182743679SGarrett D'Amore if (ip->running) { 172282743679SGarrett D'Amore iprb_update_stats(ip); 172382743679SGarrett D'Amore iprb_stop(ip); 172482743679SGarrett D'Amore } 172582743679SGarrett D'Amore } 172682743679SGarrett D'Amore mutex_exit(&ip->culock); 172782743679SGarrett D'Amore mutex_exit(&ip->rulock); 172882743679SGarrett D'Amore return (DDI_SUCCESS); 172982743679SGarrett D'Amore } 173082743679SGarrett D'Amore 173182743679SGarrett D'Amore int 173282743679SGarrett D'Amore iprb_resume(dev_info_t *dip) 173382743679SGarrett D'Amore { 173482743679SGarrett D'Amore iprb_t *ip = ddi_get_driver_private(dip); 173582743679SGarrett D'Amore 173682743679SGarrett D'Amore mutex_enter(&ip->rulock); 173782743679SGarrett D'Amore mutex_enter(&ip->culock); 173882743679SGarrett D'Amore 173982743679SGarrett D'Amore ip->suspended = B_FALSE; 174082743679SGarrett D'Amore if (ip->running) { 174182743679SGarrett D'Amore if (iprb_start(ip) != DDI_SUCCESS) { 174282743679SGarrett D'Amore iprb_error(ip, "unable to restart chip!"); 174382743679SGarrett D'Amore ip->suspended = B_TRUE; 174482743679SGarrett D'Amore mutex_exit(&ip->culock); 174582743679SGarrett D'Amore mutex_exit(&ip->rulock); 174682743679SGarrett D'Amore return (DDI_FAILURE); 174782743679SGarrett D'Amore } 174882743679SGarrett D'Amore } 174982743679SGarrett D'Amore 175082743679SGarrett D'Amore mutex_exit(&ip->culock); 175182743679SGarrett D'Amore mutex_exit(&ip->rulock); 175282743679SGarrett D'Amore if (ip->miih) 175382743679SGarrett D'Amore mii_resume(ip->miih); 175482743679SGarrett D'Amore return (DDI_SUCCESS); 175582743679SGarrett D'Amore } 175682743679SGarrett D'Amore 175782743679SGarrett D'Amore int 175882743679SGarrett D'Amore iprb_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 175982743679SGarrett D'Amore { 176082743679SGarrett D'Amore switch (cmd) { 176182743679SGarrett D'Amore case DDI_ATTACH: 176282743679SGarrett D'Amore return (iprb_attach(dip)); 176382743679SGarrett D'Amore 176482743679SGarrett D'Amore case DDI_RESUME: 176582743679SGarrett D'Amore return (iprb_resume(dip)); 176682743679SGarrett D'Amore 176782743679SGarrett D'Amore default: 176882743679SGarrett D'Amore return (DDI_FAILURE); 176982743679SGarrett D'Amore } 177082743679SGarrett D'Amore } 177182743679SGarrett D'Amore 177282743679SGarrett D'Amore int 177382743679SGarrett D'Amore iprb_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 177482743679SGarrett D'Amore { 177582743679SGarrett D'Amore switch (cmd) { 177682743679SGarrett D'Amore case DDI_DETACH: 177782743679SGarrett D'Amore return (iprb_detach(dip)); 177882743679SGarrett D'Amore 177982743679SGarrett D'Amore case DDI_SUSPEND: 178082743679SGarrett D'Amore return (iprb_suspend(dip)); 178182743679SGarrett D'Amore 178282743679SGarrett D'Amore default: 178382743679SGarrett D'Amore return (DDI_FAILURE); 178482743679SGarrett D'Amore } 178582743679SGarrett D'Amore } 178682743679SGarrett D'Amore 178782743679SGarrett D'Amore void 178882743679SGarrett D'Amore iprb_error(iprb_t *ip, const char *fmt, ...) 178982743679SGarrett D'Amore { 179082743679SGarrett D'Amore va_list ap; 179182743679SGarrett D'Amore char buf[256]; 179282743679SGarrett D'Amore 179382743679SGarrett D'Amore va_start(ap, fmt); 179482743679SGarrett D'Amore (void) vsnprintf(buf, sizeof (buf), fmt, ap); 179582743679SGarrett D'Amore va_end(ap); 179682743679SGarrett D'Amore 179782743679SGarrett D'Amore cmn_err(CE_WARN, "%s%d: %s", 179882743679SGarrett D'Amore ddi_driver_name(ip->dip), ddi_get_instance(ip->dip), buf); 179982743679SGarrett D'Amore } 1800