82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * This file and its contents are supplied under the terms of the
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Common Development and Distribution License ("CDDL"), version 1.0.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * You may only use this file in accordance with the terms of version
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * 1.0 of the CDDL.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * A full copy of the text of the CDDL should have accompanied this
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * source. A copy of the CDDL is also available via the Internet at
197c9523b8946cf70fab2bc4ee633b18fc5bde68Marcel Telka * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Intel Pro/100B Ethernet Driver
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Intel has openly documented the programming interface for these
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * parts in the "Intel 8255x 10/100 Mbps Ethernet Controller Family
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Open Source Software Developer Manual".
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * While some open source systems have utilized many of the features
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * of some models in this family (especially scatter gather and IP
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * checksum support), we have elected to offer only the basic
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * functionality. These are only 10/100 parts, and the additional
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * complexity is not justified by the minimal performance benefit.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * KISS. So, we are only supporting the simple 82557 features.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic uint16_t iprb_mii_read(void *, uint8_t, uint8_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_mii_write(void *, uint8_t, uint8_t, uint16_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_mii_notify(void *, link_state_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_stat(void *, uint_t, uint64_t *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_start(void *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_m_stop(void *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_promisc(void *, boolean_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_multicst(void *, boolean_t, const uint8_t *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_unicst(void *, const uint8_t *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_m_ioctl(void *, queue_t *, mblk_t *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore const void *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_m_propinfo(void *, const char *, mac_prop_id_t,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_eeprom_sendbits(iprb_t *, uint32_t, uint8_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic uint16_t iprb_eeprom_read(iprb_t *, uint16_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_cmd_submit(iprb_t *, uint16_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic mblk_t *iprb_send(iprb_t *, mblk_t *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_periodic(void *);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_dma_alloc(iprb_t *, iprb_dma_t *, size_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic int iprb_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic void iprb_error(iprb_t *, const char *, ...);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Stream information
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'AmoreDDI_DEFINE_STREAM_OPS(iprb_devops, nulldev, nulldev,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_ddi_attach, iprb_ddi_detach, nodev, NULL, D_MP, NULL, iprb_quiesce);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * The 8225x is a 32-bit addressing engine, but it can only address up
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * to 31 bits on a single transaction. (Far less in reality it turns
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * out.) Statistics buffers have to be 16-byte aligned, and as we
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * allocate individual data pieces for other things, there is no
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * compelling reason to use another attribute with support for less
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * strict alignment.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore 0, /* dma_attr_addr_lo */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore 0 /* dma_attr_flags */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore static const uint32_t x ## _WORDS[] = x ## _RCVBUNDLE_UCODE
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amorestatic uint8_t iprb_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * We don't bother allowing for tuning of the CPU saver algorithm.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * The ucode has reasonable defaults built-in. However, some variants
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * apparently have bug fixes delivered via this ucode, so we still
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * need to support the ucode upload.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoretypedef struct {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore sizeof (x ## _WORDS) / sizeof (uint32_t), x ## _WORDS
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((rv = mod_install(&iprb_modlinkage)) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((rv = mod_remove(&iprb_modlinkage)) == DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore return (mod_info(&iprb_modlinkage, modinfop));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore list_create(&ip->mcast, sizeof (struct iprb_mcast),
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* we don't support high level interrupts, so we don't need cookies */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore mutex_init(&ip->culock, NULL, MUTEX_DRIVER, NULL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore mutex_init(&ip->rulock, NULL, MUTEX_DRIVER, NULL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (pci_config_setup(dip, &ip->pcih) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to map configuration space");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_regs_map_setup(dip, 1, &ip->regs, 0, 0, &acc_attr,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to map device registers");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Reset, but first go into idle state */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Precalculate watchdog times.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Obtain our factory MAC address */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Generally, most devices we will ever see will
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * already have fixed firmware. Since I can't verify
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * the validity of the fix (no suitably downrev
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * hardware), we'll just do our best to avoid it for
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * devices that exhibit this behavior.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((iprb_eeprom_read(ip, 10) & 0x02) == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* EEPROM fix was already applied, assume safe. */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((iprb_eeprom_read(ip, 3) & 0x3) != 0x3) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore cmn_err(CE_CONT, "?Enabling RX errata workaround.\n");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Determine whether we have an MII or a legacy 80c24 */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((ip->miih = mii_alloc(ip, dip, &iprb_mii_ops)) == NULL) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to allocate MII ops vector");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore mii_set_pauseable(ip->miih, B_TRUE, B_FALSE);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Allocate cmds and tx region */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_TX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Command blocks */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (iprb_dma_alloc(ip, &ip->cmds[i], CB_SIZE) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_TX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Link the command blocks into a ring */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB32(cb, CB_LNK_OFFSET, (ip->cmds[(i + 1) % NUM_TX].paddr));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_RX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Rx packet buffers */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (iprb_dma_alloc(ip, &ip->rxb[i], RFD_SIZE) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (iprb_dma_alloc(ip, &ip->stats, STATS_SIZE) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to allocate mac structure");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to register mac with framework");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_intr_alloc(ip->dip, &ip->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "failed allocating interrupt handle");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_intr_add_handler(ip->intrh, iprb_intr, ip, NULL) !=
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "failed adding interrupt handler");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_intr_enable(ip->intrh) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "failed enabling interrupt");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_dma_alloc(iprb_t *ip, iprb_dma_t *h, size_t size)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_dma_alloc_handle(ip->dip, &dma_attr, DDI_DMA_SLEEP, NULL,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to allocate dma handle");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_dma_mem_alloc(h->dmah, size, &buf_attr, DDI_DMA_CONSISTENT,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore DDI_DMA_SLEEP, NULL, &h->vaddr, &rlen, &h->acch) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to allocate dma memory");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_dma_addr_bind_handle(h->dmah, NULL, h->vaddr, size,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore DDI_DMA_CONSISTENT | DDI_DMA_RDWR, DDI_DMA_SLEEP, NULL,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "unable to map command memory");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* shut down interrupts */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* release DMA resources */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_TX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_RX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* clean up the multicast list */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore while ((mc = list_head(&ip->mcast)) != NULL) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* tear down register mappings */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* clean the dip */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* and finally toss the structure itself */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->devid = pci_config_get16(ip->pcih, PCI_CONF_DEVID);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->revid = pci_config_get8(ip->pcih, PCI_CONF_REVID);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Allow property override MWI support - not normally needed. */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ddi_prop_get_int(DDI_DEV_T_ANY, ip->dip, 0, "MWIEnable", 1) == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_eeprom_sendbits(iprb_t *ip, uint32_t val, uint8_t nbits)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_eeprom_read(iprb_t *ip, uint16_t address)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* if we don't know the address size yet call again to determine it */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((address != 0) && (ip->eeprom_bits == 0))
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* enable the EEPROM chip select */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* send a read command */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (mask = (1U << (bits - 1)); mask != 0; mask >>= 1) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore uint16_t x = (mask & address) ? EEPROM_EEDI : 0;
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* check the dummy 0 bit */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((GET16(ip, CSR_EECTL) & EEPROM_EEDO) == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore cmn_err(CE_CONT, "?EEPROM size %d words.\n",
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "cannot determine EEPROM size (%d, %d)",
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* shift out a 16-bit word */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUT16(ip, CSR_EECTL, EEPROM_EECS | EEPROM_EESK);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* and disable the eeprom */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* wait for pending SCB commands to be accepted */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (int cnt = 1000000; cnt != 0; cnt -= 10) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "timeout waiting for chip to become ready");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCCB(cb, CB_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((GETCB16(cb, CB_STS_OFFSET) & CB_STS_C) == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "time out waiting for commands to drain");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* If this command will consume the last CB, interrupt when done */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* clear the status entry */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* suspend upon completion of this new command */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* clear the suspend flag from the last submitted command */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB16(lcb, CB_CMD_OFFSET, GETCB16(lcb, CB_CMD_OFFSET) & ~CB_CMD_S);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORDEV);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * If the chip has a resume bug, then we need to try this as a work
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * around. Some anecdotal evidence is that this will help solve
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * the resume bug. Its a performance hit, but only if the EEPROM
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * is not updated. (In theory we could do this only for 10Mbps HDX,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * but since it should just about never get used, we keep it simple.)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* wait for the SCB to be ready to accept a new command */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Finally we can resume the CU. Note that if this the first
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * command in the sequence (i.e. if the CU is IDLE), or if the
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * CU is already busy working, then this CU resume command
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * will not have any effect.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCBEA(cb, CB_IAS_ADR_OFFSET, ip->curraddr);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((ip->nmcast <= 0) || (ip->nmcast > CB_MCS_CNT_MAX)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Only send the list if the total number of multicast
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * address is nonzero and small enough to fit. We
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * don't error out if it is too big, because in that
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * case we will use the "allmulticast" support
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * via iprb_set_config instead.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (mc = list_head(l), i = 0; mc; mc = list_next(l, mc), i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCBEA(cb, CB_MCS_ADR_OFFSET + (i * 6), mc->addr);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 3, (ip->canmwi ? 1 : 0));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 6, (ip->promisc ? 0x80 : 0) | 0x3a);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 7, (ip->promisc ? 0 : 0x1) | 2);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 8, (ip->miih ? 0x1 : 0));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 12, (ip->is557 ? 0 : 1) | 0x60);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore (ip->miih ? 0x80 : 0) | (ip->promisc ? 0x1 : 0) | 0x48);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 17, (ip->canpause ? 0x40 : 0));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_CONFIG_OFFSET + 18, (ip->is557 ? 0 : 0x8) | 0xf2);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ((ip->nmcast >= CB_MCS_CNT_MAX) ? 0x8 : 0) | 0x5);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore return (iprb_cmd_submit(ip, CB_CMD_CONFIG));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* no matching firmware found, assume success */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB32(cb, (CB_UCODE_OFFSET + i * 4), uc->ucode[i]);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* go idle */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* shut off device interrupts */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Reset, but first go into idle state */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Reset pointers */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Send a NOP. This will be the first command seen by the device. */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (iprb_cmd_submit(ip, CB_CMD_NOP) != DDI_SUCCESS)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* as that was the first command, go ahead and submit a CU start */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Upload firmware. */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Set up RFDs */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* wait for the SCB */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Enable device interrupts */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Collect the hardware stats, but don't keep redoing it */
0529d5c654f682ce87e4f74affd1c83c429c50e1Josef 'Jeff' Sipek if (tstamp / NANOSEC == ip->stats_time / NANOSEC)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCSTATS(sp, 0, 0, DDI_DMA_SYNC_FORKERNEL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (GETSTAT(sp, STATS_DONE_OFFSET) == STATS_RST_DONE) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* yay stats are updated */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (i == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore iprb_error(ip, "time out acquiring hardware statistics");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->ex_coll += GETSTAT(sp, STATS_TX_MAXCOL_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->late_coll += GETSTAT(sp, STATS_TX_LATECOL_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->uflo += GETSTAT(sp, STATS_TX_UFLO_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->defer_xmt += GETSTAT(sp, STATS_TX_DEFER_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->one_coll += GETSTAT(sp, STATS_TX_ONECOL_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->multi_coll += GETSTAT(sp, STATS_TX_MULTCOL_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->collisions += GETSTAT(sp, STATS_TX_TOTCOL_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->fcs_errs += GETSTAT(sp, STATS_RX_FCS_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->align_errs += GETSTAT(sp, STATS_RX_ALIGN_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->norcvbuf += GETSTAT(sp, STATS_RX_NOBUF_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->oflo += GETSTAT(sp, STATS_RX_OFLO_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->runt += GETSTAT(sp, STATS_RX_SHORT_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* possibly reclaim some CBs */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* flow control */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((sz = msgsize(mp)) > (ETHERMAX + VLAN_TAGSZ)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Generally this should never occur */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB16(cb, CB_TX_COUNT_OFFSET, (sz & 0x3fff) | CB_TX_EOF);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTCB8(cb, CB_TX_THRESH_OFFSET, (sz / 8) & 0xff);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore mcopymsg(mp, cb->vaddr + CB_TX_DATA_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (bcmp(cb->vaddr + CB_TX_DATA_OFFSET, &iprb_bcast, 6) != 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCCB(cb, 0, CB_TX_DATA_OFFSET + sz, DDI_DMA_SYNC_FORDEV);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (iprb_cmd_submit(ip, CB_CMD_TX) != DDI_SUCCESS) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore PUTRFD16(rfd, RFD_SIZ_OFFSET, RFD_SIZE - RFD_PKT_OFFSET);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCRFD(rfd, 0, RFD_PKT_OFFSET, DDI_DMA_SYNC_FORDEV);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* clear the suspend & EL bits from the previous RFD */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCRFD(rfd, RFD_CTL_OFFSET, 2, DDI_DMA_SYNC_FORDEV);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (int i = 0; i < NUM_RX; i++)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (i = 0; i < NUM_RX; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore SYNCRFD(rfd, RFD_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((GETRFD16(rfd, RFD_STS_OFFSET) & RFD_STS_C) == 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (((sts & RFD_STS_OK) == 0) && (sts & RFD_STS_ERRS)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore bcopy(rfd->vaddr + RFD_PKT_OFFSET, mp->b_wptr, cnt);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* return it to the RFD list */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (bcmp(mp->b_rptr, &iprb_bcast, 6) != 0) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_unicst(void *arg, const uint8_t *macaddr)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (mc = list_head(l); mc != NULL; mc = list_next(l, mc)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ip->perh = ddi_periodic_add(iprb_periodic, ip, 5000000000, 0);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* might be a lie. */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_stat(void *arg, uint_t stat, uint64_t *val)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if (ip->miih && (mii_m_getstat(ip->miih, stat, val) == 0)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore *val = ip->one_coll + ip->multi_coll + ip->ex_coll;
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore *val = ip->nocarrier; /* reported only for "suspend" */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_propinfo(void *arg, const char *name, mac_prop_id_t id,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore mac_prop_info_set_perm(pih, MAC_PROP_PERM_READ);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore return (mii_m_getprop(ip->miih, name, id, sz, val));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore x = 10000000;
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz,
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore return (mii_m_setprop(ip->miih, name, id, sz, val));
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((ip->miih != NULL) && (mii_m_loop_ioctl(ip->miih, wq, mp)))
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_mii_read(void *arg, uint8_t phy, uint8_t reg)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * NB: we are guaranteed by the MII layer not to be suspended.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * Furthermore, we have an independent MII register.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (int i = 0; i < 100; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore return (0xffff);
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore for (int i = 0; i < 100; i++) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_mii_notify(void *arg, link_state_t link)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* No interrupt status! */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* acknowledge the interrupts */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ((GET8(ip, CSR_STATE) & STATE_RUS) == STATE_RUS_NORES)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* wait for the SCB */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore if ((sts & (STS_CNA | STS_CX)) && ip->wantw) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * If we haven't received a packet in a while, and if the link
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * is up, then it might be a hung chip. This problem
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore * reportedly only occurs at 10 Mbps.
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore ((ip->miih == NULL) || (mii_get_speed(ip->miih) == 10000000)) &&
0529d5c654f682ce87e4f74affd1c83c429c50e1Josef 'Jeff' Sipek ((gethrtime() - ip->rx_wdog) > ip->rx_timeout)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore cmn_err(CE_CONT, "?Possible RU hang, resetting.\n");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* update the statistics */
0529d5c654f682ce87e4f74affd1c83c429c50e1Josef 'Jeff' Sipek if (ip->tx_wdog && ((gethrtime() - ip->tx_wdog) > ip->tx_timeout)) {
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore cmn_err(CE_CONT, "?CU stalled, resetting.\n");
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* We want to reconfigure */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore /* Reset, but first go into idle state */
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amoreiprb_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
82743679557cf8b7a5dd51eaa0015e0ca498ac37Garrett D'Amore (void) vsnprintf(buf, sizeof (buf), fmt, ap);