/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 NetXen, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "unm_nic_hw.h" #include "unm_nic.h" #include "nic_phan_reg.h" #include "unm_nic_ioctl.h" #include "nic_cmn.h" #include "unm_version.h" #include "unm_brdcfg.h" #if defined(lint) #undef MBLKL #define MBLKL(_mp_) ((uintptr_t)(_mp_)->b_wptr - (uintptr_t)(_mp_)->b_rptr) #endif /* lint */ #undef UNM_LOOPBACK #undef SINGLE_DMA_BUF #define UNM_ADAPTER_UP_MAGIC 777 #define VLAN_TAGSZ 0x4 #define index2rxbuf(_rdp_, _idx_) ((_rdp_)->rx_buf_pool + (_idx_)) #define rxbuf2index(_rdp_, _bufp_) ((_bufp_) - (_rdp_)->rx_buf_pool) /* * Receive ISR processes NX_RX_MAXBUFS incoming packets at most, then posts * as many buffers as packets processed. This loop repeats as required to * process all incoming packets delivered in a single interrupt. Higher * value of NX_RX_MAXBUFS improves performance by posting rx buffers less * frequently, but at the cost of not posting quickly enough when card is * running out of rx buffers. */ #define NX_RX_THRESHOLD 32 #define NX_RX_MAXBUFS 128 #define NX_MAX_TXCOMPS 256 extern void unm_free_tx_buffers(unm_adapter *adapter); extern void unm_free_tx_dmahdl(unm_adapter *adapter); extern void unm_destroy_rx_ring(unm_rcv_desc_ctx_t *rcv_desc); static void unm_post_rx_buffers_nodb(struct unm_adapter_s *adapter, uint32_t ringid); static mblk_t *unm_process_rcv(unm_adapter *adapter, statusDesc_t *desc); static int unm_process_rcv_ring(unm_adapter *, int); static int unm_process_cmd_ring(struct unm_adapter_s *adapter); static int unm_nic_do_ioctl(unm_adapter *adapter, queue_t *q, mblk_t *mp); static void unm_nic_ioctl(struct unm_adapter_s *adapter, int cmd, queue_t *q, mblk_t *mp); /* GLDv3 interface functions */ static int ntxn_m_start(void *); static void ntxn_m_stop(void *); static int ntxn_m_multicst(void *, boolean_t, const uint8_t *); static int ntxn_m_promisc(void *, boolean_t); static int ntxn_m_stat(void *arg, uint_t stat, uint64_t *val); static mblk_t *ntxn_m_tx(void *, mblk_t *); static void ntxn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); static boolean_t ntxn_m_getcapab(void *arg, mac_capab_t cap, void *cap_data); /* * Allocates DMA handle, virtual memory and binds them * returns size of actual memory binded and the physical address. */ int unm_pci_alloc_consistent(unm_adapter *adapter, int size, caddr_t *address, ddi_dma_cookie_t *cookie, ddi_dma_handle_t *dma_handle, ddi_acc_handle_t *handlep) { int err; uint32_t ncookies; size_t ring_len; uint_t dma_flags = DDI_DMA_RDWR | DDI_DMA_CONSISTENT; *dma_handle = NULL; if (size <= 0) return (DDI_ENOMEM); err = ddi_dma_alloc_handle(adapter->dip, &adapter->gc_dma_attr_desc, DDI_DMA_DONTWAIT, NULL, dma_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "!%s: %s: ddi_dma_alloc_handle FAILED:" " %d", unm_nic_driver_name, __func__, err); return (DDI_ENOMEM); } err = ddi_dma_mem_alloc(*dma_handle, size, &adapter->gc_attr_desc, dma_flags & (DDI_DMA_STREAMING | DDI_DMA_CONSISTENT), DDI_DMA_DONTWAIT, NULL, address, &ring_len, handlep); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "!%s: %s: ddi_dma_mem_alloc failed:" "ret %d, request size: %d", unm_nic_driver_name, __func__, err, size); ddi_dma_free_handle(dma_handle); return (DDI_ENOMEM); } if (ring_len < size) { cmn_err(CE_WARN, "%s: %s: could not allocate required " "memory :%d\n", unm_nic_driver_name, __func__, err); ddi_dma_mem_free(handlep); ddi_dma_free_handle(dma_handle); return (DDI_FAILURE); } (void) memset(*address, 0, size); if (((err = ddi_dma_addr_bind_handle(*dma_handle, NULL, *address, ring_len, dma_flags, DDI_DMA_DONTWAIT, NULL, cookie, &ncookies)) != DDI_DMA_MAPPED) || (ncookies != 1)) { cmn_err(CE_WARN, "!%s: %s: ddi_dma_addr_bind_handle FAILED: %d", unm_nic_driver_name, __func__, err); ddi_dma_mem_free(handlep); ddi_dma_free_handle(dma_handle); return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * Unbinds the memory, frees the DMA handle and at the end, frees the memory */ void unm_pci_free_consistent(ddi_dma_handle_t *dma_handle, ddi_acc_handle_t *acc_handle) { int err; err = ddi_dma_unbind_handle(*dma_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: Error unbinding memory\n", __func__); return; } ddi_dma_mem_free(acc_handle); ddi_dma_free_handle(dma_handle); } static uint32_t msi_tgt_status[] = { ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1, ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3, ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5, ISR_INT_TARGET_STATUS_F6, ISR_INT_TARGET_STATUS_F7 }; static void unm_nic_disable_int(unm_adapter *adapter) { __uint32_t temp = 0; adapter->unm_nic_hw_write_wx(adapter, adapter->interrupt_crb, &temp, 4); } static int unm_nic_clear_int(unm_adapter *adapter) { uint32_t mask, temp, our_int, status; UNM_READ_LOCK(&adapter->adapter_lock); /* check whether it's our interrupt */ if (!UNM_IS_MSI_FAMILY(adapter)) { /* Legacy Interrupt case */ adapter->unm_nic_pci_read_immediate(adapter, ISR_INT_VECTOR, &status); if (!(status & adapter->legacy_intr.int_vec_bit)) { UNM_READ_UNLOCK(&adapter->adapter_lock); return (-1); } if (adapter->ahw.revision_id >= NX_P3_B1) { adapter->unm_nic_pci_read_immediate(adapter, ISR_INT_STATE_REG, &temp); if (!ISR_IS_LEGACY_INTR_TRIGGERED(temp)) { UNM_READ_UNLOCK(&adapter->adapter_lock); return (-1); } } else if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { our_int = adapter->unm_nic_pci_read_normalize(adapter, CRB_INT_VECTOR); /* FIXME: Assumes pci_func is same as ctx */ if ((our_int & (0x80 << adapter->portnum)) == 0) { if (our_int != 0) { /* not our interrupt */ UNM_READ_UNLOCK(&adapter->adapter_lock); return (-1); } } temp = our_int & ~((u32)(0x80 << adapter->portnum)); adapter->unm_nic_pci_write_normalize(adapter, CRB_INT_VECTOR, temp); } if (adapter->fw_major < 4) unm_nic_disable_int(adapter); /* claim interrupt */ temp = 0xffffffff; adapter->unm_nic_pci_write_immediate(adapter, adapter->legacy_intr.tgt_status_reg, &temp); adapter->unm_nic_pci_read_immediate(adapter, ISR_INT_VECTOR, &mask); /* * Read again to make sure the legacy interrupt message got * flushed out */ adapter->unm_nic_pci_read_immediate(adapter, ISR_INT_VECTOR, &mask); } else if (adapter->flags & UNM_NIC_MSI_ENABLED) { /* clear interrupt */ temp = 0xffffffff; adapter->unm_nic_pci_write_immediate(adapter, msi_tgt_status[adapter->ahw.pci_func], &temp); } UNM_READ_UNLOCK(&adapter->adapter_lock); return (0); } static void unm_nic_enable_int(unm_adapter *adapter) { u32 temp = 1; adapter->unm_nic_hw_write_wx(adapter, adapter->interrupt_crb, &temp, 4); if (!UNM_IS_MSI_FAMILY(adapter)) { u32 mask = 0xfbff; adapter->unm_nic_pci_write_immediate(adapter, adapter->legacy_intr.tgt_mask_reg, &mask); } } static void unm_free_hw_resources(unm_adapter *adapter) { unm_recv_context_t *recv_ctx; unm_rcv_desc_ctx_t *rcv_desc; int ctx, ring; if (adapter->context_alloced == 1) { netxen_destroy_rxtx(adapter); adapter->context_alloced = 0; } if (adapter->ctxDesc != NULL) { unm_pci_free_consistent(&adapter->ctxDesc_dma_handle, &adapter->ctxDesc_acc_handle); adapter->ctxDesc = NULL; } if (adapter->ahw.cmdDescHead != NULL) { unm_pci_free_consistent(&adapter->ahw.cmd_desc_dma_handle, &adapter->ahw.cmd_desc_acc_handle); adapter->ahw.cmdDesc_physAddr = NULL; adapter->ahw.cmdDescHead = NULL; } for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { recv_ctx = &adapter->recv_ctx[ctx]; for (ring = 0; ring < adapter->max_rds_rings; ring++) { rcv_desc = &recv_ctx->rcv_desc[ring]; if (rcv_desc->desc_head != NULL) { unm_pci_free_consistent( &rcv_desc->rx_desc_dma_handle, &rcv_desc->rx_desc_acc_handle); rcv_desc->desc_head = NULL; rcv_desc->phys_addr = NULL; } } if (recv_ctx->rcvStatusDescHead != NULL) { unm_pci_free_consistent( &recv_ctx->status_desc_dma_handle, &recv_ctx->status_desc_acc_handle); recv_ctx->rcvStatusDesc_physAddr = NULL; recv_ctx->rcvStatusDescHead = NULL; } } } static void cleanup_adapter(struct unm_adapter_s *adapter) { if (adapter->cmd_buf_arr != NULL) kmem_free(adapter->cmd_buf_arr, sizeof (struct unm_cmd_buffer) * adapter->MaxTxDescCount); ddi_regs_map_free(&(adapter->regs_handle)); ddi_regs_map_free(&(adapter->db_handle)); kmem_free(adapter, sizeof (unm_adapter)); } void unm_nic_remove(unm_adapter *adapter) { unm_recv_context_t *recv_ctx; unm_rcv_desc_ctx_t *rcv_desc; int ctx, ring; mac_link_update(adapter->mach, LINK_STATE_DOWN); unm_nic_stop_port(adapter); if (adapter->interrupt_crb) { UNM_READ_LOCK(&adapter->adapter_lock); unm_nic_disable_int(adapter); UNM_READ_UNLOCK(&adapter->adapter_lock); } (void) untimeout(adapter->watchdog_timer); unm_free_hw_resources(adapter); unm_free_tx_buffers(adapter); unm_free_tx_dmahdl(adapter); for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { recv_ctx = &adapter->recv_ctx[ctx]; for (ring = 0; ring < adapter->max_rds_rings; ring++) { rcv_desc = &recv_ctx->rcv_desc[ring]; if (rcv_desc->rx_buf_pool != NULL) unm_destroy_rx_ring(rcv_desc); } } if (adapter->portnum == 0) unm_free_dummy_dma(adapter); unm_destroy_intr(adapter); ddi_set_driver_private(adapter->dip, NULL); cleanup_adapter(adapter); } static int init_firmware(unm_adapter *adapter) { uint32_t state = 0, loops = 0, tempout; /* Window 1 call */ UNM_READ_LOCK(&adapter->adapter_lock); state = adapter->unm_nic_pci_read_normalize(adapter, CRB_CMDPEG_STATE); UNM_READ_UNLOCK(&adapter->adapter_lock); if (state == PHAN_INITIALIZE_ACK) return (0); while (state != PHAN_INITIALIZE_COMPLETE && loops < 200000) { drv_usecwait(100); /* Window 1 call */ UNM_READ_LOCK(&adapter->adapter_lock); state = adapter->unm_nic_pci_read_normalize(adapter, CRB_CMDPEG_STATE); UNM_READ_UNLOCK(&adapter->adapter_lock); loops++; } if (loops >= 200000) { cmn_err(CE_WARN, "%s%d: CmdPeg init incomplete:%x\n", adapter->name, adapter->instance, state); return (-EIO); } /* Window 1 call */ UNM_READ_LOCK(&adapter->adapter_lock); tempout = INTR_SCHEME_PERPORT; adapter->unm_nic_hw_write_wx(adapter, CRB_NIC_CAPABILITIES_HOST, &tempout, 4); tempout = MSI_MODE_MULTIFUNC; adapter->unm_nic_hw_write_wx(adapter, CRB_NIC_MSI_MODE_HOST, &tempout, 4); tempout = MPORT_MULTI_FUNCTION_MODE; adapter->unm_nic_hw_write_wx(adapter, CRB_MPORT_MODE, &tempout, 4); tempout = PHAN_INITIALIZE_ACK; adapter->unm_nic_hw_write_wx(adapter, CRB_CMDPEG_STATE, &tempout, 4); UNM_READ_UNLOCK(&adapter->adapter_lock); return (0); } /* * Utility to synchronize with receive peg. * Returns 0 on sucess * -EIO on error */ int receive_peg_ready(struct unm_adapter_s *adapter) { uint32_t state = 0; int loops = 0, err = 0; /* Window 1 call */ UNM_READ_LOCK(&adapter->adapter_lock); state = adapter->unm_nic_pci_read_normalize(adapter, CRB_RCVPEG_STATE); UNM_READ_UNLOCK(&adapter->adapter_lock); while ((state != PHAN_PEG_RCV_INITIALIZED) && (loops < 20000)) { drv_usecwait(100); /* Window 1 call */ UNM_READ_LOCK(&adapter->adapter_lock); state = adapter->unm_nic_pci_read_normalize(adapter, CRB_RCVPEG_STATE); UNM_READ_UNLOCK(&adapter->adapter_lock); loops++; } if (loops >= 20000) { cmn_err(CE_WARN, "Receive Peg initialization incomplete 0x%x\n", state); err = -EIO; } return (err); } /* * check if the firmware has been downloaded and ready to run and * setup the address for the descriptors in the adapter */ static int unm_nic_hw_resources(unm_adapter *adapter) { hardware_context *hw = &adapter->ahw; void *addr; int err; int ctx, ring; unm_recv_context_t *recv_ctx; unm_rcv_desc_ctx_t *rcv_desc; ddi_dma_cookie_t cookie; int size; if (err = receive_peg_ready(adapter)) return (err); size = (sizeof (RingContext) + sizeof (uint32_t)); err = unm_pci_alloc_consistent(adapter, size, (caddr_t *)&addr, &cookie, &adapter->ctxDesc_dma_handle, &adapter->ctxDesc_acc_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "Failed to allocate HW context\n"); return (err); } adapter->ctxDesc_physAddr = cookie.dmac_laddress; (void) memset(addr, 0, sizeof (RingContext)); adapter->ctxDesc = (RingContext *) addr; adapter->ctxDesc->CtxId = adapter->portnum; adapter->ctxDesc->CMD_CONSUMER_OFFSET = adapter->ctxDesc_physAddr + sizeof (RingContext); adapter->cmdConsumer = (uint32_t *)(uintptr_t)(((char *)addr) + sizeof (RingContext)); ASSERT(!((unsigned long)adapter->ctxDesc_physAddr & 0x3f)); /* * Allocate command descriptor ring. */ size = (sizeof (cmdDescType0_t) * adapter->MaxTxDescCount); err = unm_pci_alloc_consistent(adapter, size, (caddr_t *)&addr, &cookie, &hw->cmd_desc_dma_handle, &hw->cmd_desc_acc_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "Failed to allocate cmd desc ring\n"); return (err); } hw->cmdDesc_physAddr = cookie.dmac_laddress; hw->cmdDescHead = (cmdDescType0_t *)addr; for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { recv_ctx = &adapter->recv_ctx[ctx]; size = (sizeof (statusDesc_t)* adapter->MaxRxDescCount); err = unm_pci_alloc_consistent(adapter, size, (caddr_t *)&addr, &recv_ctx->status_desc_dma_cookie, &recv_ctx->status_desc_dma_handle, &recv_ctx->status_desc_acc_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "Failed to allocate sts desc ring\n"); goto free_cmd_desc; } (void) memset(addr, 0, size); recv_ctx->rcvStatusDesc_physAddr = recv_ctx->status_desc_dma_cookie.dmac_laddress; recv_ctx->rcvStatusDescHead = (statusDesc_t *)addr; /* rds rings */ for (ring = 0; ring < adapter->max_rds_rings; ring++) { rcv_desc = &recv_ctx->rcv_desc[ring]; size = (sizeof (rcvDesc_t) * adapter->MaxRxDescCount); err = unm_pci_alloc_consistent(adapter, size, (caddr_t *)&addr, &rcv_desc->rx_desc_dma_cookie, &rcv_desc->rx_desc_dma_handle, &rcv_desc->rx_desc_acc_handle); if (err != DDI_SUCCESS) { cmn_err(CE_WARN, "Failed to allocate " "rx desc ring %d\n", ring); goto free_status_desc; } rcv_desc->phys_addr = rcv_desc->rx_desc_dma_cookie.dmac_laddress; rcv_desc->desc_head = (rcvDesc_t *)addr; } } if (err = netxen_create_rxtx(adapter)) goto free_statusrx_desc; adapter->context_alloced = 1; return (DDI_SUCCESS); free_statusrx_desc: free_status_desc: free_cmd_desc: unm_free_hw_resources(adapter); return (err); } void unm_desc_dma_sync(ddi_dma_handle_t handle, uint_t start, uint_t count, uint_t range, uint_t unit_size, uint_t direction) { if ((start + count) < range) { (void) ddi_dma_sync(handle, start * unit_size, count * unit_size, direction); } else { (void) ddi_dma_sync(handle, start * unit_size, 0, direction); (void) ddi_dma_sync(handle, 0, (start + count - range) * unit_size, DDI_DMA_SYNC_FORCPU); } } static uint32_t crb_cmd_producer[4] = { CRB_CMD_PRODUCER_OFFSET, CRB_CMD_PRODUCER_OFFSET_1, CRB_CMD_PRODUCER_OFFSET_2, CRB_CMD_PRODUCER_OFFSET_3 }; static uint32_t crb_cmd_consumer[4] = { CRB_CMD_CONSUMER_OFFSET, CRB_CMD_CONSUMER_OFFSET_1, CRB_CMD_CONSUMER_OFFSET_2, CRB_CMD_CONSUMER_OFFSET_3 }; void unm_nic_update_cmd_producer(struct unm_adapter_s *adapter, uint32_t crb_producer) { int data = crb_producer; if (adapter->crb_addr_cmd_producer) { UNM_READ_LOCK(&adapter->adapter_lock); adapter->unm_nic_hw_write_wx(adapter, adapter->crb_addr_cmd_producer, &data, 4); UNM_READ_UNLOCK(&adapter->adapter_lock); } } static void unm_nic_update_cmd_consumer(struct unm_adapter_s *adapter, uint32_t crb_producer) { int data = crb_producer; if (adapter->crb_addr_cmd_consumer) adapter->unm_nic_hw_write_wx(adapter, adapter->crb_addr_cmd_consumer, &data, 4); } /* * Looks for type of packet and sets opcode accordingly * so that checksum offload can be used. */ static void unm_tx_csum(cmdDescType0_t *desc, mblk_t *mp, pktinfo_t *pktinfo) { if (pktinfo->mac_hlen == sizeof (struct ether_vlan_header)) desc->u1.s1.flags = FLAGS_VLAN_TAGGED; if (pktinfo->etype == htons(ETHERTYPE_IP)) { uint32_t start, flags; hcksum_retrieve(mp, NULL, NULL, &start, NULL, NULL, NULL, &flags); if ((flags & (HCK_FULLCKSUM | HCK_IPV4_HDRCKSUM)) == 0) return; /* * For TCP/UDP, ask hardware to do both IP header and * full checksum, even if stack has already done one or * the other. Hardware will always get it correct even * if stack has already done it. */ switch (pktinfo->l4_proto) { case IPPROTO_TCP: desc->u1.s1.opcode = TX_TCP_PKT; break; case IPPROTO_UDP: desc->u1.s1.opcode = TX_UDP_PKT; break; default: /* Must be here with HCK_IPV4_HDRCKSUM */ desc->u1.s1.opcode = TX_IP_PKT; return; } desc->u1.s1.ipHdrOffset = pktinfo->mac_hlen; desc->u1.s1.tcpHdrOffset = pktinfo->mac_hlen + pktinfo->ip_hlen; } } /* * For IP/UDP/TCP checksum offload, this checks for MAC+IP header in one * contiguous block ending at 8 byte aligned address as required by hardware. * Caller assumes pktinfo->total_len will be updated by this function and * if pktinfo->etype is set to 0, it will need to linearize the mblk and * invoke unm_update_pkt_info() to determine ethertype, IP header len and * protocol. */ static boolean_t unm_get_pkt_info(mblk_t *mp, pktinfo_t *pktinfo) { mblk_t *bp; ushort_t type; (void) memset(pktinfo, 0, sizeof (pktinfo_t)); for (bp = mp; bp != NULL; bp = bp->b_cont) { if (MBLKL(bp) == 0) continue; pktinfo->mblk_no++; pktinfo->total_len += MBLKL(bp); } if (MBLKL(mp) < (sizeof (struct ether_header) + sizeof (ipha_t))) return (B_FALSE); /* * We just need non 1 byte aligned address, since ether_type is * ushort. */ if ((uintptr_t)mp->b_rptr & 1) return (B_FALSE); type = ((struct ether_header *)(uintptr_t)mp->b_rptr)->ether_type; if (type == htons(ETHERTYPE_VLAN)) { if (MBLKL(mp) < (sizeof (struct ether_vlan_header) + sizeof (ipha_t))) return (B_FALSE); type = ((struct ether_vlan_header *) \ (uintptr_t)mp->b_rptr)->ether_type; pktinfo->mac_hlen = sizeof (struct ether_vlan_header); } else { pktinfo->mac_hlen = sizeof (struct ether_header); } pktinfo->etype = type; if (pktinfo->etype == htons(ETHERTYPE_IP)) { uchar_t *ip_off = mp->b_rptr + pktinfo->mac_hlen; pktinfo->ip_hlen = IPH_HDR_LENGTH((uintptr_t)ip_off); pktinfo->l4_proto = ((ipha_t *)(uintptr_t)ip_off)->ipha_protocol; /* IP header not aligned to quadward boundary? */ if ((unsigned long)(ip_off + pktinfo->ip_hlen) % 8 != 0) return (B_FALSE); } return (B_TRUE); } static void unm_update_pkt_info(char *ptr, pktinfo_t *pktinfo) { ushort_t type; type = ((struct ether_header *)(uintptr_t)ptr)->ether_type; if (type == htons(ETHERTYPE_VLAN)) { type = ((struct ether_vlan_header *)(uintptr_t)ptr)->ether_type; pktinfo->mac_hlen = sizeof (struct ether_vlan_header); } else { pktinfo->mac_hlen = sizeof (struct ether_header); } pktinfo->etype = type; if (pktinfo->etype == htons(ETHERTYPE_IP)) { char *ipp = ptr + pktinfo->mac_hlen; pktinfo->ip_hlen = IPH_HDR_LENGTH((uintptr_t)ipp); pktinfo->l4_proto = ((ipha_t *)(uintptr_t)ipp)->ipha_protocol; } } static boolean_t unm_send_copy(struct unm_adapter_s *adapter, mblk_t *mp, pktinfo_t *pktinfo) { hardware_context *hw; u32 producer = 0; cmdDescType0_t *hwdesc; struct unm_cmd_buffer *pbuf = NULL; u32 mblen; int no_of_desc = 1; int MaxTxDescCount; mblk_t *bp; char *txb; hw = &adapter->ahw; MaxTxDescCount = adapter->MaxTxDescCount; UNM_SPIN_LOCK(&adapter->tx_lock); membar_enter(); if (find_diff_among(adapter->cmdProducer, adapter->lastCmdConsumer, MaxTxDescCount) <= 2) { adapter->stats.outofcmddesc++; adapter->resched_needed = 1; membar_exit(); UNM_SPIN_UNLOCK(&adapter->tx_lock); return (B_FALSE); } adapter->freecmds -= no_of_desc; producer = adapter->cmdProducer; adapter->cmdProducer = get_index_range(adapter->cmdProducer, MaxTxDescCount, no_of_desc); hwdesc = &hw->cmdDescHead[producer]; (void) memset(hwdesc, 0, sizeof (cmdDescType0_t)); pbuf = &adapter->cmd_buf_arr[producer]; pbuf->msg = NULL; pbuf->head = NULL; pbuf->tail = NULL; txb = pbuf->dma_area.vaddr; for (bp = mp; bp != NULL; bp = bp->b_cont) { if ((mblen = MBLKL(bp)) == 0) continue; bcopy(bp->b_rptr, txb, mblen); txb += mblen; } /* * Determine metadata if not previously done due to fragmented mblk. */ if (pktinfo->etype == 0) unm_update_pkt_info(pbuf->dma_area.vaddr, pktinfo); (void) ddi_dma_sync(pbuf->dma_area.dma_hdl, 0, pktinfo->total_len, DDI_DMA_SYNC_FORDEV); /* hwdesc->u1.s1.tcpHdrOffset = 0; */ /* hwdesc->mss = 0; */ hwdesc->u1.s1.opcode = TX_ETHER_PKT; hwdesc->u3.s1.port = adapter->portnum; hwdesc->u3.s1.ctx_id = adapter->portnum; hwdesc->u6.s1.buffer1Length = pktinfo->total_len; hwdesc->u5.AddrBuffer1 = pbuf->dma_area.dma_addr; hwdesc->u1.s1.numOfBuffers = 1; hwdesc->u1.s1.totalLength = pktinfo->total_len; unm_tx_csum(hwdesc, mp, pktinfo); unm_desc_dma_sync(hw->cmd_desc_dma_handle, producer, no_of_desc, MaxTxDescCount, sizeof (cmdDescType0_t), DDI_DMA_SYNC_FORDEV); hw->cmdProducer = adapter->cmdProducer; unm_nic_update_cmd_producer(adapter, adapter->cmdProducer); adapter->stats.txbytes += pktinfo->total_len; adapter->stats.xmitfinished++; adapter->stats.txcopyed++; UNM_SPIN_UNLOCK(&adapter->tx_lock); freemsg(mp); return (B_TRUE); } /* Should be called with adapter->tx_lock held. */ static void unm_return_dma_handle(unm_adapter *adapter, unm_dmah_node_t *head, unm_dmah_node_t *tail, uint32_t num) { ASSERT(tail != NULL); tail->next = adapter->dmahdl_pool; adapter->dmahdl_pool = head; adapter->freehdls += num; } static unm_dmah_node_t * unm_reserve_dma_handle(unm_adapter* adapter) { unm_dmah_node_t *dmah = NULL; dmah = adapter->dmahdl_pool; if (dmah != NULL) { adapter->dmahdl_pool = dmah->next; dmah->next = NULL; adapter->freehdls--; membar_exit(); } return (dmah); } static boolean_t unm_send_mapped(struct unm_adapter_s *adapter, mblk_t *mp, pktinfo_t *pktinfo) { hardware_context *hw; u32 producer = 0; u32 saved_producer = 0; cmdDescType0_t *hwdesc; struct unm_cmd_buffer *pbuf = NULL; int no_of_desc; int k; int MaxTxDescCount; mblk_t *bp; unm_dmah_node_t *dmah, *head = NULL, *tail = NULL, *hdlp; ddi_dma_cookie_t cookie[MAX_COOKIES_PER_CMD + 1]; int ret, i; uint32_t hdl_reserved = 0; uint32_t mblen; uint32_t ncookies, index = 0, total_cookies = 0; MaxTxDescCount = adapter->MaxTxDescCount; UNM_SPIN_LOCK(&adapter->tx_lock); /* bind all the mblks of the packet first */ for (bp = mp; bp != NULL; bp = bp->b_cont) { mblen = MBLKL(bp); if (mblen == 0) continue; dmah = unm_reserve_dma_handle(adapter); if (dmah == NULL) { adapter->stats.outoftxdmahdl++; goto err_map; } ret = ddi_dma_addr_bind_handle(dmah->dmahdl, NULL, (caddr_t)bp->b_rptr, mblen, DDI_DMA_STREAMING | DDI_DMA_WRITE, DDI_DMA_DONTWAIT, NULL, &cookie[index], &ncookies); if (ret != DDI_DMA_MAPPED) goto err_map; if (tail == NULL) { head = tail = dmah; } else { tail->next = dmah; tail = dmah; } hdl_reserved++; total_cookies += ncookies; if (total_cookies > MAX_COOKIES_PER_CMD) { dmah = NULL; goto err_map; } if (index == 0) { size_t hsize = cookie[0].dmac_size; /* * For TCP/UDP packets with checksum offload, * MAC/IP headers need to be contiguous. Otherwise, * there must be at least 16 bytes in the first * descriptor. */ if ((pktinfo->l4_proto == IPPROTO_TCP) || (pktinfo->l4_proto == IPPROTO_UDP)) { if (hsize < (pktinfo->mac_hlen + pktinfo->ip_hlen)) { dmah = NULL; goto err_map; } } else { if (hsize < 16) { dmah = NULL; goto err_map; } } } index++; ncookies--; for (i = 0; i < ncookies; i++, index++) ddi_dma_nextcookie(dmah->dmahdl, &cookie[index]); } dmah = NULL; hw = &adapter->ahw; no_of_desc = (total_cookies + 3) >> 2; membar_enter(); if (find_diff_among(adapter->cmdProducer, adapter->lastCmdConsumer, MaxTxDescCount) < no_of_desc+2) { /* * If we are going to be trying the copy path, no point * scheduling an upcall when Tx resources are freed. */ if (pktinfo->total_len > adapter->maxmtu) { adapter->stats.outofcmddesc++; adapter->resched_needed = 1; } membar_exit(); goto err_alloc_desc; } adapter->freecmds -= no_of_desc; /* Copy the descriptors into the hardware */ producer = adapter->cmdProducer; saved_producer = producer; hwdesc = &hw->cmdDescHead[producer]; (void) memset(hwdesc, 0, sizeof (cmdDescType0_t)); pbuf = &adapter->cmd_buf_arr[producer]; pbuf->msg = mp; pbuf->head = head; pbuf->tail = tail; hwdesc->u1.s1.numOfBuffers = total_cookies; hwdesc->u1.s1.opcode = TX_ETHER_PKT; hwdesc->u3.s1.port = adapter->portnum; /* hwdesc->u1.s1.tcpHdrOffset = 0; */ /* hwdesc->mss = 0; */ hwdesc->u3.s1.ctx_id = adapter->portnum; hwdesc->u1.s1.totalLength = pktinfo->total_len; unm_tx_csum(hwdesc, mp, pktinfo); for (i = k = 0; i < total_cookies; i++) { if (k == 4) { /* Move to the next descriptor */ k = 0; producer = get_next_index(producer, MaxTxDescCount); hwdesc = &hw->cmdDescHead[producer]; (void) memset(hwdesc, 0, sizeof (cmdDescType0_t)); } switch (k) { case 0: hwdesc->u6.s1.buffer1Length = cookie[i].dmac_size; hwdesc->u5.AddrBuffer1 = cookie[i].dmac_laddress; break; case 1: hwdesc->u6.s1.buffer2Length = cookie[i].dmac_size; hwdesc->u2.AddrBuffer2 = cookie[i].dmac_laddress; break; case 2: hwdesc->u6.s1.buffer3Length = cookie[i].dmac_size; hwdesc->u4.AddrBuffer3 = cookie[i].dmac_laddress; break; case 3: hwdesc->u6.s1.buffer4Length = cookie[i].dmac_size; hwdesc->u7.AddrBuffer4 = cookie[i].dmac_laddress; break; } k++; } unm_desc_dma_sync(hw->cmd_desc_dma_handle, saved_producer, no_of_desc, MaxTxDescCount, sizeof (cmdDescType0_t), DDI_DMA_SYNC_FORDEV); adapter->cmdProducer = get_next_index(producer, MaxTxDescCount); hw->cmdProducer = adapter->cmdProducer; unm_nic_update_cmd_producer(adapter, adapter->cmdProducer); adapter->stats.txbytes += pktinfo->total_len; adapter->stats.xmitfinished++; adapter->stats.txmapped++; UNM_SPIN_UNLOCK(&adapter->tx_lock); return (B_TRUE); err_alloc_desc: err_map: hdlp = head; while (hdlp != NULL) { (void) ddi_dma_unbind_handle(hdlp->dmahdl); hdlp = hdlp->next; } /* * add the reserved but bind failed one to the list to be returned */ if (dmah != NULL) { if (tail == NULL) head = tail = dmah; else { tail->next = dmah; tail = dmah; } hdl_reserved++; } if (head != NULL) unm_return_dma_handle(adapter, head, tail, hdl_reserved); UNM_SPIN_UNLOCK(&adapter->tx_lock); return (B_FALSE); } static boolean_t unm_nic_xmit_frame(unm_adapter *adapter, mblk_t *mp) { pktinfo_t pktinfo; boolean_t status = B_FALSE, send_mapped; adapter->stats.xmitcalled++; send_mapped = unm_get_pkt_info(mp, &pktinfo); if (pktinfo.total_len <= adapter->tx_bcopy_threshold || pktinfo.mblk_no >= MAX_BUFFERS_PER_CMD) send_mapped = B_FALSE; if (send_mapped == B_TRUE) status = unm_send_mapped(adapter, mp, &pktinfo); if (status != B_TRUE) { if (pktinfo.total_len <= adapter->maxmtu) return (unm_send_copy(adapter, mp, &pktinfo)); /* message too large */ freemsg(mp); adapter->stats.txdropped++; status = B_TRUE; } return (status); } static int unm_nic_check_temp(struct unm_adapter_s *adapter) { uint32_t temp, temp_state, temp_val; int rv = 0; if ((adapter->ahw.revision_id == NX_P3_A2) || (adapter->ahw.revision_id == NX_P3_A0)) return (0); temp = adapter->unm_nic_pci_read_normalize(adapter, CRB_TEMP_STATE); temp_state = nx_get_temp_state(temp); temp_val = nx_get_temp_val(temp); if (temp_state == NX_TEMP_PANIC) { cmn_err(CE_WARN, "%s: Device temperature %d C exceeds " "maximum allowed, device has been shut down\n", unm_nic_driver_name, temp_val); rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { cmn_err(CE_WARN, "%s: Device temperature %d C exceeds" "operating range. Immediate action needed.\n", unm_nic_driver_name, temp_val); } } else { if (adapter->temp == NX_TEMP_WARN) { cmn_err(CE_WARN, "%s: Device temperature is now %d " "degrees C in normal range.\n", unm_nic_driver_name, temp_val); } } adapter->temp = temp_state; return (rv); } static void unm_watchdog(unsigned long v) { unm_adapter *adapter = (unm_adapter *)v; if ((adapter->portnum == 0) && unm_nic_check_temp(adapter)) { /* * We return without turning on the netdev queue as there * was an overheated device */ return; } unm_nic_handle_phy_intr(adapter); /* * This function schedules a call for itself. */ adapter->watchdog_timer = timeout((void (*)(void *))&unm_watchdog, (void *)adapter, 2 * drv_usectohz(1000000)); } static void unm_nic_clear_stats(unm_adapter *adapter) { (void) memset(&adapter->stats, 0, sizeof (adapter->stats)); } static void unm_nic_poll(unm_adapter *adapter) { int work_done, tx_complete; adapter->stats.polled++; loop: tx_complete = unm_process_cmd_ring(adapter); work_done = unm_process_rcv_ring(adapter, NX_RX_MAXBUFS); if ((!tx_complete) || (!(work_done < NX_RX_MAXBUFS))) goto loop; UNM_READ_LOCK(&adapter->adapter_lock); unm_nic_enable_int(adapter); UNM_READ_UNLOCK(&adapter->adapter_lock); } /* ARGSUSED */ uint_t unm_intr(caddr_t data, caddr_t arg) { unm_adapter *adapter = (unm_adapter *)(uintptr_t)data; if (unm_nic_clear_int(adapter)) return (DDI_INTR_UNCLAIMED); unm_nic_poll(adapter); return (DDI_INTR_CLAIMED); } /* * This is invoked from receive isr. Due to the single threaded nature * of the invocation, pool_lock acquisition is not neccesary to protect * pool_list. */ static void unm_free_rx_buffer(unm_rcv_desc_ctx_t *rcv_desc, unm_rx_buffer_t *rx_buffer) { /* mutex_enter(rcv_desc->pool_lock); */ rx_buffer->next = rcv_desc->pool_list; rcv_desc->pool_list = rx_buffer; rcv_desc->rx_buf_free++; /* mutex_exit(rcv_desc->pool_lock); */ } /* * unm_process_rcv() send the received packet to the protocol stack. */ static mblk_t * unm_process_rcv(unm_adapter *adapter, statusDesc_t *desc) { unm_recv_context_t *recv_ctx = &(adapter->recv_ctx[0]); unm_rx_buffer_t *rx_buffer; mblk_t *mp; u32 desc_ctx = desc->u1.s1.type; unm_rcv_desc_ctx_t *rcv_desc = &recv_ctx->rcv_desc[desc_ctx]; u32 pkt_length = desc->u1.s1.totalLength; int poff = desc->u1.s1.pkt_offset; int index, cksum_flags, docopy; int index_lo = desc->u1.s1.referenceHandle_lo; char *vaddr; index = ((desc->u1.s1.referenceHandle_hi << 4) | index_lo); rx_buffer = index2rxbuf(rcv_desc, index); if (rx_buffer == NULL) { cmn_err(CE_WARN, "\r\nNULL rx_buffer idx=%d", index); return (NULL); } vaddr = (char *)rx_buffer->dma_info.vaddr; if (vaddr == NULL) { cmn_err(CE_WARN, "\r\nNULL vaddr"); return (NULL); } rcv_desc->rx_desc_handled++; rcv_desc->rx_buf_card--; (void) ddi_dma_sync(rx_buffer->dma_info.dma_hdl, 0, pkt_length + poff + (adapter->ahw.cut_through ? 0 : IP_ALIGNMENT_BYTES), DDI_DMA_SYNC_FORCPU); /* * Copy packet into new allocated message buffer, if pkt_length * is below copy threshold. */ docopy = (pkt_length <= adapter->rx_bcopy_threshold) ? 1 : 0; /* * If card is running out of rx buffers, then attempt to allocate * new mblk so we can feed this rx buffer back to card (we * _could_ look at what's pending on free and recycle lists). */ if (rcv_desc->rx_buf_card < NX_RX_THRESHOLD) { docopy = 1; adapter->stats.rxbufshort++; } if (docopy == 1) { if ((mp = allocb(pkt_length + IP_ALIGNMENT_BYTES, 0)) == NULL) { adapter->stats.allocbfailed++; goto freebuf; } mp->b_rptr += IP_ALIGNMENT_BYTES; vaddr += poff; bcopy(vaddr, mp->b_rptr, pkt_length); adapter->stats.rxcopyed++; unm_free_rx_buffer(rcv_desc, rx_buffer); } else { mp = (mblk_t *)rx_buffer->mp; if (mp == NULL) { mp = desballoc(rx_buffer->dma_info.vaddr, rcv_desc->dma_size, 0, &rx_buffer->rx_recycle); if (mp == NULL) { adapter->stats.desballocfailed++; goto freebuf; } rx_buffer->mp = mp; } mp->b_rptr += poff; adapter->stats.rxmapped++; } mp->b_wptr = (uchar_t *)((unsigned long)mp->b_rptr + pkt_length); if (desc->u1.s1.status == STATUS_CKSUM_OK) { adapter->stats.csummed++; cksum_flags = HCK_FULLCKSUM_OK | HCK_IPV4_HDRCKSUM | HCK_FULLCKSUM; } else { cksum_flags = 0; } (void) hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0, cksum_flags, 0); adapter->stats.no_rcv++; adapter->stats.rxbytes += pkt_length; adapter->stats.uphappy++; return (mp); freebuf: unm_free_rx_buffer(rcv_desc, rx_buffer); return (NULL); } /* Process Receive status ring */ static int unm_process_rcv_ring(unm_adapter *adapter, int max) { unm_recv_context_t *recv_ctx = &(adapter->recv_ctx[0]); statusDesc_t *desc_head = recv_ctx->rcvStatusDescHead; statusDesc_t *desc = NULL; uint32_t consumer, start; int count = 0, ring; mblk_t *mp; start = consumer = recv_ctx->statusRxConsumer; unm_desc_dma_sync(recv_ctx->status_desc_dma_handle, start, max, adapter->MaxRxDescCount, sizeof (statusDesc_t), DDI_DMA_SYNC_FORCPU); while (count < max) { desc = &desc_head[consumer]; if (!(desc->u1.s1.owner & STATUS_OWNER_HOST)) break; mp = unm_process_rcv(adapter, desc); desc->u1.s1.owner = STATUS_OWNER_PHANTOM; consumer = (consumer + 1) % adapter->MaxRxDescCount; count++; if (mp != NULL) mac_rx(adapter->mach, NULL, mp); } for (ring = 0; ring < adapter->max_rds_rings; ring++) { if (recv_ctx->rcv_desc[ring].rx_desc_handled > 0) unm_post_rx_buffers_nodb(adapter, ring); } if (count) { unm_desc_dma_sync(recv_ctx->status_desc_dma_handle, start, count, adapter->MaxRxDescCount, sizeof (statusDesc_t), DDI_DMA_SYNC_FORDEV); /* update the consumer index in phantom */ recv_ctx->statusRxConsumer = consumer; UNM_READ_LOCK(&adapter->adapter_lock); adapter->unm_nic_hw_write_wx(adapter, recv_ctx->host_sds_consumer, &consumer, 4); UNM_READ_UNLOCK(&adapter->adapter_lock); } return (count); } /* Process Command status ring */ static int unm_process_cmd_ring(struct unm_adapter_s *adapter) { u32 last_consumer; u32 consumer; int count = 0; struct unm_cmd_buffer *buffer; int done; unm_dmah_node_t *dmah, *head = NULL, *tail = NULL; uint32_t free_hdls = 0; (void) ddi_dma_sync(adapter->ctxDesc_dma_handle, sizeof (RingContext), sizeof (uint32_t), DDI_DMA_SYNC_FORCPU); last_consumer = adapter->lastCmdConsumer; consumer = *(adapter->cmdConsumer); while (last_consumer != consumer) { buffer = &adapter->cmd_buf_arr[last_consumer]; if (buffer->head != NULL) { dmah = buffer->head; while (dmah != NULL) { (void) ddi_dma_unbind_handle(dmah->dmahdl); dmah = dmah->next; free_hdls++; } if (head == NULL) { head = buffer->head; tail = buffer->tail; } else { tail->next = buffer->head; tail = buffer->tail; } buffer->head = NULL; buffer->tail = NULL; if (buffer->msg != NULL) { freemsg(buffer->msg); buffer->msg = NULL; } } last_consumer = get_next_index(last_consumer, adapter->MaxTxDescCount); if (++count > NX_MAX_TXCOMPS) break; } if (count) { int doresched; UNM_SPIN_LOCK(&adapter->tx_lock); adapter->lastCmdConsumer = last_consumer; adapter->freecmds += count; membar_exit(); doresched = adapter->resched_needed; if (doresched) adapter->resched_needed = 0; if (head != NULL) unm_return_dma_handle(adapter, head, tail, free_hdls); UNM_SPIN_UNLOCK(&adapter->tx_lock); if (doresched) mac_tx_update(adapter->mach); } (void) ddi_dma_sync(adapter->ctxDesc_dma_handle, sizeof (RingContext), sizeof (uint32_t), DDI_DMA_SYNC_FORCPU); consumer = *(adapter->cmdConsumer); done = (adapter->lastCmdConsumer == consumer); return (done); } /* * This is invoked from receive isr, and at initialization time when no * rx buffers have been posted to card. Due to the single threaded nature * of the invocation, pool_lock acquisition is not neccesary to protect * pool_list. */ static unm_rx_buffer_t * unm_reserve_rx_buffer(unm_rcv_desc_ctx_t *rcv_desc) { unm_rx_buffer_t *rx_buffer = NULL; /* mutex_enter(rcv_desc->pool_lock); */ if (rcv_desc->rx_buf_free) { rx_buffer = rcv_desc->pool_list; rcv_desc->pool_list = rx_buffer->next; rx_buffer->next = NULL; rcv_desc->rx_buf_free--; } else { mutex_enter(rcv_desc->recycle_lock); if (rcv_desc->rx_buf_recycle) { rcv_desc->pool_list = rcv_desc->recycle_list; rcv_desc->recycle_list = NULL; rcv_desc->rx_buf_free += rcv_desc->rx_buf_recycle; rcv_desc->rx_buf_recycle = 0; rx_buffer = rcv_desc->pool_list; rcv_desc->pool_list = rx_buffer->next; rx_buffer->next = NULL; rcv_desc->rx_buf_free--; } mutex_exit(rcv_desc->recycle_lock); } /* mutex_exit(rcv_desc->pool_lock); */ return (rx_buffer); } static void post_rx_doorbell(struct unm_adapter_s *adapter, uint32_t ringid, int count) { #define UNM_RCV_PEG_DB_ID 2 #define UNM_RCV_PRODUCER_OFFSET 0 ctx_msg msg = {0}; /* * Write a doorbell msg to tell phanmon of change in * receive ring producer */ msg.PegId = UNM_RCV_PEG_DB_ID; msg.privId = 1; msg.Count = count; msg.CtxId = adapter->portnum; msg.Opcode = UNM_RCV_PRODUCER(ringid); dbwritel(*((__uint32_t *)&msg), (void *)(DB_NORMALIZE(adapter, UNM_RCV_PRODUCER_OFFSET))); } static int unm_post_rx_buffers(struct unm_adapter_s *adapter, uint32_t ringid) { unm_recv_context_t *recv_ctx = &(adapter->recv_ctx[0]); unm_rcv_desc_ctx_t *rcv_desc = &recv_ctx->rcv_desc[ringid]; unm_rx_buffer_t *rx_buffer; rcvDesc_t *pdesc; int count; for (count = 0; count < rcv_desc->MaxRxDescCount; count++) { rx_buffer = unm_reserve_rx_buffer(rcv_desc); if (rx_buffer != NULL) { pdesc = &rcv_desc->desc_head[count]; pdesc->referenceHandle = rxbuf2index(rcv_desc, rx_buffer); pdesc->flags = ringid; pdesc->bufferLength = rcv_desc->dma_size; pdesc->AddrBuffer = rx_buffer->dma_info.dma_addr; } else return (DDI_FAILURE); } rcv_desc->producer = count % rcv_desc->MaxRxDescCount; count--; unm_desc_dma_sync(rcv_desc->rx_desc_dma_handle, 0, /* start */ count, /* count */ count, /* range */ sizeof (rcvDesc_t), /* unit_size */ DDI_DMA_SYNC_FORDEV); /* direction */ rcv_desc->rx_buf_card = rcv_desc->MaxRxDescCount; UNM_READ_LOCK(&adapter->adapter_lock); adapter->unm_nic_hw_write_wx(adapter, rcv_desc->host_rx_producer, &count, 4); if (adapter->fw_major < 4) post_rx_doorbell(adapter, ringid, count); UNM_READ_UNLOCK(&adapter->adapter_lock); return (DDI_SUCCESS); } static void unm_post_rx_buffers_nodb(struct unm_adapter_s *adapter, uint32_t ringid) { unm_recv_context_t *recv_ctx = &(adapter->recv_ctx[0]); unm_rcv_desc_ctx_t *rcv_desc = &recv_ctx->rcv_desc[ringid]; struct unm_rx_buffer *rx_buffer; rcvDesc_t *pdesc; int count, producer = rcv_desc->producer; int last_producer = producer; for (count = 0; count < rcv_desc->rx_desc_handled; count++) { rx_buffer = unm_reserve_rx_buffer(rcv_desc); if (rx_buffer != NULL) { pdesc = &rcv_desc->desc_head[producer]; pdesc->referenceHandle = rxbuf2index(rcv_desc, rx_buffer); pdesc->flags = ringid; pdesc->bufferLength = rcv_desc->dma_size; pdesc->AddrBuffer = rx_buffer->dma_info.dma_addr; } else { adapter->stats.outofrxbuf++; break; } producer = get_next_index(producer, rcv_desc->MaxRxDescCount); } /* if we did allocate buffers, then write the count to Phantom */ if (count) { /* Sync rx ring, considering case for wrap around */ unm_desc_dma_sync(rcv_desc->rx_desc_dma_handle, last_producer, count, rcv_desc->MaxRxDescCount, sizeof (rcvDesc_t), DDI_DMA_SYNC_FORDEV); rcv_desc->producer = producer; rcv_desc->rx_desc_handled -= count; rcv_desc->rx_buf_card += count; producer = (producer - 1) % rcv_desc->MaxRxDescCount; UNM_READ_LOCK(&adapter->adapter_lock); adapter->unm_nic_hw_write_wx(adapter, rcv_desc->host_rx_producer, &producer, 4); UNM_READ_UNLOCK(&adapter->adapter_lock); } } int unm_nic_fill_statistics_128M(struct unm_adapter_s *adapter, struct unm_statistics *unm_stats) { void *addr; if (adapter->ahw.board_type == UNM_NIC_XGBE) { UNM_WRITE_LOCK(&adapter->adapter_lock); unm_nic_pci_change_crbwindow_128M(adapter, 0); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_TX_BYTE_CNT, &(unm_stats->tx_bytes)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_TX_FRAME_CNT, &(unm_stats->tx_packets)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_RX_BYTE_CNT, &(unm_stats->rx_bytes)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_RX_FRAME_CNT, &(unm_stats->rx_packets)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_AGGR_ERROR_CNT, &(unm_stats->rx_errors)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_CRC_ERROR_CNT, &(unm_stats->rx_CRC_errors)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_OVERSIZE_FRAME_ERR, &(unm_stats->rx_long_length_error)); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_READ_REG(UNM_NIU_XGE_UNDERSIZE_FRAME_ERR, &(unm_stats->rx_short_length_error)); /* * For reading rx_MAC_error bit different procedure * UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_TEST_MUX_CTL, 0x15); * UNM_NIC_LOCKED_READ_REG((UNM_CRB_NIU + 0xC0), &temp); * unm_stats->rx_MAC_errors = temp & 0xff; */ unm_nic_pci_change_crbwindow_128M(adapter, 1); UNM_WRITE_UNLOCK(&adapter->adapter_lock); } else { UNM_SPIN_LOCK_ISR(&adapter->tx_lock); unm_stats->tx_bytes = adapter->stats.txbytes; unm_stats->tx_packets = adapter->stats.xmitedframes + adapter->stats.xmitfinished; unm_stats->rx_bytes = adapter->stats.rxbytes; unm_stats->rx_packets = adapter->stats.no_rcv; unm_stats->rx_errors = adapter->stats.rcvdbadmsg; unm_stats->tx_errors = adapter->stats.nocmddescriptor; unm_stats->rx_short_length_error = adapter->stats.uplcong; unm_stats->rx_long_length_error = adapter->stats.uphcong; unm_stats->rx_CRC_errors = 0; unm_stats->rx_MAC_errors = 0; UNM_SPIN_UNLOCK_ISR(&adapter->tx_lock); } return (0); } int unm_nic_fill_statistics_2M(struct unm_adapter_s *adapter, struct unm_statistics *unm_stats) { if (adapter->ahw.board_type == UNM_NIC_XGBE) { (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_TX_BYTE_CNT, &(unm_stats->tx_bytes), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_TX_FRAME_CNT, &(unm_stats->tx_packets), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_RX_BYTE_CNT, &(unm_stats->rx_bytes), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_RX_FRAME_CNT, &(unm_stats->rx_packets), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_AGGR_ERROR_CNT, &(unm_stats->rx_errors), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_CRC_ERROR_CNT, &(unm_stats->rx_CRC_errors), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_OVERSIZE_FRAME_ERR, &(unm_stats->rx_long_length_error), 4); (void) unm_nic_hw_read_wx_2M(adapter, UNM_NIU_XGE_UNDERSIZE_FRAME_ERR, &(unm_stats->rx_short_length_error), 4); } else { UNM_SPIN_LOCK_ISR(&adapter->tx_lock); unm_stats->tx_bytes = adapter->stats.txbytes; unm_stats->tx_packets = adapter->stats.xmitedframes + adapter->stats.xmitfinished; unm_stats->rx_bytes = adapter->stats.rxbytes; unm_stats->rx_packets = adapter->stats.no_rcv; unm_stats->rx_errors = adapter->stats.rcvdbadmsg; unm_stats->tx_errors = adapter->stats.nocmddescriptor; unm_stats->rx_short_length_error = adapter->stats.uplcong; unm_stats->rx_long_length_error = adapter->stats.uphcong; unm_stats->rx_CRC_errors = 0; unm_stats->rx_MAC_errors = 0; UNM_SPIN_UNLOCK_ISR(&adapter->tx_lock); } return (0); } int unm_nic_clear_statistics_128M(struct unm_adapter_s *adapter) { void *addr; int data = 0; UNM_WRITE_LOCK(&adapter->adapter_lock); unm_nic_pci_change_crbwindow_128M(adapter, 0); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_TX_BYTE_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_TX_FRAME_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_RX_BYTE_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_RX_FRAME_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_AGGR_ERROR_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_CRC_ERROR_CNT, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_OVERSIZE_FRAME_ERR, &data); /* LINTED: E_FALSE_LOGICAL_EXPR */ UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_XGE_UNDERSIZE_FRAME_ERR, &data); unm_nic_pci_change_crbwindow_128M(adapter, 1); UNM_WRITE_UNLOCK(&adapter->adapter_lock); unm_nic_clear_stats(adapter); return (0); } int unm_nic_clear_statistics_2M(struct unm_adapter_s *adapter) { int data = 0; (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_TX_BYTE_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_TX_FRAME_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_RX_BYTE_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_RX_FRAME_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_AGGR_ERROR_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_CRC_ERROR_CNT, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_OVERSIZE_FRAME_ERR, &data, 4); (void) unm_nic_hw_write_wx_2M(adapter, UNM_NIU_XGE_UNDERSIZE_FRAME_ERR, &data, 4); unm_nic_clear_stats(adapter); return (0); } /* * unm_nic_ioctl () We provide the tcl/phanmon support * through these ioctls. */ static void unm_nic_ioctl(struct unm_adapter_s *adapter, int cmd, queue_t *q, mblk_t *mp) { void *ptr; switch (cmd) { case UNM_NIC_CMD: (void) unm_nic_do_ioctl(adapter, q, mp); break; case UNM_NIC_NAME: ptr = (void *) mp->b_cont->b_rptr; /* * Phanmon checks for "UNM-UNM" string * Replace the hardcoded value with appropriate macro */ DPRINTF(-1, (CE_CONT, "UNM_NIC_NAME ioctl executed %d %d\n", cmd, __LINE__)); (void) memcpy(ptr, "UNM-UNM", 10); miocack(q, mp, 10, 0); break; default: cmn_err(CE_WARN, "Netxen ioctl cmd %x not supported\n", cmd); miocnak(q, mp, 0, EINVAL); break; } } int unm_nic_resume(unm_adapter *adapter) { adapter->watchdog_timer = timeout((void (*)(void *))&unm_watchdog, (void *) adapter, 50000); if (adapter->intr_type == DDI_INTR_TYPE_MSI) (void) ddi_intr_block_enable(&adapter->intr_handle, 1); else (void) ddi_intr_enable(adapter->intr_handle); UNM_READ_LOCK(&adapter->adapter_lock); unm_nic_enable_int(adapter); UNM_READ_UNLOCK(&adapter->adapter_lock); mac_link_update(adapter->mach, LINK_STATE_UP); return (DDI_SUCCESS); } int unm_nic_suspend(unm_adapter *adapter) { mac_link_update(adapter->mach, LINK_STATE_DOWN); (void) untimeout(adapter->watchdog_timer); UNM_READ_LOCK(&adapter->adapter_lock); unm_nic_disable_int(adapter); UNM_READ_UNLOCK(&adapter->adapter_lock); if (adapter->intr_type == DDI_INTR_TYPE_MSI) (void) ddi_intr_block_disable(&adapter->intr_handle, 1); else (void) ddi_intr_disable(adapter->intr_handle); return (DDI_SUCCESS); } static int unm_nic_do_ioctl(unm_adapter *adapter, queue_t *wq, mblk_t *mp) { unm_nic_ioctl_data_t data; struct unm_nic_ioctl_data *up_data; ddi_acc_handle_t conf_handle; int retval = 0; unsigned int efuse_chip_id; char *ptr1; short *ptr2; int *ptr4; up_data = (struct unm_nic_ioctl_data *)(mp->b_cont->b_rptr); (void) memcpy(&data, (void **)(uintptr_t)(mp->b_cont->b_rptr), sizeof (data)); /* Shouldn't access beyond legal limits of "char u[64];" member */ if (data.size > sizeof (data.uabc)) { /* evil user tried to crash the kernel */ cmn_err(CE_WARN, "bad size: %d\n", data.size); retval = GLD_BADARG; goto error_out; } switch (data.cmd) { case unm_nic_cmd_pci_read: if ((retval = adapter->unm_nic_hw_read_ioctl(adapter, data.off, up_data, data.size))) { DPRINTF(-1, (CE_WARN, "%s(%d) unm_nic_hw_read_wx " "returned %d\n", __FUNCTION__, __LINE__, retval)); retval = data.rv; goto error_out; } data.rv = 0; break; case unm_nic_cmd_pci_write: if ((data.rv = adapter->unm_nic_hw_write_ioctl(adapter, data.off, &(data.uabc), data.size))) { DPRINTF(-1, (CE_WARN, "%s(%d) unm_nic_hw_write_wx " "returned %d\n", __FUNCTION__, __LINE__, data.rv)); retval = data.rv; goto error_out; } data.size = 0; break; case unm_nic_cmd_pci_mem_read: if ((data.rv = adapter->unm_nic_pci_mem_read(adapter, data.off, up_data, data.size))) { DPRINTF(-1, (CE_WARN, "%s(%d) unm_nic_pci_mem_read " "returned %d\n", __FUNCTION__, __LINE__, data.rv)); retval = data.rv; goto error_out; } data.rv = 0; break; case unm_nic_cmd_pci_mem_write: if ((data.rv = adapter->unm_nic_pci_mem_write(adapter, data.off, &(data.uabc), data.size))) { DPRINTF(-1, (CE_WARN, "%s(%d) unm_nic_cmd_pci_mem_write " "returned %d\n", __FUNCTION__, __LINE__, data.rv)); retval = data.rv; goto error_out; } data.size = 0; data.rv = 0; break; case unm_nic_cmd_pci_config_read: if (adapter->pci_cfg_handle != NULL) { conf_handle = adapter->pci_cfg_handle; } else if ((retval = pci_config_setup(adapter->dip, &conf_handle)) != DDI_SUCCESS) { DPRINTF(-1, (CE_WARN, "!%s: pci_config_setup failed" " error:%d\n", unm_nic_driver_name, retval)); goto error_out; } else adapter->pci_cfg_handle = conf_handle; switch (data.size) { case 1: ptr1 = (char *)up_data; *ptr1 = (char)pci_config_get8(conf_handle, data.off); break; case 2: ptr2 = (short *)up_data; *ptr2 = (short)pci_config_get16(conf_handle, data.off); break; case 4: ptr4 = (int *)up_data; *ptr4 = (int)pci_config_get32(conf_handle, data.off); break; } break; case unm_nic_cmd_pci_config_write: if (adapter->pci_cfg_handle != NULL) { conf_handle = adapter->pci_cfg_handle; } else if ((retval = pci_config_setup(adapter->dip, &conf_handle)) != DDI_SUCCESS) { DPRINTF(-1, (CE_WARN, "!%s: pci_config_setup failed" " error:%d\n", unm_nic_driver_name, retval)); goto error_out; } else { adapter->pci_cfg_handle = conf_handle; } switch (data.size) { case 1: pci_config_put8(conf_handle, data.off, *(char *)&(data.uabc)); break; case 2: pci_config_put16(conf_handle, data.off, *(short *)(uintptr_t)&(data.uabc)); break; case 4: pci_config_put32(conf_handle, data.off, *(u32 *)(uintptr_t)&(data.uabc)); break; } data.size = 0; break; case unm_nic_cmd_get_stats: data.rv = adapter->unm_nic_fill_statistics(adapter, (struct unm_statistics *)up_data); data.size = sizeof (struct unm_statistics); break; case unm_nic_cmd_clear_stats: data.rv = adapter->unm_nic_clear_statistics(adapter); break; case unm_nic_cmd_get_version: (void) memcpy(up_data, UNM_NIC_VERSIONID, sizeof (UNM_NIC_VERSIONID)); data.size = sizeof (UNM_NIC_VERSIONID); break; case unm_nic_cmd_get_phy_type: cmn_err(CE_WARN, "unm_nic_cmd_get_phy_type unimplemented\n"); break; case unm_nic_cmd_efuse_chip_id: efuse_chip_id = adapter->unm_nic_pci_read_normalize(adapter, UNM_EFUSE_CHIP_ID); (void) memcpy(up_data, &efuse_chip_id, sizeof (unsigned long)); data.rv = 0; break; default: cmn_err(CE_WARN, "%s%d: bad command %d\n", adapter->name, adapter->instance, data.cmd); data.rv = GLD_NOTSUPPORTED; data.size = 0; goto error_out; } work_done: miocack(wq, mp, data.size, data.rv); return (DDI_SUCCESS); error_out: cmn_err(CE_WARN, "%s(%d) ioctl error\n", __FUNCTION__, data.cmd); miocnak(wq, mp, 0, EINVAL); return (retval); } /* * Local datatype for defining tables of (Offset, Name) pairs */ typedef struct { offset_t index; char *name; } unm_ksindex_t; static const unm_ksindex_t unm_kstat[] = { { 0, "freehdls" }, { 1, "freecmds" }, { 2, "tx_bcopy_threshold" }, { 3, "rx_bcopy_threshold" }, { 4, "xmitcalled" }, { 5, "xmitedframes" }, { 6, "xmitfinished" }, { 7, "txbytes" }, { 8, "txcopyed" }, { 9, "txmapped" }, { 10, "outoftxdmahdl" }, { 11, "outofcmddesc" }, { 12, "txdropped" }, { 13, "polled" }, { 14, "uphappy" }, { 15, "updropped" }, { 16, "csummed" }, { 17, "no_rcv" }, { 18, "rxbytes" }, { 19, "rxcopyed" }, { 20, "rxmapped" }, { 21, "desballocfailed" }, { 22, "outofrxbuf" }, { 23, "promiscmode" }, { 24, "rxbufshort" }, { 25, "allocbfailed" }, { -1, NULL } }; static int unm_kstat_update(kstat_t *ksp, int flag) { unm_adapter *adapter; kstat_named_t *knp; if (flag != KSTAT_READ) return (EACCES); adapter = ksp->ks_private; knp = ksp->ks_data; (knp++)->value.ui32 = adapter->freehdls; (knp++)->value.ui64 = adapter->freecmds; (knp++)->value.ui64 = adapter->tx_bcopy_threshold; (knp++)->value.ui64 = adapter->rx_bcopy_threshold; (knp++)->value.ui64 = adapter->stats.xmitcalled; (knp++)->value.ui64 = adapter->stats.xmitedframes; (knp++)->value.ui64 = adapter->stats.xmitfinished; (knp++)->value.ui64 = adapter->stats.txbytes; (knp++)->value.ui64 = adapter->stats.txcopyed; (knp++)->value.ui64 = adapter->stats.txmapped; (knp++)->value.ui64 = adapter->stats.outoftxdmahdl; (knp++)->value.ui64 = adapter->stats.outofcmddesc; (knp++)->value.ui64 = adapter->stats.txdropped; (knp++)->value.ui64 = adapter->stats.polled; (knp++)->value.ui64 = adapter->stats.uphappy; (knp++)->value.ui64 = adapter->stats.updropped; (knp++)->value.ui64 = adapter->stats.csummed; (knp++)->value.ui64 = adapter->stats.no_rcv; (knp++)->value.ui64 = adapter->stats.rxbytes; (knp++)->value.ui64 = adapter->stats.rxcopyed; (knp++)->value.ui64 = adapter->stats.rxmapped; (knp++)->value.ui64 = adapter->stats.desballocfailed; (knp++)->value.ui64 = adapter->stats.outofrxbuf; (knp++)->value.ui64 = adapter->stats.promiscmode; (knp++)->value.ui64 = adapter->stats.rxbufshort; (knp++)->value.ui64 = adapter->stats.allocbfailed; return (0); } static kstat_t * unm_setup_named_kstat(unm_adapter *adapter, int instance, char *name, const unm_ksindex_t *ksip, size_t size, int (*update)(kstat_t *, int)) { kstat_t *ksp; kstat_named_t *knp; char *np; int type; int count = 0; size /= sizeof (unm_ksindex_t); ksp = kstat_create(unm_nic_driver_name, instance, name, "net", KSTAT_TYPE_NAMED, size-1, KSTAT_FLAG_PERSISTENT); if (ksp == NULL) return (NULL); ksp->ks_private = adapter; ksp->ks_update = update; for (knp = ksp->ks_data; (np = ksip->name) != NULL; ++knp, ++ksip) { count++; switch (*np) { default: type = KSTAT_DATA_UINT64; break; case '%': np += 1; type = KSTAT_DATA_UINT32; break; case '$': np += 1; type = KSTAT_DATA_STRING; break; case '&': np += 1; type = KSTAT_DATA_CHAR; break; } kstat_named_init(knp, np, type); } kstat_install(ksp); return (ksp); } void unm_init_kstats(unm_adapter* adapter, int instance) { adapter->kstats[0] = unm_setup_named_kstat(adapter, instance, "kstatinfo", unm_kstat, sizeof (unm_kstat), unm_kstat_update); } void unm_fini_kstats(unm_adapter* adapter) { if (adapter->kstats[0] != NULL) { kstat_delete(adapter->kstats[0]); adapter->kstats[0] = NULL; } } static int unm_nic_set_pauseparam(unm_adapter *adapter, unm_pauseparam_t *pause) { int ret = 0; if (adapter->ahw.board_type == UNM_NIC_GBE) { if (unm_niu_gbe_set_rx_flow_ctl(adapter, pause->rx_pause)) ret = -EIO; if (unm_niu_gbe_set_tx_flow_ctl(adapter, pause->tx_pause)) ret = -EIO; } else if (adapter->ahw.board_type == UNM_NIC_XGBE) { if (unm_niu_xg_set_tx_flow_ctl(adapter, pause->tx_pause)) ret = -EIO; } else ret = -EIO; return (ret); } /* * * GLD/MAC interfaces * */ static int ntxn_m_start(void *arg) { unm_adapter *adapter = arg; int ring; UNM_SPIN_LOCK(&adapter->lock); if (adapter->is_up == UNM_ADAPTER_UP_MAGIC) { UNM_SPIN_UNLOCK(&adapter->lock); return (DDI_SUCCESS); } if (init_firmware(adapter) != DDI_SUCCESS) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Failed to init firmware\n", adapter->name, adapter->instance); return (DDI_FAILURE); } unm_nic_clear_stats(adapter); if (unm_nic_hw_resources(adapter) != 0) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Error setting hw resources\n", adapter->name, adapter->instance); return (DDI_FAILURE); } if (adapter->fw_major < 4) { adapter->crb_addr_cmd_producer = crb_cmd_producer[adapter->portnum]; adapter->crb_addr_cmd_consumer = crb_cmd_consumer[adapter->portnum]; unm_nic_update_cmd_producer(adapter, 0); unm_nic_update_cmd_consumer(adapter, 0); } for (ring = 0; ring < adapter->max_rds_rings; ring++) { if (unm_post_rx_buffers(adapter, ring) != DDI_SUCCESS) { /* TODO: clean up */ UNM_SPIN_UNLOCK(&adapter->lock); return (DDI_FAILURE); } } if (unm_nic_macaddr_set(adapter, adapter->mac_addr) != 0) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Could not set mac address\n", adapter->name, adapter->instance); return (DDI_FAILURE); } if (unm_nic_init_port(adapter) != 0) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Could not initialize port\n", adapter->name, adapter->instance); return (DDI_FAILURE); } unm_nic_set_link_parameters(adapter); /* * P2 and P3 should be handled similarly. */ if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { if (unm_nic_set_promisc_mode(adapter) != 0) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Could not set promisc mode\n", adapter->name, adapter->instance); return (DDI_FAILURE); } } else { nx_p3_nic_set_multi(adapter); } adapter->stats.promiscmode = 1; if (unm_nic_set_mtu(adapter, adapter->mtu) != 0) { UNM_SPIN_UNLOCK(&adapter->lock); cmn_err(CE_WARN, "%s%d: Could not set mtu\n", adapter->name, adapter->instance); return (DDI_FAILURE); } adapter->watchdog_timer = timeout((void (*)(void *))&unm_watchdog, (void *)adapter, 0); adapter->is_up = UNM_ADAPTER_UP_MAGIC; if (adapter->intr_type == DDI_INTR_TYPE_MSI) (void) ddi_intr_block_enable(&adapter->intr_handle, 1); else (void) ddi_intr_enable(adapter->intr_handle); unm_nic_enable_int(adapter); UNM_SPIN_UNLOCK(&adapter->lock); return (GLD_SUCCESS); } /* * This code is kept here for reference so as to * see if something different is required to be done * in GLDV3. This will be deleted later. */ /* ARGSUSED */ static void ntxn_m_stop(void *arg) { } /*ARGSUSED*/ static int ntxn_m_multicst(void *arg, boolean_t add, const uint8_t *ep) { /* * When we correctly implement this, invoke nx_p3_nic_set_multi() * or nx_p2_nic_set_multi() here. */ return (GLD_SUCCESS); } /*ARGSUSED*/ static int ntxn_m_promisc(void *arg, boolean_t on) { #if 0 int err = 0; struct unm_adapter_s *adapter = arg; err = on ? unm_nic_set_promisc_mode(adapter) : unm_nic_unset_promisc_mode(adapter); if (err) return (GLD_FAILURE); #endif return (GLD_SUCCESS); } static int ntxn_m_stat(void *arg, uint_t stat, uint64_t *val) { struct unm_adapter_s *adapter = arg; struct unm_adapter_stats *portstat = &adapter->stats; switch (stat) { case MAC_STAT_IFSPEED: if (adapter->ahw.board_type == UNM_NIC_XGBE) { /* 10 Gigs */ *val = 10000000000ULL; } else { /* 1 Gig */ *val = 1000000000; } break; case MAC_STAT_MULTIRCV: *val = 0; break; case MAC_STAT_BRDCSTRCV: case MAC_STAT_BRDCSTXMT: *val = 0; break; case MAC_STAT_NORCVBUF: *val = portstat->updropped; break; case MAC_STAT_NOXMTBUF: *val = portstat->txdropped; break; case MAC_STAT_RBYTES: *val = portstat->rxbytes; break; case MAC_STAT_OBYTES: *val = portstat->txbytes; break; case MAC_STAT_OPACKETS: *val = portstat->xmitedframes; break; case MAC_STAT_IPACKETS: *val = portstat->uphappy; break; case MAC_STAT_OERRORS: *val = portstat->xmitcalled - portstat->xmitedframes; break; case ETHER_STAT_LINK_DUPLEX: *val = LINK_DUPLEX_FULL; break; default: /* * Shouldn't reach here... */ *val = 0; DPRINTF(0, (CE_WARN, ": unrecognized parameter = %d, value " "returned 1\n", stat)); } return (0); } static int ntxn_m_unicst(void *arg, const uint8_t *mac) { struct unm_adapter_s *adapter = arg; DPRINTF(-1, (CE_CONT, "%s: called\n", __func__)); if (unm_nic_macaddr_set(adapter, (uint8_t *)mac)) return (EAGAIN); bcopy(mac, adapter->mac_addr, ETHERADDRL); return (0); } static mblk_t * ntxn_m_tx(void *arg, mblk_t *mp) { unm_adapter *adapter = arg; mblk_t *next; while (mp != NULL) { next = mp->b_next; mp->b_next = NULL; if (unm_nic_xmit_frame(adapter, mp) != B_TRUE) { mp->b_next = next; break; } mp = next; adapter->stats.xmitedframes++; } return (mp); } static void ntxn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) { int cmd; struct iocblk *iocp = (struct iocblk *)(uintptr_t)mp->b_rptr; struct unm_adapter_s *adapter = (struct unm_adapter_s *)arg; enum ioc_reply status = IOC_DONE; iocp->ioc_error = 0; cmd = iocp->ioc_cmd; if (cmd == ND_GET || cmd == ND_SET) { status = unm_nd_ioctl(adapter, wq, mp, iocp); switch (status) { default: case IOC_INVAL: miocnak(wq, mp, 0, iocp->ioc_error == 0 ? EINVAL : iocp->ioc_error); break; case IOC_DONE: break; case IOC_RESTART_ACK: case IOC_ACK: miocack(wq, mp, 0, 0); break; case IOC_RESTART_REPLY: case IOC_REPLY: mp->b_datap->db_type = iocp->ioc_error == 0 ? M_IOCACK : M_IOCNAK; qreply(wq, mp); break; } } else if (cmd <= UNM_NIC_NAME && cmd >= UNM_CMD_START) { unm_nic_ioctl(adapter, cmd, wq, mp); return; } else { miocnak(wq, mp, 0, EINVAL); return; } } /* ARGSUSED */ static boolean_t ntxn_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) { switch (cap) { case MAC_CAPAB_HCKSUM: { uint32_t *txflags = cap_data; *txflags = (HCKSUM_ENABLE | HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM); } break; default: return (B_FALSE); } return (B_TRUE); } #define NETXEN_M_CALLBACK_FLAGS (MC_IOCTL | MC_GETCAPAB) static mac_callbacks_t ntxn_m_callbacks = { NETXEN_M_CALLBACK_FLAGS, ntxn_m_stat, ntxn_m_start, ntxn_m_stop, ntxn_m_promisc, ntxn_m_multicst, ntxn_m_unicst, ntxn_m_tx, ntxn_m_ioctl, ntxn_m_getcapab, NULL, /* mc_open */ NULL, /* mc_close */ NULL, /* mc_setprop */ NULL /* mc_getprop */ }; int unm_register_mac(unm_adapter *adapter) { int ret; mac_register_t *macp; unm_pauseparam_t pause; dev_info_t *dip = adapter->dip; if ((macp = mac_alloc(MAC_VERSION)) == NULL) { cmn_err(CE_WARN, "Memory not available\n"); return (DDI_FAILURE); } macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; macp->m_driver = adapter; macp->m_dip = dip; macp->m_instance = adapter->instance; macp->m_src_addr = adapter->mac_addr; macp->m_callbacks = &ntxn_m_callbacks; macp->m_min_sdu = 0; macp->m_max_sdu = adapter->mtu; #ifdef SOLARIS11 macp->m_margin = VLAN_TAGSZ; #endif /* SOLARIS11 */ ret = mac_register(macp, &adapter->mach); mac_free(macp); if (ret != 0) { cmn_err(CE_WARN, "mac_register failed for port %d\n", adapter->portnum); return (DDI_FAILURE); } unm_init_kstats(adapter, adapter->instance); /* Register NDD-tweakable parameters */ if (unm_nd_init(adapter)) { cmn_err(CE_WARN, "unm_nd_init() failed"); return (DDI_FAILURE); } pause.rx_pause = adapter->nd_params[PARAM_ADV_PAUSE_CAP].ndp_val; pause.tx_pause = adapter->nd_params[PARAM_ADV_ASYM_PAUSE_CAP].ndp_val; if (unm_nic_set_pauseparam(adapter, &pause)) { cmn_err(CE_WARN, "\nBad Pause settings RX %d, Tx %d", pause.rx_pause, pause.tx_pause); } adapter->nd_params[PARAM_PAUSE_CAP].ndp_val = pause.rx_pause; adapter->nd_params[PARAM_ASYM_PAUSE_CAP].ndp_val = pause.tx_pause; return (DDI_SUCCESS); }