| /* |
| * 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 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #include <hxge_impl.h> |
| #include <hxge_txdma.h> |
| #include <sys/llc1.h> |
| |
| uint32_t hxge_reclaim_pending = TXDMA_RECLAIM_PENDING_DEFAULT; |
| uint32_t hxge_tx_minfree = 64; |
| uint32_t hxge_tx_intr_thres = 0; |
| uint32_t hxge_tx_max_gathers = TX_MAX_GATHER_POINTERS; |
| uint32_t hxge_tx_tiny_pack = 1; |
| uint32_t hxge_tx_use_bcopy = 1; |
| |
| extern uint32_t hxge_tx_ring_size; |
| extern uint32_t hxge_bcopy_thresh; |
| extern uint32_t hxge_dvma_thresh; |
| extern uint32_t hxge_dma_stream_thresh; |
| extern dma_method_t hxge_force_dma; |
| |
| /* Device register access attributes for PIO. */ |
| extern ddi_device_acc_attr_t hxge_dev_reg_acc_attr; |
| |
| /* Device descriptor access attributes for DMA. */ |
| extern ddi_device_acc_attr_t hxge_dev_desc_dma_acc_attr; |
| |
| /* Device buffer access attributes for DMA. */ |
| extern ddi_device_acc_attr_t hxge_dev_buf_dma_acc_attr; |
| extern ddi_dma_attr_t hxge_desc_dma_attr; |
| extern ddi_dma_attr_t hxge_tx_dma_attr; |
| |
| static hxge_status_t hxge_map_txdma(p_hxge_t hxgep); |
| static void hxge_unmap_txdma(p_hxge_t hxgep); |
| static hxge_status_t hxge_txdma_hw_start(p_hxge_t hxgep); |
| static void hxge_txdma_hw_stop(p_hxge_t hxgep); |
| |
| static hxge_status_t hxge_map_txdma_channel(p_hxge_t hxgep, uint16_t channel, |
| p_hxge_dma_common_t *dma_buf_p, p_tx_ring_t *tx_desc_p, |
| uint32_t num_chunks, p_hxge_dma_common_t *dma_cntl_p, |
| p_tx_mbox_t *tx_mbox_p); |
| static void hxge_unmap_txdma_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p); |
| static hxge_status_t hxge_map_txdma_channel_buf_ring(p_hxge_t hxgep, uint16_t, |
| p_hxge_dma_common_t *, p_tx_ring_t *, uint32_t); |
| static void hxge_unmap_txdma_channel_buf_ring(p_hxge_t hxgep, |
| p_tx_ring_t tx_ring_p); |
| static void hxge_map_txdma_channel_cfg_ring(p_hxge_t, uint16_t, |
| p_hxge_dma_common_t *, p_tx_ring_t, p_tx_mbox_t *); |
| static void hxge_unmap_txdma_channel_cfg_ring(p_hxge_t hxgep, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p); |
| static hxge_status_t hxge_txdma_start_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p); |
| static hxge_status_t hxge_txdma_stop_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p); |
| static p_tx_ring_t hxge_txdma_get_ring(p_hxge_t hxgep, uint16_t channel); |
| static hxge_status_t hxge_tx_err_evnts(p_hxge_t hxgep, uint_t index, |
| p_hxge_ldv_t ldvp, tdc_stat_t cs); |
| static p_tx_mbox_t hxge_txdma_get_mbox(p_hxge_t hxgep, uint16_t channel); |
| static hxge_status_t hxge_txdma_fatal_err_recover(p_hxge_t hxgep, |
| uint16_t channel, p_tx_ring_t tx_ring_p); |
| static hxge_status_t hxge_tx_port_fatal_err_recover(p_hxge_t hxgep); |
| |
| hxge_status_t |
| hxge_init_txdma_channels(p_hxge_t hxgep) |
| { |
| hxge_status_t status = HXGE_OK; |
| block_reset_t reset_reg; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_init_txdma_channels")); |
| |
| /* |
| * Reset TDC block from PEU to cleanup any unknown configuration. |
| * This may be resulted from previous reboot. |
| */ |
| reset_reg.value = 0; |
| reset_reg.bits.tdc_rst = 1; |
| HXGE_REG_WR32(hxgep->hpi_handle, BLOCK_RESET, reset_reg.value); |
| |
| HXGE_DELAY(1000); |
| |
| status = hxge_map_txdma(hxgep); |
| if (status != HXGE_OK) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "<== hxge_init_txdma_channels: status 0x%x", status)); |
| return (status); |
| } |
| |
| status = hxge_txdma_hw_start(hxgep); |
| if (status != HXGE_OK) { |
| hxge_unmap_txdma(hxgep); |
| return (status); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_init_txdma_channels: status 0x%x", status)); |
| |
| return (HXGE_OK); |
| } |
| |
| void |
| hxge_uninit_txdma_channels(p_hxge_t hxgep) |
| { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_uninit_txdma_channels")); |
| |
| hxge_txdma_hw_stop(hxgep); |
| hxge_unmap_txdma(hxgep); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_uinit_txdma_channels")); |
| } |
| |
| void |
| hxge_setup_dma_common(p_hxge_dma_common_t dest_p, p_hxge_dma_common_t src_p, |
| uint32_t entries, uint32_t size) |
| { |
| size_t tsize; |
| *dest_p = *src_p; |
| tsize = size * entries; |
| dest_p->alength = tsize; |
| dest_p->nblocks = entries; |
| dest_p->block_size = size; |
| dest_p->offset += tsize; |
| |
| src_p->kaddrp = (caddr_t)dest_p->kaddrp + tsize; |
| src_p->alength -= tsize; |
| src_p->dma_cookie.dmac_laddress += tsize; |
| src_p->dma_cookie.dmac_size -= tsize; |
| } |
| |
| hxge_status_t |
| hxge_reset_txdma_channel(p_hxge_t hxgep, uint16_t channel, uint64_t reg_data) |
| { |
| hpi_status_t rs = HPI_SUCCESS; |
| hxge_status_t status = HXGE_OK; |
| hpi_handle_t handle; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, " ==> hxge_reset_txdma_channel")); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| if ((reg_data & TDC_TDR_RST_MASK) == TDC_TDR_RST_MASK) { |
| rs = hpi_txdma_channel_reset(handle, channel); |
| } else { |
| rs = hpi_txdma_channel_control(handle, TXDMA_RESET, channel); |
| } |
| |
| if (rs != HPI_SUCCESS) { |
| status = HXGE_ERROR | rs; |
| } |
| |
| /* |
| * Reset the tail (kick) register to 0. (Hardware will not reset it. Tx |
| * overflow fatal error if tail is not set to 0 after reset! |
| */ |
| TXDMA_REG_WRITE64(handle, TDC_TDR_KICK, channel, 0); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, " <== hxge_reset_txdma_channel")); |
| |
| return (status); |
| } |
| |
| hxge_status_t |
| hxge_init_txdma_channel_event_mask(p_hxge_t hxgep, uint16_t channel, |
| tdc_int_mask_t *mask_p) |
| { |
| hpi_handle_t handle; |
| hpi_status_t rs = HPI_SUCCESS; |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_init_txdma_channel_event_mask")); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| |
| /* |
| * Mask off tx_rng_oflow since it is a false alarm. The driver |
| * ensures not over flowing the hardware and check the hardware |
| * status. |
| */ |
| mask_p->bits.tx_rng_oflow = 1; |
| rs = hpi_txdma_event_mask(handle, OP_SET, channel, mask_p); |
| if (rs != HPI_SUCCESS) { |
| status = HXGE_ERROR | rs; |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_init_txdma_channel_event_mask")); |
| return (status); |
| } |
| |
| hxge_status_t |
| hxge_enable_txdma_channel(p_hxge_t hxgep, |
| uint16_t channel, p_tx_ring_t tx_desc_p, p_tx_mbox_t mbox_p) |
| { |
| hpi_handle_t handle; |
| hpi_status_t rs = HPI_SUCCESS; |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_enable_txdma_channel")); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| /* |
| * Use configuration data composed at init time. Write to hardware the |
| * transmit ring configurations. |
| */ |
| rs = hpi_txdma_ring_config(handle, OP_SET, channel, |
| (uint64_t *)&(tx_desc_p->tx_ring_cfig.value)); |
| |
| if (rs != HPI_SUCCESS) { |
| return (HXGE_ERROR | rs); |
| } |
| |
| /* Write to hardware the mailbox */ |
| rs = hpi_txdma_mbox_config(handle, OP_SET, channel, |
| (uint64_t *)&mbox_p->tx_mbox.dma_cookie.dmac_laddress); |
| |
| if (rs != HPI_SUCCESS) { |
| return (HXGE_ERROR | rs); |
| } |
| |
| /* Start the DMA engine. */ |
| rs = hpi_txdma_channel_init_enable(handle, channel); |
| if (rs != HPI_SUCCESS) { |
| return (HXGE_ERROR | rs); |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_enable_txdma_channel")); |
| return (status); |
| } |
| |
| void |
| hxge_fill_tx_hdr(p_mblk_t mp, boolean_t fill_len, boolean_t l4_cksum, |
| int pkt_len, uint8_t npads, p_tx_pkt_hdr_all_t pkthdrp) |
| { |
| p_tx_pkt_header_t hdrp; |
| p_mblk_t nmp; |
| uint64_t tmp; |
| size_t mblk_len; |
| size_t iph_len; |
| size_t hdrs_size; |
| uint8_t *ip_buf; |
| uint16_t eth_type; |
| uint8_t ipproto; |
| boolean_t is_vlan = B_FALSE; |
| size_t eth_hdr_size; |
| uint8_t hdrs_buf[sizeof (struct ether_header) + 64 + sizeof (uint32_t)]; |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_fill_tx_hdr: mp $%p", mp)); |
| |
| /* |
| * Caller should zero out the headers first. |
| */ |
| hdrp = (p_tx_pkt_header_t)&pkthdrp->pkthdr; |
| |
| if (fill_len) { |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_fill_tx_hdr: pkt_len %d npads %d", |
| pkt_len, npads)); |
| tmp = (uint64_t)pkt_len; |
| hdrp->value |= (tmp << TX_PKT_HEADER_TOT_XFER_LEN_SHIFT); |
| |
| goto fill_tx_header_done; |
| } |
| tmp = (uint64_t)npads; |
| hdrp->value |= (tmp << TX_PKT_HEADER_PAD_SHIFT); |
| |
| /* |
| * mp is the original data packet (does not include the Neptune |
| * transmit header). |
| */ |
| nmp = mp; |
| mblk_len = (size_t)nmp->b_wptr - (size_t)nmp->b_rptr; |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_fill_tx_hdr: mp $%p b_rptr $%p len %d", |
| mp, nmp->b_rptr, mblk_len)); |
| ip_buf = NULL; |
| bcopy(nmp->b_rptr, &hdrs_buf[0], sizeof (struct ether_vlan_header)); |
| eth_type = ntohs(((p_ether_header_t)hdrs_buf)->ether_type); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> : hxge_fill_tx_hdr: (value 0x%llx) ether type 0x%x", |
| eth_type, hdrp->value)); |
| |
| if (eth_type < ETHERMTU) { |
| tmp = 1ull; |
| hdrp->value |= (tmp << TX_PKT_HEADER_LLC_SHIFT); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: LLC value 0x%llx", hdrp->value)); |
| if (*(hdrs_buf + sizeof (struct ether_header)) == |
| LLC_SNAP_SAP) { |
| eth_type = ntohs(*((uint16_t *)(hdrs_buf + |
| sizeof (struct ether_header) + 6))); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: LLC ether type 0x%x", |
| eth_type)); |
| } else { |
| goto fill_tx_header_done; |
| } |
| } else if (eth_type == VLAN_ETHERTYPE) { |
| tmp = 1ull; |
| hdrp->value |= (tmp << TX_PKT_HEADER_VLAN__SHIFT); |
| |
| eth_type = ntohs(((struct ether_vlan_header *) |
| hdrs_buf)->ether_type); |
| is_vlan = B_TRUE; |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: VLAN value 0x%llx", |
| hdrp->value)); |
| } |
| if (!is_vlan) { |
| eth_hdr_size = sizeof (struct ether_header); |
| } else { |
| eth_hdr_size = sizeof (struct ether_vlan_header); |
| } |
| |
| switch (eth_type) { |
| case ETHERTYPE_IP: |
| if (mblk_len > eth_hdr_size + sizeof (uint8_t)) { |
| ip_buf = nmp->b_rptr + eth_hdr_size; |
| mblk_len -= eth_hdr_size; |
| iph_len = ((*ip_buf) & 0x0f); |
| if (mblk_len > (iph_len + sizeof (uint32_t))) { |
| ip_buf = nmp->b_rptr; |
| ip_buf += eth_hdr_size; |
| } else { |
| ip_buf = NULL; |
| } |
| } |
| if (ip_buf == NULL) { |
| hdrs_size = 0; |
| ((p_ether_header_t)hdrs_buf)->ether_type = 0; |
| while ((nmp) && (hdrs_size < sizeof (hdrs_buf))) { |
| mblk_len = (size_t)nmp->b_wptr - |
| (size_t)nmp->b_rptr; |
| if (mblk_len >= |
| (sizeof (hdrs_buf) - hdrs_size)) |
| mblk_len = sizeof (hdrs_buf) - |
| hdrs_size; |
| bcopy(nmp->b_rptr, |
| &hdrs_buf[hdrs_size], mblk_len); |
| hdrs_size += mblk_len; |
| nmp = nmp->b_cont; |
| } |
| ip_buf = hdrs_buf; |
| ip_buf += eth_hdr_size; |
| iph_len = ((*ip_buf) & 0x0f); |
| } |
| ipproto = ip_buf[9]; |
| |
| tmp = (uint64_t)iph_len; |
| hdrp->value |= (tmp << TX_PKT_HEADER_IHL_SHIFT); |
| tmp = (uint64_t)(eth_hdr_size >> 1); |
| hdrp->value |= (tmp << TX_PKT_HEADER_L3START_SHIFT); |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_fill_tx_hdr: IPv4 " |
| " iph_len %d l3start %d eth_hdr_size %d proto 0x%x" |
| "tmp 0x%x", iph_len, hdrp->bits.l3start, eth_hdr_size, |
| ipproto, tmp)); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: IP value 0x%llx", hdrp->value)); |
| break; |
| |
| case ETHERTYPE_IPV6: |
| hdrs_size = 0; |
| ((p_ether_header_t)hdrs_buf)->ether_type = 0; |
| while ((nmp) && (hdrs_size < sizeof (hdrs_buf))) { |
| mblk_len = (size_t)nmp->b_wptr - (size_t)nmp->b_rptr; |
| if (mblk_len >= (sizeof (hdrs_buf) - hdrs_size)) |
| mblk_len = sizeof (hdrs_buf) - hdrs_size; |
| bcopy(nmp->b_rptr, &hdrs_buf[hdrs_size], mblk_len); |
| hdrs_size += mblk_len; |
| nmp = nmp->b_cont; |
| } |
| ip_buf = hdrs_buf; |
| ip_buf += eth_hdr_size; |
| |
| tmp = 1ull; |
| hdrp->value |= (tmp << TX_PKT_HEADER_IP_VER_SHIFT); |
| |
| tmp = (eth_hdr_size >> 1); |
| hdrp->value |= (tmp << TX_PKT_HEADER_L3START_SHIFT); |
| |
| /* byte 6 is the next header protocol */ |
| ipproto = ip_buf[6]; |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_fill_tx_hdr: IPv6 " |
| " iph_len %d l3start %d eth_hdr_size %d proto 0x%x", |
| iph_len, hdrp->bits.l3start, eth_hdr_size, ipproto)); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_tx_pkt_hdr_init: IPv6 " |
| "value 0x%llx", hdrp->value)); |
| break; |
| |
| default: |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_fill_tx_hdr: non-IP")); |
| goto fill_tx_header_done; |
| } |
| |
| switch (ipproto) { |
| case IPPROTO_TCP: |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_fill_tx_hdr: TCP (cksum flag %d)", l4_cksum)); |
| if (l4_cksum) { |
| tmp = 1ull; |
| hdrp->value |= (tmp << TX_PKT_HEADER_PKT_TYPE_SHIFT); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: TCP CKSUM" |
| "value 0x%llx", hdrp->value)); |
| } |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: TCP value 0x%llx", hdrp->value)); |
| break; |
| |
| case IPPROTO_UDP: |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_fill_tx_hdr: UDP")); |
| if (l4_cksum) { |
| tmp = 0x2ull; |
| hdrp->value |= (tmp << TX_PKT_HEADER_PKT_TYPE_SHIFT); |
| } |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_hdr_init: UDP value 0x%llx", |
| hdrp->value)); |
| break; |
| |
| default: |
| goto fill_tx_header_done; |
| } |
| |
| fill_tx_header_done: |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_fill_tx_hdr: pkt_len %d npads %d value 0x%llx", |
| pkt_len, npads, hdrp->value)); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "<== hxge_fill_tx_hdr")); |
| } |
| |
| /*ARGSUSED*/ |
| p_mblk_t |
| hxge_tx_pkt_header_reserve(p_mblk_t mp, uint8_t *npads) |
| { |
| p_mblk_t newmp = NULL; |
| |
| if ((newmp = allocb(TX_PKT_HEADER_SIZE, BPRI_MED)) == NULL) { |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "<== hxge_tx_pkt_header_reserve: allocb failed")); |
| return (NULL); |
| } |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_header_reserve: get new mp")); |
| DB_TYPE(newmp) = M_DATA; |
| newmp->b_rptr = newmp->b_wptr = DB_LIM(newmp); |
| linkb(newmp, mp); |
| newmp->b_rptr -= TX_PKT_HEADER_SIZE; |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==>hxge_tx_pkt_header_reserve: b_rptr $%p b_wptr $%p", |
| newmp->b_rptr, newmp->b_wptr)); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "<== hxge_tx_pkt_header_reserve: use new mp")); |
| return (newmp); |
| } |
| |
| int |
| hxge_tx_pkt_nmblocks(p_mblk_t mp, int *tot_xfer_len_p) |
| { |
| uint_t nmblks; |
| ssize_t len; |
| uint_t pkt_len; |
| p_mblk_t nmp, bmp, tmp; |
| uint8_t *b_wptr; |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks: mp $%p rptr $%p wptr $%p len %d", |
| mp, mp->b_rptr, mp->b_wptr, MBLKL(mp))); |
| |
| nmp = mp; |
| bmp = mp; |
| nmblks = 0; |
| pkt_len = 0; |
| *tot_xfer_len_p = 0; |
| |
| while (nmp) { |
| len = MBLKL(nmp); |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_tx_pkt_nmblocks: " |
| "len %d pkt_len %d nmblks %d tot_xfer_len %d", |
| len, pkt_len, nmblks, *tot_xfer_len_p)); |
| |
| if (len <= 0) { |
| bmp = nmp; |
| nmp = nmp->b_cont; |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks:" |
| " len (0) pkt_len %d nmblks %d", pkt_len, nmblks)); |
| continue; |
| } |
| *tot_xfer_len_p += len; |
| HXGE_DEBUG_MSG((NULL, TX_CTL, "==> hxge_tx_pkt_nmblocks: " |
| "len %d pkt_len %d nmblks %d tot_xfer_len %d", |
| len, pkt_len, nmblks, *tot_xfer_len_p)); |
| |
| if (len < hxge_bcopy_thresh) { |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks: " |
| "len %d (< thresh) pkt_len %d nmblks %d", |
| len, pkt_len, nmblks)); |
| if (pkt_len == 0) |
| nmblks++; |
| pkt_len += len; |
| if (pkt_len >= hxge_bcopy_thresh) { |
| pkt_len = 0; |
| len = 0; |
| nmp = bmp; |
| } |
| } else { |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks: " |
| "len %d (> thresh) pkt_len %d nmblks %d", |
| len, pkt_len, nmblks)); |
| pkt_len = 0; |
| nmblks++; |
| /* |
| * Hardware limits the transfer length to 4K. If len is |
| * more than 4K, we need to break it up to at most 2 |
| * more blocks. |
| */ |
| if (len > TX_MAX_TRANSFER_LENGTH) { |
| uint32_t nsegs; |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks: " |
| "len %d pkt_len %d nmblks %d nsegs %d", |
| len, pkt_len, nmblks, nsegs)); |
| nsegs = 1; |
| if (len % (TX_MAX_TRANSFER_LENGTH * 2)) { |
| ++nsegs; |
| } |
| do { |
| b_wptr = nmp->b_rptr + |
| TX_MAX_TRANSFER_LENGTH; |
| nmp->b_wptr = b_wptr; |
| if ((tmp = dupb(nmp)) == NULL) { |
| return (0); |
| } |
| tmp->b_rptr = b_wptr; |
| tmp->b_wptr = nmp->b_wptr; |
| tmp->b_cont = nmp->b_cont; |
| nmp->b_cont = tmp; |
| nmblks++; |
| if (--nsegs) { |
| nmp = tmp; |
| } |
| } while (nsegs); |
| nmp = tmp; |
| } |
| } |
| |
| /* |
| * Hardware limits the transmit gather pointers to 15. |
| */ |
| if (nmp->b_cont && (nmblks + TX_GATHER_POINTERS_THRESHOLD) > |
| TX_MAX_GATHER_POINTERS) { |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "==> hxge_tx_pkt_nmblocks: pull msg - " |
| "len %d pkt_len %d nmblks %d", |
| len, pkt_len, nmblks)); |
| /* Pull all message blocks from b_cont */ |
| if ((tmp = msgpullup(nmp->b_cont, -1)) == NULL) { |
| return (0); |
| } |
| freemsg(nmp->b_cont); |
| nmp->b_cont = tmp; |
| pkt_len = 0; |
| } |
| bmp = nmp; |
| nmp = nmp->b_cont; |
| } |
| |
| HXGE_DEBUG_MSG((NULL, TX_CTL, |
| "<== hxge_tx_pkt_nmblocks: rptr $%p wptr $%p " |
| "nmblks %d len %d tot_xfer_len %d", |
| mp->b_rptr, mp->b_wptr, nmblks, MBLKL(mp), *tot_xfer_len_p)); |
| return (nmblks); |
| } |
| |
| boolean_t |
| hxge_txdma_reclaim(p_hxge_t hxgep, p_tx_ring_t tx_ring_p, int nmblks) |
| { |
| boolean_t status = B_TRUE; |
| p_hxge_dma_common_t tx_desc_dma_p; |
| hxge_dma_common_t desc_area; |
| p_tx_desc_t tx_desc_ring_vp; |
| p_tx_desc_t tx_desc_p; |
| p_tx_desc_t tx_desc_pp; |
| tx_desc_t r_tx_desc; |
| p_tx_msg_t tx_msg_ring; |
| p_tx_msg_t tx_msg_p; |
| hpi_handle_t handle; |
| tdc_tdr_head_t tx_head; |
| uint32_t pkt_len; |
| uint_t tx_rd_index; |
| uint16_t head_index, tail_index; |
| uint8_t tdc; |
| boolean_t head_wrap, tail_wrap; |
| p_hxge_tx_ring_stats_t tdc_stats; |
| tdc_byte_cnt_t byte_cnt; |
| tdc_tdr_qlen_t qlen; |
| int rc; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_reclaim")); |
| |
| status = ((tx_ring_p->descs_pending < hxge_reclaim_pending) && |
| (nmblks != 0)); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: pending %d reclaim %d nmblks %d", |
| tx_ring_p->descs_pending, hxge_reclaim_pending, nmblks)); |
| |
| if (!status) { |
| tx_desc_dma_p = &tx_ring_p->tdc_desc; |
| desc_area = tx_ring_p->tdc_desc; |
| tx_desc_ring_vp = tx_desc_dma_p->kaddrp; |
| tx_desc_ring_vp = (p_tx_desc_t)DMA_COMMON_VPTR(desc_area); |
| tx_rd_index = tx_ring_p->rd_index; |
| tx_desc_p = &tx_desc_ring_vp[tx_rd_index]; |
| tx_msg_ring = tx_ring_p->tx_msg_ring; |
| tx_msg_p = &tx_msg_ring[tx_rd_index]; |
| tdc = tx_ring_p->tdc; |
| tdc_stats = tx_ring_p->tdc_stats; |
| if (tx_ring_p->descs_pending > tdc_stats->tx_max_pend) { |
| tdc_stats->tx_max_pend = tx_ring_p->descs_pending; |
| } |
| tail_index = tx_ring_p->wr_index; |
| tail_wrap = tx_ring_p->wr_index_wrap; |
| |
| /* |
| * tdc_byte_cnt reg can be used to get bytes transmitted. It |
| * includes padding too in case of runt packets. |
| */ |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| TXDMA_REG_READ64(handle, TDC_BYTE_CNT, tdc, &byte_cnt.value); |
| tdc_stats->obytes_with_pad += byte_cnt.bits.byte_count; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: tdc %d tx_rd_index %d " |
| "tail_index %d tail_wrap %d tx_desc_p $%p ($%p) ", |
| tdc, tx_rd_index, tail_index, tail_wrap, |
| tx_desc_p, (*(uint64_t *)tx_desc_p))); |
| |
| /* |
| * Read the hardware maintained transmit head and wrap around |
| * bit. |
| */ |
| TXDMA_REG_READ64(handle, TDC_TDR_HEAD, tdc, &tx_head.value); |
| head_index = tx_head.bits.head; |
| head_wrap = tx_head.bits.wrap; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: " |
| "tx_rd_index %d tail %d tail_wrap %d head %d wrap %d", |
| tx_rd_index, tail_index, tail_wrap, head_index, head_wrap)); |
| |
| /* |
| * For debug only. This can be used to verify the qlen and make |
| * sure the hardware is wrapping the Tdr correctly. |
| */ |
| TXDMA_REG_READ64(handle, TDC_TDR_QLEN, tdc, &qlen.value); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: tdr_qlen %d tdr_pref_qlen %d", |
| qlen.bits.tdr_qlen, qlen.bits.tdr_pref_qlen)); |
| |
| if (head_index == tail_index) { |
| if (TXDMA_RING_EMPTY(head_index, head_wrap, tail_index, |
| tail_wrap) && (head_index == tx_rd_index)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: EMPTY")); |
| return (B_TRUE); |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: Checking if ring full")); |
| if (TXDMA_RING_FULL(head_index, head_wrap, tail_index, |
| tail_wrap)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: full")); |
| return (B_FALSE); |
| } |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: tx_rd_index and head_index")); |
| |
| /* XXXX: limit the # of reclaims */ |
| tx_desc_pp = &r_tx_desc; |
| while ((tx_rd_index != head_index) && |
| (tx_ring_p->descs_pending != 0)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: Checking if pending")); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: descs_pending %d ", |
| tx_ring_p->descs_pending)); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: " |
| "(tx_rd_index %d head_index %d (tx_desc_p $%p)", |
| tx_rd_index, head_index, tx_desc_p)); |
| |
| tx_desc_pp->value = tx_desc_p->value; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: " |
| "(tx_rd_index %d head_index %d " |
| "tx_desc_p $%p (desc value 0x%llx) ", |
| tx_rd_index, head_index, |
| tx_desc_pp, (*(uint64_t *)tx_desc_pp))); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: dump desc:")); |
| |
| /* |
| * tdc_byte_cnt reg can be used to get bytes |
| * transmitted |
| */ |
| pkt_len = tx_desc_pp->bits.tr_len; |
| tdc_stats->obytes += pkt_len; |
| tdc_stats->opackets += tx_desc_pp->bits.sop; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: pkt_len %d " |
| "tdc channel %d opackets %d", |
| pkt_len, tdc, tdc_stats->opackets)); |
| |
| if (tx_msg_p->flags.dma_type == USE_DVMA) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "tx_desc_p = $%p tx_desc_pp = $%p " |
| "index = %d", |
| tx_desc_p, tx_desc_pp, |
| tx_ring_p->rd_index)); |
| (void) dvma_unload(tx_msg_p->dvma_handle, |
| 0, -1); |
| tx_msg_p->dvma_handle = NULL; |
| if (tx_ring_p->dvma_wr_index == |
| tx_ring_p->dvma_wrap_mask) { |
| tx_ring_p->dvma_wr_index = 0; |
| } else { |
| tx_ring_p->dvma_wr_index++; |
| } |
| tx_ring_p->dvma_pending--; |
| } else if (tx_msg_p->flags.dma_type == USE_DMA) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: USE DMA")); |
| if (rc = ddi_dma_unbind_handle |
| (tx_msg_p->dma_handle)) { |
| cmn_err(CE_WARN, "hxge_reclaim: " |
| "ddi_dma_unbind_handle " |
| "failed. status %d", rc); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_reclaim: count packets")); |
| |
| /* |
| * count a chained packet only once. |
| */ |
| if (tx_msg_p->tx_message != NULL) { |
| freemsg(tx_msg_p->tx_message); |
| tx_msg_p->tx_message = NULL; |
| } |
| tx_msg_p->flags.dma_type = USE_NONE; |
| tx_rd_index = tx_ring_p->rd_index; |
| tx_rd_index = (tx_rd_index + 1) & |
| tx_ring_p->tx_wrap_mask; |
| tx_ring_p->rd_index = tx_rd_index; |
| tx_ring_p->descs_pending--; |
| tx_desc_p = &tx_desc_ring_vp[tx_rd_index]; |
| tx_msg_p = &tx_msg_ring[tx_rd_index]; |
| } |
| |
| status = (nmblks <= ((int)tx_ring_p->tx_ring_size - |
| (int)tx_ring_p->descs_pending - TX_FULL_MARK)); |
| if (status) { |
| (void) atomic_cas_32((uint32_t *)&tx_ring_p->queueing, |
| 1, 0); |
| } |
| } else { |
| status = (nmblks <= ((int)tx_ring_p->tx_ring_size - |
| (int)tx_ring_p->descs_pending - TX_FULL_MARK)); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_reclaim status = 0x%08x", status)); |
| return (status); |
| } |
| |
| uint_t |
| hxge_tx_intr(caddr_t arg1, caddr_t arg2) |
| { |
| p_hxge_ldv_t ldvp = (p_hxge_ldv_t)arg1; |
| p_hxge_t hxgep = (p_hxge_t)arg2; |
| p_hxge_ldg_t ldgp; |
| uint8_t channel; |
| uint32_t vindex; |
| hpi_handle_t handle; |
| tdc_stat_t cs; |
| p_tx_ring_t *tx_rings; |
| p_tx_ring_t tx_ring_p; |
| hpi_status_t rs = HPI_SUCCESS; |
| uint_t serviced = DDI_INTR_UNCLAIMED; |
| hxge_status_t status = HXGE_OK; |
| |
| if (ldvp == NULL) { |
| HXGE_DEBUG_MSG((NULL, INT_CTL, |
| "<== hxge_tx_intr: hxgep $%p ldvp $%p", hxgep, ldvp)); |
| return (DDI_INTR_UNCLAIMED); |
| } |
| |
| if (arg2 == NULL || (void *) ldvp->hxgep != arg2) { |
| hxgep = ldvp->hxgep; |
| } |
| |
| /* |
| * If the interface is not started, just swallow the interrupt |
| * and don't rearm the logical device. |
| */ |
| if (hxgep->hxge_mac_state != HXGE_MAC_STARTED) |
| return (DDI_INTR_CLAIMED); |
| |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, |
| "==> hxge_tx_intr: hxgep(arg2) $%p ldvp(arg1) $%p", hxgep, ldvp)); |
| |
| /* |
| * This interrupt handler is for a specific transmit dma channel. |
| */ |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| |
| /* Get the control and status for this channel. */ |
| channel = ldvp->channel; |
| ldgp = ldvp->ldgp; |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, |
| "==> hxge_tx_intr: hxgep $%p ldvp (ldvp) $%p channel %d", |
| hxgep, ldvp, channel)); |
| |
| rs = hpi_txdma_control_status(handle, OP_GET, channel, &cs); |
| vindex = ldvp->vdma_index; |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, |
| "==> hxge_tx_intr:channel %d ring index %d status 0x%08x", |
| channel, vindex, rs)); |
| |
| if (!rs && cs.bits.marked) { |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, |
| "==> hxge_tx_intr:channel %d ring index %d " |
| "status 0x%08x (marked bit set)", channel, vindex, rs)); |
| tx_rings = hxgep->tx_rings->rings; |
| tx_ring_p = tx_rings[vindex]; |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, |
| "==> hxge_tx_intr:channel %d ring index %d " |
| "status 0x%08x (marked bit set, calling reclaim)", |
| channel, vindex, rs)); |
| |
| MUTEX_ENTER(&tx_ring_p->lock); |
| (void) hxge_txdma_reclaim(hxgep, tx_rings[vindex], 0); |
| MUTEX_EXIT(&tx_ring_p->lock); |
| mac_tx_update(hxgep->mach); |
| } |
| |
| /* |
| * Process other transmit control and status. Check the ldv state. |
| */ |
| status = hxge_tx_err_evnts(hxgep, ldvp->vdma_index, ldvp, cs); |
| |
| /* Clear the error bits */ |
| RXDMA_REG_WRITE64(handle, TDC_STAT, channel, cs.value); |
| |
| /* |
| * Rearm this logical group if this is a single device group. |
| */ |
| if (ldgp->nldvs == 1) { |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, "==> hxge_tx_intr: rearm")); |
| if (status == HXGE_OK) { |
| (void) hpi_intr_ldg_mgmt_set(handle, ldgp->ldg, |
| B_TRUE, ldgp->ldg_timer); |
| } |
| } |
| HXGE_DEBUG_MSG((hxgep, INT_CTL, "<== hxge_tx_intr")); |
| serviced = DDI_INTR_CLAIMED; |
| return (serviced); |
| } |
| |
| void |
| hxge_txdma_stop(p_hxge_t hxgep) |
| { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_stop")); |
| |
| (void) hxge_tx_vmac_disable(hxgep); |
| (void) hxge_txdma_hw_mode(hxgep, HXGE_DMA_STOP); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_stop")); |
| } |
| |
| hxge_status_t |
| hxge_txdma_hw_mode(p_hxge_t hxgep, boolean_t enable) |
| { |
| int i, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t *tx_desc_rings; |
| hpi_handle_t handle; |
| hpi_status_t rs = HPI_SUCCESS; |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_mode: enable mode %d", enable)); |
| |
| if (!(hxgep->drv_state & STATE_HW_INITIALIZED)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_mode: not initialized")); |
| return (HXGE_ERROR); |
| } |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_mode: NULL global ring pointer")); |
| return (HXGE_ERROR); |
| } |
| tx_desc_rings = tx_rings->rings; |
| if (tx_desc_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_mode: NULL rings pointer")); |
| return (HXGE_ERROR); |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "<== hxge_txdma_hw_mode: no dma channel allocated")); |
| return (HXGE_ERROR); |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_mode: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_desc_rings, ndmas)); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| for (i = 0; i < ndmas; i++) { |
| if (tx_desc_rings[i] == NULL) { |
| continue; |
| } |
| channel = tx_desc_rings[i]->tdc; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_mode: channel %d", channel)); |
| if (enable) { |
| rs = hpi_txdma_channel_enable(handle, channel); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_mode: channel %d (enable) " |
| "rs 0x%x", channel, rs)); |
| } else { |
| /* |
| * Stop the dma channel and waits for the stop done. If |
| * the stop done bit is not set, then force an error so |
| * TXC will stop. All channels bound to this port need |
| * to be stopped and reset after injecting an interrupt |
| * error. |
| */ |
| rs = hpi_txdma_channel_disable(handle, channel); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_mode: channel %d (disable) " |
| "rs 0x%x", channel, rs)); |
| } |
| } |
| |
| status = ((rs == HPI_SUCCESS) ? HXGE_OK : HXGE_ERROR | rs); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_txdma_hw_mode: status 0x%x", status)); |
| |
| return (status); |
| } |
| |
| void |
| hxge_txdma_enable_channel(p_hxge_t hxgep, uint16_t channel) |
| { |
| hpi_handle_t handle; |
| |
| HXGE_DEBUG_MSG((hxgep, DMA_CTL, |
| "==> hxge_txdma_enable_channel: channel %d", channel)); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| /* enable the transmit dma channels */ |
| (void) hpi_txdma_channel_enable(handle, channel); |
| |
| HXGE_DEBUG_MSG((hxgep, DMA_CTL, "<== hxge_txdma_enable_channel")); |
| } |
| |
| void |
| hxge_txdma_disable_channel(p_hxge_t hxgep, uint16_t channel) |
| { |
| hpi_handle_t handle; |
| |
| HXGE_DEBUG_MSG((hxgep, DMA_CTL, |
| "==> hxge_txdma_disable_channel: channel %d", channel)); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| /* stop the transmit dma channels */ |
| (void) hpi_txdma_channel_disable(handle, channel); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_disable_channel")); |
| } |
| |
| int |
| hxge_txdma_stop_inj_err(p_hxge_t hxgep, int channel) |
| { |
| hpi_handle_t handle; |
| int status; |
| hpi_status_t rs = HPI_SUCCESS; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_stop_inj_err")); |
| |
| /* |
| * Stop the dma channel waits for the stop done. If the stop done bit |
| * is not set, then create an error. |
| */ |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| rs = hpi_txdma_channel_disable(handle, channel); |
| status = ((rs == HPI_SUCCESS) ? HXGE_OK : HXGE_ERROR | rs); |
| if (status == HXGE_OK) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_stop_inj_err (channel %d): " |
| "stopped OK", channel)); |
| return (status); |
| } |
| |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_stop_inj_err (channel): stop failed (0x%x) " |
| " (injected error but still not stopped)", channel, rs)); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_stop_inj_err")); |
| |
| return (status); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_fixup_txdma_rings(p_hxge_t hxgep) |
| { |
| int index, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_fixup_txdma_rings")); |
| |
| /* |
| * For each transmit channel, reclaim each descriptor and free buffers. |
| */ |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_fixup_txdma_rings: NULL ring pointer")); |
| return; |
| } |
| |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_fixup_txdma_rings: no channel allocated")); |
| return; |
| } |
| |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_fixup_txdma_rings: NULL rings pointer")); |
| return; |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_fixup_txdma_rings: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings->rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| channel = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_fixup_txdma_rings: channel %d", channel)); |
| hxge_txdma_fixup_channel(hxgep, tx_rings->rings[index], |
| channel); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_fixup_txdma_rings")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_fix_channel(p_hxge_t hxgep, uint16_t channel) |
| { |
| p_tx_ring_t ring_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_fix_channel")); |
| |
| ring_p = hxge_txdma_get_ring(hxgep, channel); |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_fix_channel")); |
| return; |
| } |
| |
| if (ring_p->tdc != channel) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fix_channel: channel not matched " |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| return; |
| } |
| |
| hxge_txdma_fixup_channel(hxgep, ring_p, channel); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_fix_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_fixup_channel(p_hxge_t hxgep, p_tx_ring_t ring_p, uint16_t channel) |
| { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_fixup_channel")); |
| |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_channel: NULL ring pointer")); |
| return; |
| } |
| if (ring_p->tdc != channel) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_channel: channel not matched " |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| return; |
| } |
| MUTEX_ENTER(&ring_p->lock); |
| (void) hxge_txdma_reclaim(hxgep, ring_p, 0); |
| |
| ring_p->rd_index = 0; |
| ring_p->wr_index = 0; |
| ring_p->ring_head.value = 0; |
| ring_p->ring_kick_tail.value = 0; |
| ring_p->descs_pending = 0; |
| MUTEX_EXIT(&ring_p->lock); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_fixup_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_hw_kick(p_hxge_t hxgep) |
| { |
| int index, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_hw_kick")); |
| |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_kick: NULL ring pointer")); |
| return; |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_kick: no channel allocated")); |
| return; |
| } |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_kick: NULL rings pointer")); |
| return; |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_kick: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings->rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| channel = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_kick: channel %d", channel)); |
| hxge_txdma_hw_kick_channel(hxgep, tx_rings->rings[index], |
| channel); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_hw_kick")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_kick_channel(p_hxge_t hxgep, uint16_t channel) |
| { |
| p_tx_ring_t ring_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_kick_channel")); |
| |
| ring_p = hxge_txdma_get_ring(hxgep, channel); |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, " hxge_txdma_kick_channel")); |
| return; |
| } |
| |
| if (ring_p->tdc != channel) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_kick_channel: channel not matched " |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| return; |
| } |
| |
| hxge_txdma_hw_kick_channel(hxgep, ring_p, channel); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_kick_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_hw_kick_channel(p_hxge_t hxgep, p_tx_ring_t ring_p, uint16_t channel) |
| { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_hw_kick_channel")); |
| |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_kick_channel: NULL ring pointer")); |
| return; |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_hw_kick_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_check_tx_hang(p_hxge_t hxgep) |
| { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_check_tx_hang")); |
| |
| /* |
| * Needs inputs from hardware for regs: head index had not moved since |
| * last timeout. packets not transmitted or stuffed registers. |
| */ |
| if (hxge_txdma_hung(hxgep)) { |
| hxge_fixup_hung_txdma_rings(hxgep); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_check_tx_hang")); |
| } |
| |
| int |
| hxge_txdma_hung(p_hxge_t hxgep) |
| { |
| int index, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t tx_ring_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_hung")); |
| |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hung: NULL ring pointer")); |
| return (B_FALSE); |
| } |
| |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hung: no channel allocated")); |
| return (B_FALSE); |
| } |
| |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hung: NULL rings pointer")); |
| return (B_FALSE); |
| } |
| |
| for (index = 0; index < ndmas; index++) { |
| channel = tx_rings->rings[index]->tdc; |
| tx_ring_p = tx_rings->rings[index]; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_hung: channel %d", channel)); |
| if (hxge_txdma_channel_hung(hxgep, tx_ring_p, channel)) { |
| return (B_TRUE); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_hung")); |
| |
| return (B_FALSE); |
| } |
| |
| int |
| hxge_txdma_channel_hung(p_hxge_t hxgep, p_tx_ring_t tx_ring_p, uint16_t channel) |
| { |
| uint16_t head_index, tail_index; |
| boolean_t head_wrap, tail_wrap; |
| hpi_handle_t handle; |
| tdc_tdr_head_t tx_head; |
| uint_t tx_rd_index; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_channel_hung")); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_channel_hung: channel %d", channel)); |
| MUTEX_ENTER(&tx_ring_p->lock); |
| (void) hxge_txdma_reclaim(hxgep, tx_ring_p, 0); |
| |
| tail_index = tx_ring_p->wr_index; |
| tail_wrap = tx_ring_p->wr_index_wrap; |
| tx_rd_index = tx_ring_p->rd_index; |
| MUTEX_EXIT(&tx_ring_p->lock); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_channel_hung: tdc %d tx_rd_index %d " |
| "tail_index %d tail_wrap %d ", |
| channel, tx_rd_index, tail_index, tail_wrap)); |
| /* |
| * Read the hardware maintained transmit head and wrap around bit. |
| */ |
| (void) hpi_txdma_ring_head_get(handle, channel, &tx_head); |
| head_index = tx_head.bits.head; |
| head_wrap = tx_head.bits.wrap; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_channel_hung: " |
| "tx_rd_index %d tail %d tail_wrap %d head %d wrap %d", |
| tx_rd_index, tail_index, tail_wrap, head_index, head_wrap)); |
| |
| if (TXDMA_RING_EMPTY(head_index, head_wrap, tail_index, tail_wrap) && |
| (head_index == tx_rd_index)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_channel_hung: EMPTY")); |
| return (B_FALSE); |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_channel_hung: Checking if ring full")); |
| if (TXDMA_RING_FULL(head_index, head_wrap, tail_index, tail_wrap)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_txdma_channel_hung: full")); |
| return (B_TRUE); |
| } |
| |
| /* If not full, check with hardware to see if it is hung */ |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_channel_hung")); |
| |
| return (B_FALSE); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_fixup_hung_txdma_rings(p_hxge_t hxgep) |
| { |
| int index, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_fixup_hung_txdma_rings")); |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_fixup_hung_txdma_rings: NULL ring pointer")); |
| return; |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_fixup_hung_txdma_rings: no channel allocated")); |
| return; |
| } |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_fixup_hung_txdma_rings: NULL rings pointer")); |
| return; |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_fixup_hung_txdma_rings: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings->rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| channel = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_fixup_hung_txdma_rings: channel %d", channel)); |
| hxge_txdma_fixup_hung_channel(hxgep, tx_rings->rings[index], |
| channel); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_fixup_hung_txdma_rings")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_fix_hung_channel(p_hxge_t hxgep, uint16_t channel) |
| { |
| p_tx_ring_t ring_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_fix_hung_channel")); |
| ring_p = hxge_txdma_get_ring(hxgep, channel); |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fix_hung_channel")); |
| return; |
| } |
| if (ring_p->tdc != channel) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fix_hung_channel: channel not matched " |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| return; |
| } |
| hxge_txdma_fixup_channel(hxgep, ring_p, channel); |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_fix_hung_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_txdma_fixup_hung_channel(p_hxge_t hxgep, p_tx_ring_t ring_p, |
| uint16_t channel) |
| { |
| hpi_handle_t handle; |
| int status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_fixup_hung_channel")); |
| |
| if (ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_hung_channel: NULL ring pointer")); |
| return; |
| } |
| if (ring_p->tdc != channel) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_hung_channel: channel " |
| "not matched ring tdc %d passed channel", |
| ring_p->tdc, channel)); |
| return; |
| } |
| /* Reclaim descriptors */ |
| MUTEX_ENTER(&ring_p->lock); |
| (void) hxge_txdma_reclaim(hxgep, ring_p, 0); |
| MUTEX_EXIT(&ring_p->lock); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| /* |
| * Stop the dma channel waits for the stop done. If the stop done bit |
| * is not set, then force an error. |
| */ |
| status = hpi_txdma_channel_disable(handle, channel); |
| if (!(status & HPI_TXDMA_STOP_FAILED)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_hung_channel: stopped OK " |
| "ring tdc %d passed channel %d", ring_p->tdc, channel)); |
| return; |
| } |
| /* Stop done bit will be set as a result of error injection */ |
| status = hpi_txdma_channel_disable(handle, channel); |
| if (!(status & HPI_TXDMA_STOP_FAILED)) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_hung_channel: stopped again" |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| return; |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_fixup_hung_channel: stop done still not set!! " |
| "ring tdc %d passed channel", ring_p->tdc, channel)); |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_fixup_hung_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| hxge_reclaim_rings(p_hxge_t hxgep) |
| { |
| int index, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t tx_ring_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_reclaim_ring")); |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_reclain_rimgs: NULL ring pointer")); |
| return; |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_reclain_rimgs: no channel allocated")); |
| return; |
| } |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_reclain_rimgs: NULL rings pointer")); |
| return; |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_reclain_rimgs: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings->rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| channel = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> reclain_rimgs: channel %d", |
| channel)); |
| tx_ring_p = tx_rings->rings[index]; |
| MUTEX_ENTER(&tx_ring_p->lock); |
| (void) hxge_txdma_reclaim(hxgep, tx_ring_p, channel); |
| MUTEX_EXIT(&tx_ring_p->lock); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_reclaim_rings")); |
| } |
| |
| /* |
| * Static functions start here. |
| */ |
| static hxge_status_t |
| hxge_map_txdma(p_hxge_t hxgep) |
| { |
| int i, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t *tx_desc_rings; |
| p_tx_mbox_areas_t tx_mbox_areas_p; |
| p_tx_mbox_t *tx_mbox_p; |
| p_hxge_dma_pool_t dma_buf_poolp; |
| p_hxge_dma_pool_t dma_cntl_poolp; |
| p_hxge_dma_common_t *dma_buf_p; |
| p_hxge_dma_common_t *dma_cntl_p; |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_map_txdma")); |
| |
| dma_buf_poolp = hxgep->tx_buf_pool_p; |
| dma_cntl_poolp = hxgep->tx_cntl_pool_p; |
| |
| if (!dma_buf_poolp->buf_allocated || !dma_cntl_poolp->buf_allocated) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_map_txdma: buf not allocated")); |
| return (HXGE_ERROR); |
| } |
| ndmas = dma_buf_poolp->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_map_txdma: no dma allocated")); |
| return (HXGE_ERROR); |
| } |
| dma_buf_p = dma_buf_poolp->dma_buf_pool_p; |
| dma_cntl_p = dma_cntl_poolp->dma_buf_pool_p; |
| |
| tx_rings = (p_tx_rings_t)KMEM_ZALLOC(sizeof (tx_rings_t), KM_SLEEP); |
| tx_desc_rings = (p_tx_ring_t *)KMEM_ZALLOC( |
| sizeof (p_tx_ring_t) * ndmas, KM_SLEEP); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_map_txdma: " |
| "tx_rings $%p tx_desc_rings $%p", tx_rings, tx_desc_rings)); |
| |
| tx_mbox_areas_p = (p_tx_mbox_areas_t) |
| KMEM_ZALLOC(sizeof (tx_mbox_areas_t), KM_SLEEP); |
| tx_mbox_p = (p_tx_mbox_t *)KMEM_ZALLOC( |
| sizeof (p_tx_mbox_t) * ndmas, KM_SLEEP); |
| |
| /* |
| * Map descriptors from the buffer pools for each dma channel. |
| */ |
| for (i = 0; i < ndmas; i++) { |
| /* |
| * Set up and prepare buffer blocks, descriptors and mailbox. |
| */ |
| channel = ((p_hxge_dma_common_t)dma_buf_p[i])->dma_channel; |
| status = hxge_map_txdma_channel(hxgep, channel, |
| (p_hxge_dma_common_t *)&dma_buf_p[i], |
| (p_tx_ring_t *)&tx_desc_rings[i], |
| dma_buf_poolp->num_chunks[i], |
| (p_hxge_dma_common_t *)&dma_cntl_p[i], |
| (p_tx_mbox_t *)&tx_mbox_p[i]); |
| if (status != HXGE_OK) { |
| goto hxge_map_txdma_fail1; |
| } |
| tx_desc_rings[i]->index = (uint16_t)i; |
| tx_desc_rings[i]->tdc_stats = &hxgep->statsp->tdc_stats[i]; |
| } |
| |
| tx_rings->ndmas = ndmas; |
| tx_rings->rings = tx_desc_rings; |
| hxgep->tx_rings = tx_rings; |
| tx_mbox_areas_p->txmbox_areas_p = tx_mbox_p; |
| hxgep->tx_mbox_areas_p = tx_mbox_areas_p; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_map_txdma: " |
| "tx_rings $%p rings $%p", hxgep->tx_rings, hxgep->tx_rings->rings)); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_map_txdma: " |
| "tx_rings $%p tx_desc_rings $%p", |
| hxgep->tx_rings, tx_desc_rings)); |
| |
| goto hxge_map_txdma_exit; |
| |
| hxge_map_txdma_fail1: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma: uninit tx desc " |
| "(status 0x%x channel %d i %d)", hxgep, status, channel, i)); |
| i--; |
| for (; i >= 0; i--) { |
| channel = ((p_hxge_dma_common_t)dma_buf_p[i])->dma_channel; |
| hxge_unmap_txdma_channel(hxgep, channel, tx_desc_rings[i], |
| tx_mbox_p[i]); |
| } |
| |
| KMEM_FREE(tx_desc_rings, sizeof (p_tx_ring_t) * ndmas); |
| KMEM_FREE(tx_rings, sizeof (tx_rings_t)); |
| KMEM_FREE(tx_mbox_p, sizeof (p_tx_mbox_t) * ndmas); |
| KMEM_FREE(tx_mbox_areas_p, sizeof (tx_mbox_areas_t)); |
| |
| hxge_map_txdma_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma: (status 0x%x channel %d)", status, channel)); |
| |
| return (status); |
| } |
| |
| static void |
| hxge_unmap_txdma(p_hxge_t hxgep) |
| { |
| int i, ndmas; |
| uint8_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t *tx_desc_rings; |
| p_tx_mbox_areas_t tx_mbox_areas_p; |
| p_tx_mbox_t *tx_mbox_p; |
| p_hxge_dma_pool_t dma_buf_poolp; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_unmap_txdma")); |
| |
| dma_buf_poolp = hxgep->tx_buf_pool_p; |
| if (!dma_buf_poolp->buf_allocated) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "==> hxge_unmap_txdma: buf not allocated")); |
| return; |
| } |
| ndmas = dma_buf_poolp->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_unmap_txdma: no dma allocated")); |
| return; |
| } |
| tx_rings = hxgep->tx_rings; |
| tx_desc_rings = tx_rings->rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_unmap_txdma: NULL ring pointer")); |
| return; |
| } |
| tx_desc_rings = tx_rings->rings; |
| if (tx_desc_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_unmap_txdma: NULL ring pointers")); |
| return; |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_unmap_txdma: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_desc_rings, ndmas)); |
| |
| tx_mbox_areas_p = hxgep->tx_mbox_areas_p; |
| tx_mbox_p = tx_mbox_areas_p->txmbox_areas_p; |
| |
| for (i = 0; i < ndmas; i++) { |
| channel = tx_desc_rings[i]->tdc; |
| (void) hxge_unmap_txdma_channel(hxgep, channel, |
| (p_tx_ring_t)tx_desc_rings[i], |
| (p_tx_mbox_t)tx_mbox_p[i]); |
| } |
| |
| KMEM_FREE(tx_desc_rings, sizeof (p_tx_ring_t) * ndmas); |
| KMEM_FREE(tx_rings, sizeof (tx_rings_t)); |
| KMEM_FREE(tx_mbox_p, sizeof (p_tx_mbox_t) * ndmas); |
| KMEM_FREE(tx_mbox_areas_p, sizeof (tx_mbox_areas_t)); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_unmap_txdma")); |
| } |
| |
| static hxge_status_t |
| hxge_map_txdma_channel(p_hxge_t hxgep, uint16_t channel, |
| p_hxge_dma_common_t *dma_buf_p, p_tx_ring_t *tx_desc_p, |
| uint32_t num_chunks, p_hxge_dma_common_t *dma_cntl_p, |
| p_tx_mbox_t *tx_mbox_p) |
| { |
| int status = HXGE_OK; |
| |
| /* |
| * Set up and prepare buffer blocks, descriptors and mailbox. |
| */ |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel (channel %d)", channel)); |
| |
| /* |
| * Transmit buffer blocks |
| */ |
| status = hxge_map_txdma_channel_buf_ring(hxgep, channel, |
| dma_buf_p, tx_desc_p, num_chunks); |
| if (status != HXGE_OK) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_map_txdma_channel (channel %d): " |
| "map buffer failed 0x%x", channel, status)); |
| goto hxge_map_txdma_channel_exit; |
| } |
| /* |
| * Transmit block ring, and mailbox. |
| */ |
| hxge_map_txdma_channel_cfg_ring(hxgep, channel, dma_cntl_p, *tx_desc_p, |
| tx_mbox_p); |
| |
| goto hxge_map_txdma_channel_exit; |
| |
| hxge_map_txdma_channel_fail1: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel: unmap buf" |
| "(status 0x%x channel %d)", status, channel)); |
| hxge_unmap_txdma_channel_buf_ring(hxgep, *tx_desc_p); |
| |
| hxge_map_txdma_channel_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_map_txdma_channel: (status 0x%x channel %d)", |
| status, channel)); |
| |
| return (status); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| hxge_unmap_txdma_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p) |
| { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_unmap_txdma_channel (channel %d)", channel)); |
| |
| /* unmap tx block ring, and mailbox. */ |
| (void) hxge_unmap_txdma_channel_cfg_ring(hxgep, tx_ring_p, tx_mbox_p); |
| |
| /* unmap buffer blocks */ |
| (void) hxge_unmap_txdma_channel_buf_ring(hxgep, tx_ring_p); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_unmap_txdma_channel")); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| hxge_map_txdma_channel_cfg_ring(p_hxge_t hxgep, uint16_t dma_channel, |
| p_hxge_dma_common_t *dma_cntl_p, p_tx_ring_t tx_ring_p, |
| p_tx_mbox_t *tx_mbox_p) |
| { |
| p_tx_mbox_t mboxp; |
| p_hxge_dma_common_t cntl_dmap; |
| p_hxge_dma_common_t dmap; |
| tdc_tdr_cfg_t *tx_ring_cfig_p; |
| tdc_tdr_kick_t *tx_ring_kick_p; |
| tdc_tdr_cfg_t *tx_cs_p; |
| tdc_int_mask_t *tx_evmask_p; |
| tdc_mbh_t *mboxh_p; |
| tdc_mbl_t *mboxl_p; |
| uint64_t tx_desc_len; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring")); |
| |
| cntl_dmap = *dma_cntl_p; |
| |
| dmap = (p_hxge_dma_common_t)&tx_ring_p->tdc_desc; |
| hxge_setup_dma_common(dmap, cntl_dmap, tx_ring_p->tx_ring_size, |
| sizeof (tx_desc_t)); |
| |
| /* |
| * Zero out transmit ring descriptors. |
| */ |
| bzero((caddr_t)dmap->kaddrp, dmap->alength); |
| tx_ring_cfig_p = &(tx_ring_p->tx_ring_cfig); |
| tx_ring_kick_p = &(tx_ring_p->tx_ring_kick); |
| tx_cs_p = &(tx_ring_p->tx_cs); |
| tx_evmask_p = &(tx_ring_p->tx_evmask); |
| tx_ring_cfig_p->value = 0; |
| tx_ring_kick_p->value = 0; |
| tx_cs_p->value = 0; |
| tx_evmask_p->value = 0; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring: channel %d des $%p", |
| dma_channel, dmap->dma_cookie.dmac_laddress)); |
| |
| tx_ring_cfig_p->value = 0; |
| |
| /* Hydra len is 11 bits and the lower 5 bits are 0s */ |
| tx_desc_len = (uint64_t)(tx_ring_p->tx_ring_size >> 5); |
| tx_ring_cfig_p->value = |
| (dmap->dma_cookie.dmac_laddress & TDC_TDR_CFG_ADDR_MASK) | |
| (tx_desc_len << TDC_TDR_CFG_LEN_SHIFT); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring: channel %d cfg 0x%llx", |
| dma_channel, tx_ring_cfig_p->value)); |
| |
| tx_cs_p->bits.reset = 1; |
| |
| /* Map in mailbox */ |
| mboxp = (p_tx_mbox_t)KMEM_ZALLOC(sizeof (tx_mbox_t), KM_SLEEP); |
| dmap = (p_hxge_dma_common_t)&mboxp->tx_mbox; |
| hxge_setup_dma_common(dmap, cntl_dmap, 1, sizeof (txdma_mailbox_t)); |
| mboxh_p = (tdc_mbh_t *)&tx_ring_p->tx_mbox_mbh; |
| mboxl_p = (tdc_mbl_t *)&tx_ring_p->tx_mbox_mbl; |
| mboxh_p->value = mboxl_p->value = 0; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring: mbox 0x%lx", |
| dmap->dma_cookie.dmac_laddress)); |
| |
| mboxh_p->bits.mbaddr = ((dmap->dma_cookie.dmac_laddress >> |
| TDC_MBH_ADDR_SHIFT) & TDC_MBH_MASK); |
| mboxl_p->bits.mbaddr = ((dmap->dma_cookie.dmac_laddress & |
| TDC_MBL_MASK) >> TDC_MBL_SHIFT); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring: mbox 0x%lx", |
| dmap->dma_cookie.dmac_laddress)); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_cfg_ring: hmbox $%p mbox $%p", |
| mboxh_p->bits.mbaddr, mboxl_p->bits.mbaddr)); |
| |
| /* |
| * Set page valid and no mask |
| */ |
| tx_ring_p->page_hdl.value = 0; |
| |
| *tx_mbox_p = mboxp; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_map_txdma_channel_cfg_ring")); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| hxge_unmap_txdma_channel_cfg_ring(p_hxge_t hxgep, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p) |
| { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_unmap_txdma_channel_cfg_ring: channel %d", |
| tx_ring_p->tdc)); |
| |
| KMEM_FREE(tx_mbox_p, sizeof (tx_mbox_t)); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_unmap_txdma_channel_cfg_ring")); |
| } |
| |
| static hxge_status_t |
| hxge_map_txdma_channel_buf_ring(p_hxge_t hxgep, uint16_t channel, |
| p_hxge_dma_common_t *dma_buf_p, |
| p_tx_ring_t *tx_desc_p, uint32_t num_chunks) |
| { |
| p_hxge_dma_common_t dma_bufp, tmp_bufp; |
| p_hxge_dma_common_t dmap; |
| hxge_os_dma_handle_t tx_buf_dma_handle; |
| p_tx_ring_t tx_ring_p; |
| p_tx_msg_t tx_msg_ring; |
| hxge_status_t status = HXGE_OK; |
| int ddi_status = DDI_SUCCESS; |
| int i, j, index; |
| uint32_t size, bsize; |
| uint32_t nblocks, nmsgs; |
| char qname[TASKQ_NAMELEN]; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_buf_ring")); |
| |
| dma_bufp = tmp_bufp = *dma_buf_p; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| " hxge_map_txdma_channel_buf_ring: channel %d to map %d " |
| "chunks bufp $%p", channel, num_chunks, dma_bufp)); |
| |
| nmsgs = 0; |
| for (i = 0; i < num_chunks; i++, tmp_bufp++) { |
| nmsgs += tmp_bufp->nblocks; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_buf_ring: channel %d " |
| "bufp $%p nblocks %d nmsgs %d", |
| channel, tmp_bufp, tmp_bufp->nblocks, nmsgs)); |
| } |
| if (!nmsgs) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_map_txdma_channel_buf_ring: channel %d " |
| "no msg blocks", channel)); |
| status = HXGE_ERROR; |
| |
| goto hxge_map_txdma_channel_buf_ring_exit; |
| } |
| |
| tx_ring_p = (p_tx_ring_t)KMEM_ZALLOC(sizeof (tx_ring_t), KM_SLEEP); |
| tx_ring_p->hxgep = hxgep; |
| (void) snprintf(qname, TASKQ_NAMELEN, "hxge_%d_%d", |
| hxgep->instance, channel); |
| tx_ring_p->taskq = ddi_taskq_create(hxgep->dip, qname, 1, |
| TASKQ_DEFAULTPRI, 0); |
| if (tx_ring_p->taskq == NULL) { |
| goto hxge_map_txdma_channel_buf_ring_fail1; |
| } |
| |
| MUTEX_INIT(&tx_ring_p->lock, NULL, MUTEX_DRIVER, |
| (void *) hxgep->interrupt_cookie); |
| /* |
| * Allocate transmit message rings and handles for packets not to be |
| * copied to premapped buffers. |
| */ |
| size = nmsgs * sizeof (tx_msg_t); |
| tx_msg_ring = KMEM_ZALLOC(size, KM_SLEEP); |
| for (i = 0; i < nmsgs; i++) { |
| ddi_status = ddi_dma_alloc_handle(hxgep->dip, &hxge_tx_dma_attr, |
| DDI_DMA_DONTWAIT, 0, &tx_msg_ring[i].dma_handle); |
| if (ddi_status != DDI_SUCCESS) { |
| status |= HXGE_DDI_FAILED; |
| break; |
| } |
| } |
| |
| if (i < nmsgs) { |
| HXGE_DEBUG_MSG((hxgep, HXGE_ERR_CTL, |
| "Allocate handles failed.")); |
| |
| goto hxge_map_txdma_channel_buf_ring_fail1; |
| } |
| tx_ring_p->tdc = channel; |
| tx_ring_p->tx_msg_ring = tx_msg_ring; |
| tx_ring_p->tx_ring_size = nmsgs; |
| tx_ring_p->num_chunks = num_chunks; |
| if (!hxge_tx_intr_thres) { |
| hxge_tx_intr_thres = tx_ring_p->tx_ring_size / 4; |
| } |
| tx_ring_p->tx_wrap_mask = tx_ring_p->tx_ring_size - 1; |
| tx_ring_p->rd_index = 0; |
| tx_ring_p->wr_index = 0; |
| tx_ring_p->ring_head.value = 0; |
| tx_ring_p->ring_kick_tail.value = 0; |
| tx_ring_p->descs_pending = 0; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_buf_ring: channel %d " |
| "actual tx desc max %d nmsgs %d (config hxge_tx_ring_size %d)", |
| channel, tx_ring_p->tx_ring_size, nmsgs, hxge_tx_ring_size)); |
| |
| /* |
| * Map in buffers from the buffer pool. |
| */ |
| index = 0; |
| bsize = dma_bufp->block_size; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_map_txdma_channel_buf_ring: " |
| "dma_bufp $%p tx_rng_p $%p tx_msg_rng_p $%p bsize %d", |
| dma_bufp, tx_ring_p, tx_msg_ring, bsize)); |
| |
| for (i = 0; i < num_chunks; i++, dma_bufp++) { |
| bsize = dma_bufp->block_size; |
| nblocks = dma_bufp->nblocks; |
| tx_buf_dma_handle = dma_bufp->dma_handle; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_buf_ring: dma chunk %d " |
| "size %d dma_bufp $%p", |
| i, sizeof (hxge_dma_common_t), dma_bufp)); |
| |
| for (j = 0; j < nblocks; j++) { |
| tx_msg_ring[index].buf_dma_handle = tx_buf_dma_handle; |
| tx_msg_ring[index].offset_index = j; |
| dmap = &tx_msg_ring[index++].buf_dma; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_map_txdma_channel_buf_ring: j %d" |
| "dmap $%p", i, dmap)); |
| hxge_setup_dma_common(dmap, dma_bufp, 1, bsize); |
| } |
| } |
| |
| if (i < num_chunks) { |
| status = HXGE_ERROR; |
| |
| goto hxge_map_txdma_channel_buf_ring_fail1; |
| } |
| |
| *tx_desc_p = tx_ring_p; |
| |
| goto hxge_map_txdma_channel_buf_ring_exit; |
| |
| hxge_map_txdma_channel_buf_ring_fail1: |
| if (tx_ring_p->taskq) { |
| ddi_taskq_destroy(tx_ring_p->taskq); |
| tx_ring_p->taskq = NULL; |
| } |
| |
| index--; |
| for (; index >= 0; index--) { |
| if (tx_msg_ring[index].dma_handle != NULL) { |
| ddi_dma_free_handle(&tx_msg_ring[index].dma_handle); |
| } |
| } |
| MUTEX_DESTROY(&tx_ring_p->lock); |
| KMEM_FREE(tx_msg_ring, size); |
| KMEM_FREE(tx_ring_p, sizeof (tx_ring_t)); |
| |
| status = HXGE_ERROR; |
| |
| hxge_map_txdma_channel_buf_ring_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_map_txdma_channel_buf_ring status 0x%x", status)); |
| |
| return (status); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| hxge_unmap_txdma_channel_buf_ring(p_hxge_t hxgep, p_tx_ring_t tx_ring_p) |
| { |
| p_tx_msg_t tx_msg_ring; |
| p_tx_msg_t tx_msg_p; |
| int i; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_unmap_txdma_channel_buf_ring")); |
| if (tx_ring_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_unmap_txdma_channel_buf_ring: NULL ringp")); |
| return; |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_unmap_txdma_channel_buf_ring: channel %d", |
| tx_ring_p->tdc)); |
| |
| MUTEX_ENTER(&tx_ring_p->lock); |
| tx_msg_ring = tx_ring_p->tx_msg_ring; |
| for (i = 0; i < tx_ring_p->tx_ring_size; i++) { |
| tx_msg_p = &tx_msg_ring[i]; |
| if (tx_msg_p->flags.dma_type == USE_DVMA) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "entry = %d", i)); |
| (void) dvma_unload(tx_msg_p->dvma_handle, 0, -1); |
| tx_msg_p->dvma_handle = NULL; |
| if (tx_ring_p->dvma_wr_index == |
| tx_ring_p->dvma_wrap_mask) { |
| tx_ring_p->dvma_wr_index = 0; |
| } else { |
| tx_ring_p->dvma_wr_index++; |
| } |
| tx_ring_p->dvma_pending--; |
| } else if (tx_msg_p->flags.dma_type == USE_DMA) { |
| if (ddi_dma_unbind_handle(tx_msg_p->dma_handle)) { |
| cmn_err(CE_WARN, "hxge_unmap_tx_bug_ring: " |
| "ddi_dma_unbind_handle failed."); |
| } |
| } |
| if (tx_msg_p->tx_message != NULL) { |
| freemsg(tx_msg_p->tx_message); |
| tx_msg_p->tx_message = NULL; |
| } |
| } |
| |
| for (i = 0; i < tx_ring_p->tx_ring_size; i++) { |
| if (tx_msg_ring[i].dma_handle != NULL) { |
| ddi_dma_free_handle(&tx_msg_ring[i].dma_handle); |
| } |
| } |
| MUTEX_EXIT(&tx_ring_p->lock); |
| |
| if (tx_ring_p->taskq) { |
| ddi_taskq_destroy(tx_ring_p->taskq); |
| tx_ring_p->taskq = NULL; |
| } |
| |
| MUTEX_DESTROY(&tx_ring_p->lock); |
| KMEM_FREE(tx_msg_ring, sizeof (tx_msg_t) * tx_ring_p->tx_ring_size); |
| KMEM_FREE(tx_ring_p, sizeof (tx_ring_t)); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_unmap_txdma_channel_buf_ring")); |
| } |
| |
| static hxge_status_t |
| hxge_txdma_hw_start(p_hxge_t hxgep) |
| { |
| int i, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t *tx_desc_rings; |
| p_tx_mbox_areas_t tx_mbox_areas_p; |
| p_tx_mbox_t *tx_mbox_p; |
| hxge_status_t status = HXGE_OK; |
| uint64_t tmp; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_start")); |
| |
| /* |
| * Initialize REORD Table 1. Disable VMAC 2. Reset the FIFO Err Stat. |
| * 3. Scrub memory and check for errors. |
| */ |
| (void) hxge_tx_vmac_disable(hxgep); |
| |
| /* |
| * Clear the error status |
| */ |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_FIFO_ERR_STAT, 0x7); |
| |
| /* |
| * Scrub the rtab memory for the TDC and reset the TDC. |
| */ |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_REORD_TBL_DATA_HI, 0x0ULL); |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_REORD_TBL_DATA_LO, 0x0ULL); |
| |
| for (i = 0; i < 256; i++) { |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_REORD_TBL_CMD, |
| (uint64_t)i); |
| |
| /* |
| * Write the command register with an indirect read instruction |
| */ |
| tmp = (0x1ULL << 30) | i; |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_REORD_TBL_CMD, tmp); |
| |
| /* |
| * Wait for status done |
| */ |
| tmp = 0; |
| do { |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_REORD_TBL_CMD, |
| &tmp); |
| } while (((tmp >> 31) & 0x1ULL) == 0x0); |
| } |
| |
| for (i = 0; i < 256; i++) { |
| /* |
| * Write the command register with an indirect read instruction |
| */ |
| tmp = (0x1ULL << 30) | i; |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_REORD_TBL_CMD, tmp); |
| |
| /* |
| * Wait for status done |
| */ |
| tmp = 0; |
| do { |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_REORD_TBL_CMD, |
| &tmp); |
| } while (((tmp >> 31) & 0x1ULL) == 0x0); |
| |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_REORD_TBL_DATA_HI, &tmp); |
| if (0x1ff00ULL != (0x1ffffULL & tmp)) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, "PANIC ReordTbl " |
| "unexpected data (hi), entry: %x, value: 0x%0llx\n", |
| i, (unsigned long long)tmp)); |
| status = HXGE_ERROR; |
| } |
| |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_REORD_TBL_DATA_LO, &tmp); |
| if (tmp != 0) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, "PANIC ReordTbl " |
| "unexpected data (lo), entry: %x\n", i)); |
| status = HXGE_ERROR; |
| } |
| |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_FIFO_ERR_STAT, &tmp); |
| if (tmp != 0) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, "PANIC ReordTbl " |
| "parity error, entry: %x, val 0x%llx\n", |
| i, (unsigned long long)tmp)); |
| status = HXGE_ERROR; |
| } |
| |
| HXGE_REG_RD64(hxgep->hpi_handle, TDC_FIFO_ERR_STAT, &tmp); |
| if (tmp != 0) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, "PANIC ReordTbl " |
| "parity error, entry: %x\n", i)); |
| status = HXGE_ERROR; |
| } |
| } |
| |
| if (status != HXGE_OK) |
| goto hxge_txdma_hw_start_exit; |
| |
| /* |
| * Reset FIFO Error Status for the TDC and enable FIFO error events. |
| */ |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_FIFO_ERR_STAT, 0x7); |
| HXGE_REG_WR64(hxgep->hpi_handle, TDC_FIFO_ERR_MASK, 0x0); |
| |
| /* |
| * Initialize the Transmit DMAs. |
| */ |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_start: NULL ring pointer")); |
| return (HXGE_ERROR); |
| } |
| |
| tx_desc_rings = tx_rings->rings; |
| if (tx_desc_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_start: NULL ring pointers")); |
| return (HXGE_ERROR); |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_start: no dma channel allocated")); |
| return (HXGE_ERROR); |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_start: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_desc_rings, ndmas)); |
| |
| tx_mbox_areas_p = hxgep->tx_mbox_areas_p; |
| tx_mbox_p = tx_mbox_areas_p->txmbox_areas_p; |
| |
| /* |
| * Init the DMAs. |
| */ |
| for (i = 0; i < ndmas; i++) { |
| channel = tx_desc_rings[i]->tdc; |
| status = hxge_txdma_start_channel(hxgep, channel, |
| (p_tx_ring_t)tx_desc_rings[i], |
| (p_tx_mbox_t)tx_mbox_p[i]); |
| if (status != HXGE_OK) { |
| goto hxge_txdma_hw_start_fail1; |
| } |
| } |
| |
| (void) hxge_tx_vmac_enable(hxgep); |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_start: tx_rings $%p rings $%p", |
| hxgep->tx_rings, hxgep->tx_rings->rings)); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_start: tx_rings $%p tx_desc_rings $%p", |
| hxgep->tx_rings, tx_desc_rings)); |
| |
| goto hxge_txdma_hw_start_exit; |
| |
| hxge_txdma_hw_start_fail1: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_start: disable (status 0x%x channel %d i %d)", |
| status, channel, i)); |
| |
| for (; i >= 0; i--) { |
| channel = tx_desc_rings[i]->tdc, |
| (void) hxge_txdma_stop_channel(hxgep, channel, |
| (p_tx_ring_t)tx_desc_rings[i], |
| (p_tx_mbox_t)tx_mbox_p[i]); |
| } |
| |
| hxge_txdma_hw_start_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_hw_start: (status 0x%x)", status)); |
| |
| return (status); |
| } |
| |
| static void |
| hxge_txdma_hw_stop(p_hxge_t hxgep) |
| { |
| int i, ndmas; |
| uint16_t channel; |
| p_tx_rings_t tx_rings; |
| p_tx_ring_t *tx_desc_rings; |
| p_tx_mbox_areas_t tx_mbox_areas_p; |
| p_tx_mbox_t *tx_mbox_p; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_stop")); |
| |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_stop: NULL ring pointer")); |
| return; |
| } |
| |
| tx_desc_rings = tx_rings->rings; |
| if (tx_desc_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_stop: NULL ring pointers")); |
| return; |
| } |
| |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_hw_stop: no dma channel allocated")); |
| return; |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_stop: " |
| "tx_rings $%p tx_desc_rings $%p", tx_rings, tx_desc_rings)); |
| |
| tx_mbox_areas_p = hxgep->tx_mbox_areas_p; |
| tx_mbox_p = tx_mbox_areas_p->txmbox_areas_p; |
| |
| for (i = 0; i < ndmas; i++) { |
| channel = tx_desc_rings[i]->tdc; |
| (void) hxge_txdma_stop_channel(hxgep, channel, |
| (p_tx_ring_t)tx_desc_rings[i], |
| (p_tx_mbox_t)tx_mbox_p[i]); |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_hw_stop: " |
| "tx_rings $%p tx_desc_rings $%p", tx_rings, tx_desc_rings)); |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_txdma_hw_stop")); |
| } |
| |
| static hxge_status_t |
| hxge_txdma_start_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p) |
| { |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_start_channel (channel %d)", channel)); |
| /* |
| * TXDMA/TXC must be in stopped state. |
| */ |
| (void) hxge_txdma_stop_inj_err(hxgep, channel); |
| |
| /* |
| * Reset TXDMA channel |
| */ |
| tx_ring_p->tx_cs.value = 0; |
| tx_ring_p->tx_cs.bits.reset = 1; |
| status = hxge_reset_txdma_channel(hxgep, channel, |
| tx_ring_p->tx_cs.value); |
| if (status != HXGE_OK) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_start_channel (channel %d)" |
| " reset channel failed 0x%x", channel, status)); |
| |
| goto hxge_txdma_start_channel_exit; |
| } |
| |
| /* |
| * Initialize the TXDMA channel specific FZC control configurations. |
| * These FZC registers are pertaining to each TX channel (i.e. logical |
| * pages). |
| */ |
| status = hxge_init_fzc_txdma_channel(hxgep, channel, |
| tx_ring_p, tx_mbox_p); |
| if (status != HXGE_OK) { |
| goto hxge_txdma_start_channel_exit; |
| } |
| |
| /* |
| * Initialize the event masks. |
| */ |
| tx_ring_p->tx_evmask.value = 0; |
| status = hxge_init_txdma_channel_event_mask(hxgep, |
| channel, &tx_ring_p->tx_evmask); |
| if (status != HXGE_OK) { |
| goto hxge_txdma_start_channel_exit; |
| } |
| |
| /* |
| * Load TXDMA descriptors, buffers, mailbox, initialise the DMA |
| * channels and enable each DMA channel. |
| */ |
| status = hxge_enable_txdma_channel(hxgep, channel, |
| tx_ring_p, tx_mbox_p); |
| if (status != HXGE_OK) { |
| goto hxge_txdma_start_channel_exit; |
| } |
| |
| hxge_txdma_start_channel_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_txdma_start_channel")); |
| |
| return (status); |
| } |
| |
| /*ARGSUSED*/ |
| static hxge_status_t |
| hxge_txdma_stop_channel(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p, p_tx_mbox_t tx_mbox_p) |
| { |
| int status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_stop_channel: channel %d", channel)); |
| |
| /* |
| * Stop (disable) TXDMA and TXC (if stop bit is set and STOP_N_GO bit |
| * not set, the TXDMA reset state will not be set if reset TXDMA. |
| */ |
| (void) hxge_txdma_stop_inj_err(hxgep, channel); |
| |
| /* |
| * Reset TXDMA channel |
| */ |
| tx_ring_p->tx_cs.value = 0; |
| tx_ring_p->tx_cs.bits.reset = 1; |
| status = hxge_reset_txdma_channel(hxgep, channel, |
| tx_ring_p->tx_cs.value); |
| if (status != HXGE_OK) { |
| goto hxge_txdma_stop_channel_exit; |
| } |
| |
| hxge_txdma_stop_channel_exit: |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "<== hxge_txdma_stop_channel")); |
| |
| return (status); |
| } |
| |
| static p_tx_ring_t |
| hxge_txdma_get_ring(p_hxge_t hxgep, uint16_t channel) |
| { |
| int index, ndmas; |
| uint16_t tdc; |
| p_tx_rings_t tx_rings; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_get_ring")); |
| |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_get_ring: NULL ring pointer")); |
| return (NULL); |
| } |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_get_ring: no channel allocated")); |
| return (NULL); |
| } |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_get_ring: NULL rings pointer")); |
| return (NULL); |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_get_ring: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| tdc = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_fixup_txdma_rings: channel %d", tdc)); |
| if (channel == tdc) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_get_ring: tdc %d ring $%p", |
| tdc, tx_rings->rings[index])); |
| return (p_tx_ring_t)(tx_rings->rings[index]); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_get_ring")); |
| |
| return (NULL); |
| } |
| |
| static p_tx_mbox_t |
| hxge_txdma_get_mbox(p_hxge_t hxgep, uint16_t channel) |
| { |
| int index, tdc, ndmas; |
| p_tx_rings_t tx_rings; |
| p_tx_mbox_areas_t tx_mbox_areas_p; |
| p_tx_mbox_t *tx_mbox_p; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_get_mbox")); |
| |
| tx_rings = hxgep->tx_rings; |
| if (tx_rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_txdma_get_mbox: NULL ring pointer")); |
| return (NULL); |
| } |
| tx_mbox_areas_p = hxgep->tx_mbox_areas_p; |
| if (tx_mbox_areas_p == NULL) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_txdma_get_mbox: NULL mbox pointer")); |
| return (NULL); |
| } |
| tx_mbox_p = tx_mbox_areas_p->txmbox_areas_p; |
| |
| ndmas = tx_rings->ndmas; |
| if (!ndmas) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_txdma_get_mbox: no channel allocated")); |
| return (NULL); |
| } |
| if (tx_rings->rings == NULL) { |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "<== hxge_txdma_get_mbox: NULL rings pointer")); |
| return (NULL); |
| } |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, "==> hxge_txdma_get_mbox: " |
| "tx_rings $%p tx_desc_rings $%p ndmas %d", |
| tx_rings, tx_rings, ndmas)); |
| |
| for (index = 0; index < ndmas; index++) { |
| tdc = tx_rings->rings[index]->tdc; |
| HXGE_DEBUG_MSG((hxgep, MEM3_CTL, |
| "==> hxge_txdma_get_mbox: channel %d", tdc)); |
| if (channel == tdc) { |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, |
| "<== hxge_txdma_get_mbox: tdc %d ring $%p", |
| tdc, tx_rings->rings[index])); |
| return (p_tx_mbox_t)(tx_mbox_p[index]); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_get_mbox")); |
| |
| return (NULL); |
| } |
| |
| /*ARGSUSED*/ |
| static hxge_status_t |
| hxge_tx_err_evnts(p_hxge_t hxgep, uint_t index, p_hxge_ldv_t ldvp, |
| tdc_stat_t cs) |
| { |
| hpi_handle_t handle; |
| uint8_t channel; |
| p_tx_ring_t *tx_rings; |
| p_tx_ring_t tx_ring_p; |
| p_hxge_tx_ring_stats_t tdc_stats; |
| boolean_t txchan_fatal = B_FALSE; |
| hxge_status_t status = HXGE_OK; |
| tdc_drop_cnt_t drop_cnt; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "==> hxge_tx_err_evnts")); |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| channel = ldvp->channel; |
| |
| tx_rings = hxgep->tx_rings->rings; |
| tx_ring_p = tx_rings[index]; |
| tdc_stats = tx_ring_p->tdc_stats; |
| |
| /* Get the error counts if any */ |
| TXDMA_REG_READ64(handle, TDC_DROP_CNT, channel, &drop_cnt.value); |
| tdc_stats->count_hdr_size_err += drop_cnt.bits.hdr_size_error_count; |
| tdc_stats->count_runt += drop_cnt.bits.runt_count; |
| tdc_stats->count_abort += drop_cnt.bits.abort_count; |
| |
| if (cs.bits.peu_resp_err) { |
| tdc_stats->peu_resp_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_PEU_RESP_ERR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: peu_resp_err", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.pkt_size_hdr_err) { |
| tdc_stats->pkt_size_hdr_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_PKT_SIZE_HDR_ERR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: pkt_size_hdr_err", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.runt_pkt_drop_err) { |
| tdc_stats->runt_pkt_drop_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_RUNT_PKT_DROP_ERR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: runt_pkt_drop_err", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.pkt_size_err) { |
| tdc_stats->pkt_size_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_PKT_SIZE_ERR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: pkt_size_err", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.tx_rng_oflow) { |
| tdc_stats->tx_rng_oflow++; |
| if (tdc_stats->tx_rng_oflow) |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: tx_rng_oflow", channel)); |
| } |
| |
| if (cs.bits.pref_par_err) { |
| tdc_stats->pref_par_err++; |
| |
| /* Get the address of parity error read data */ |
| TXDMA_REG_READ64(hxgep->hpi_handle, TDC_PREF_PAR_LOG, |
| channel, &tdc_stats->errlog.value); |
| |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_PREF_PAR_ERR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: pref_par_err", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.tdr_pref_cpl_to) { |
| tdc_stats->tdr_pref_cpl_to++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_TDR_PREF_CPL_TO); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: tdr_pref_cpl_to", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.pkt_cpl_to) { |
| tdc_stats->pkt_cpl_to++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_PKT_CPL_TO); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: pkt_cpl_to", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.invalid_sop) { |
| tdc_stats->invalid_sop++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_INVALID_SOP); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: invalid_sop", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| if (cs.bits.unexpected_sop) { |
| tdc_stats->unexpected_sop++; |
| HXGE_FM_REPORT_ERROR(hxgep, channel, |
| HXGE_FM_EREPORT_TDMC_UNEXPECTED_SOP); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_tx_err_evnts(channel %d): " |
| "fatal error: unexpected_sop", channel)); |
| txchan_fatal = B_TRUE; |
| } |
| |
| /* Clear error injection source in case this is an injected error */ |
| TXDMA_REG_WRITE64(hxgep->hpi_handle, TDC_STAT_INT_DBG, channel, 0); |
| |
| if (txchan_fatal) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| " hxge_tx_err_evnts: " |
| " fatal error on channel %d cs 0x%llx\n", |
| channel, cs.value)); |
| status = hxge_txdma_fatal_err_recover(hxgep, channel, |
| tx_ring_p); |
| if (status == HXGE_OK) { |
| FM_SERVICE_RESTORED(hxgep); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "<== hxge_tx_err_evnts")); |
| |
| return (status); |
| } |
| |
| hxge_status_t |
| hxge_txdma_handle_sys_errors(p_hxge_t hxgep) |
| { |
| hpi_handle_t handle; |
| hxge_status_t status = HXGE_OK; |
| tdc_fifo_err_stat_t fifo_stat; |
| hxge_tdc_sys_stats_t *tdc_sys_stats; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_txdma_handle_sys_errors")); |
| |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| |
| /* |
| * The FIFO is shared by all channels. |
| * Get the status of Reorder Buffer and Reorder Table Buffer Errors |
| */ |
| HXGE_REG_RD64(handle, TDC_FIFO_ERR_STAT, &fifo_stat.value); |
| |
| /* |
| * Clear the error bits. Note that writing a 1 clears the bit. Writing |
| * a 0 does nothing. |
| */ |
| HXGE_REG_WR64(handle, TDC_FIFO_ERR_STAT, fifo_stat.value); |
| |
| tdc_sys_stats = &hxgep->statsp->tdc_sys_stats; |
| if (fifo_stat.bits.reord_tbl_par_err) { |
| tdc_sys_stats->reord_tbl_par_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, NULL, |
| HXGE_FM_EREPORT_TDMC_REORD_TBL_PAR); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_handle_sys_errors: fatal error: " |
| "reord_tbl_par_err")); |
| } |
| |
| if (fifo_stat.bits.reord_buf_ded_err) { |
| tdc_sys_stats->reord_buf_ded_err++; |
| HXGE_FM_REPORT_ERROR(hxgep, NULL, |
| HXGE_FM_EREPORT_TDMC_REORD_BUF_DED); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_handle_sys_errors: " |
| "fatal error: reord_buf_ded_err")); |
| } |
| |
| if (fifo_stat.bits.reord_buf_sec_err) { |
| tdc_sys_stats->reord_buf_sec_err++; |
| if (tdc_sys_stats->reord_buf_sec_err == 1) |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_handle_sys_errors: " |
| "reord_buf_sec_err")); |
| } |
| |
| if (fifo_stat.bits.reord_tbl_par_err || |
| fifo_stat.bits.reord_buf_ded_err) { |
| status = hxge_tx_port_fatal_err_recover(hxgep); |
| if (status == HXGE_OK) { |
| FM_SERVICE_RESTORED(hxgep); |
| } |
| } |
| |
| HXGE_DEBUG_MSG((hxgep, TX_CTL, "<== hxge_txdma_handle_sys_errors")); |
| |
| return (status); |
| } |
| |
| static hxge_status_t |
| hxge_txdma_fatal_err_recover(p_hxge_t hxgep, uint16_t channel, |
| p_tx_ring_t tx_ring_p) |
| { |
| hpi_handle_t handle; |
| hpi_status_t rs = HPI_SUCCESS; |
| p_tx_mbox_t tx_mbox_p; |
| hxge_status_t status = HXGE_OK; |
| |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "==> hxge_txdma_fatal_err_recover")); |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "Recovering from TxDMAChannel#%d error...", channel)); |
| |
| /* |
| * Stop the dma channel waits for the stop done. If the stop done bit |
| * is not set, then create an error. |
| */ |
| handle = HXGE_DEV_HPI_HANDLE(hxgep); |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "stopping txdma channel(%d)", |
| channel)); |
| MUTEX_ENTER(&tx_ring_p->lock); |
| rs = hpi_txdma_channel_control(handle, TXDMA_STOP, channel); |
| if (rs != HPI_SUCCESS) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_fatal_err_recover (channel %d): " |
| "stop failed ", channel)); |
| |
| goto fail; |
| } |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "reclaiming txdma channel(%d)", |
| channel)); |
| (void) hxge_txdma_reclaim(hxgep, tx_ring_p, 0); |
| |
| /* |
| * Reset TXDMA channel |
| */ |
| HXGE_DEBUG_MSG((hxgep, TX_ERR_CTL, "resetting txdma channel(%d)", |
| channel)); |
| if ((rs = hpi_txdma_channel_control(handle, TXDMA_RESET, channel)) != |
| HPI_SUCCESS) { |
| HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL, |
| "==> hxge_txdma_fatal_err_recover (channel %d)" |
| " reset channel failed 0x%x", channel, rs)); |
| |
| goto fail; |
| } |
| /* |
| * Reset the tail (kick) register to 0. (Hardware will not reset it. Tx |
| * overflow fatal error if tail is not set to 0 after reset! |
| */ |
| TXDMA_REG_WRITE64(handle, TDC_TDR_KICK, channel, 0); |
|