/****************************************************************************** 
 | 
 * This software may be used and distributed according to the terms of 
 | 
 * the GNU General Public License (GPL), incorporated herein by reference. 
 | 
 * Drivers based on or derived from this code fall under the GPL and must 
 | 
 * retain the authorship, copyright and license notice.  This file is not 
 | 
 * a complete program and may only be used when the entire operating 
 | 
 * system is licensed under the GPL. 
 | 
 * See the file COPYING in this distribution for more information. 
 | 
 * 
 | 
 * vxge-traffic.c: Driver for Exar Corp's X3100 Series 10GbE PCIe I/O 
 | 
 *                 Virtualized Server Adapter. 
 | 
 * Copyright(c) 2002-2010 Exar Corp. 
 | 
 ******************************************************************************/ 
 | 
#include <linux/etherdevice.h> 
 | 
#include <linux/io-64-nonatomic-lo-hi.h> 
 | 
#include <linux/prefetch.h> 
 | 
  
 | 
#include "vxge-traffic.h" 
 | 
#include "vxge-config.h" 
 | 
#include "vxge-main.h" 
 | 
  
 | 
/* 
 | 
 * vxge_hw_vpath_intr_enable - Enable vpath interrupts. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * 
 | 
 * Enable vpath interrupts. The function is to be executed the last in 
 | 
 * vpath initialization sequence. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_intr_disable() 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_intr_enable(struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    struct vxge_hw_vpath_reg __iomem *vp_reg; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    if (vpath->vp_open == VXGE_HW_VP_NOT_OPEN) { 
 | 
        status = VXGE_HW_ERR_VPATH_NOT_OPEN; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vp_reg = vpath->vp_reg; 
 | 
  
 | 
    writeq(VXGE_HW_INTR_MASK_ALL, &vp_reg->kdfcctl_errors_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->general_errors_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->pci_config_errors_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->mrpcim_to_vpath_alarm_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_to_vpath_alarm_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->vpath_ppif_int_status); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_msg_to_vpath_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->vpath_pcipif_int_status); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->prc_alarm_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->wrdma_alarm_status); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->asic_ntwk_vp_err_reg); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->xgmac_vp_int_status); 
 | 
  
 | 
    readq(&vp_reg->vpath_general_int_status); 
 | 
  
 | 
    /* Mask unwanted interrupts */ 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->vpath_pcipif_int_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_msg_to_vpath_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_to_vpath_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->mrpcim_to_vpath_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->pci_config_errors_mask); 
 | 
  
 | 
    /* Unmask the individual interrupts */ 
 | 
  
 | 
    writeq((u32)vxge_bVALn((VXGE_HW_GENERAL_ERRORS_REG_DBLGEN_FIFO1_OVRFLOW| 
 | 
        VXGE_HW_GENERAL_ERRORS_REG_DBLGEN_FIFO2_OVRFLOW| 
 | 
        VXGE_HW_GENERAL_ERRORS_REG_STATSB_DROP_TIMEOUT_REQ| 
 | 
        VXGE_HW_GENERAL_ERRORS_REG_STATSB_PIF_CHAIN_ERR), 0, 32), 
 | 
        &vp_reg->general_errors_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32)vxge_bVALn((VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_OVRWR| 
 | 
        VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO2_OVRWR| 
 | 
        VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_POISON| 
 | 
        VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO2_POISON| 
 | 
        VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO1_DMA_ERR| 
 | 
        VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO2_DMA_ERR), 0, 32), 
 | 
        &vp_reg->kdfcctl_errors_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper(0, &vp_reg->vpath_ppif_int_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32)vxge_bVALn(VXGE_HW_PRC_ALARM_REG_PRC_RING_BUMP, 0, 32), 
 | 
        &vp_reg->prc_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper(0, &vp_reg->wrdma_alarm_mask); 
 | 
    __vxge_hw_pio_mem_write32_upper(0, &vp_reg->xgmac_vp_int_mask); 
 | 
  
 | 
    if (vpath->hldev->first_vp_id != vpath->vp_id) 
 | 
        __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->asic_ntwk_vp_err_mask); 
 | 
    else 
 | 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(( 
 | 
        VXGE_HW_ASIC_NTWK_VP_ERR_REG_XMACJ_NTWK_REAFFIRMED_FAULT | 
 | 
        VXGE_HW_ASIC_NTWK_VP_ERR_REG_XMACJ_NTWK_REAFFIRMED_OK), 0, 32), 
 | 
        &vp_reg->asic_ntwk_vp_err_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper(0, 
 | 
        &vp_reg->vpath_general_int_mask); 
 | 
exit: 
 | 
    return status; 
 | 
  
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_vpath_intr_disable - Disable vpath interrupts. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * 
 | 
 * Disable vpath interrupts. The function is to be executed the last in 
 | 
 * vpath initialization sequence. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_intr_enable() 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_intr_disable( 
 | 
            struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
    struct vxge_hw_vpath_reg __iomem *vp_reg; 
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    if (vpath->vp_open == VXGE_HW_VP_NOT_OPEN) { 
 | 
        status = VXGE_HW_ERR_VPATH_NOT_OPEN; 
 | 
        goto exit; 
 | 
    } 
 | 
    vp_reg = vpath->vp_reg; 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32)VXGE_HW_INTR_MASK_ALL, 
 | 
        &vp_reg->vpath_general_int_mask); 
 | 
  
 | 
    writeq(VXGE_HW_INTR_MASK_ALL, &vp_reg->kdfcctl_errors_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->general_errors_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->pci_config_errors_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->mrpcim_to_vpath_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_to_vpath_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->vpath_ppif_int_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->srpcim_msg_to_vpath_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->vpath_pcipif_int_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->wrdma_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->prc_alarm_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->xgmac_vp_int_mask); 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)VXGE_HW_INTR_MASK_ALL, 
 | 
            &vp_reg->asic_ntwk_vp_err_mask); 
 | 
  
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
void vxge_hw_vpath_tti_ci_set(struct __vxge_hw_fifo *fifo) 
 | 
{ 
 | 
    struct vxge_hw_vpath_reg __iomem *vp_reg; 
 | 
    struct vxge_hw_vp_config *config; 
 | 
    u64 val64; 
 | 
  
 | 
    if (fifo->config->enable != VXGE_HW_FIFO_ENABLE) 
 | 
        return; 
 | 
  
 | 
    vp_reg = fifo->vp_reg; 
 | 
    config = container_of(fifo->config, struct vxge_hw_vp_config, fifo); 
 | 
  
 | 
    if (config->tti.timer_ci_en != VXGE_HW_TIM_TIMER_CI_ENABLE) { 
 | 
        config->tti.timer_ci_en = VXGE_HW_TIM_TIMER_CI_ENABLE; 
 | 
        val64 = readq(&vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); 
 | 
        val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; 
 | 
        fifo->tim_tti_cfg1_saved = val64; 
 | 
        writeq(val64, &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); 
 | 
    } 
 | 
} 
 | 
  
 | 
void vxge_hw_vpath_dynamic_rti_ci_set(struct __vxge_hw_ring *ring) 
 | 
{ 
 | 
    u64 val64 = ring->tim_rti_cfg1_saved; 
 | 
  
 | 
    val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; 
 | 
    ring->tim_rti_cfg1_saved = val64; 
 | 
    writeq(val64, &ring->vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_RX]); 
 | 
} 
 | 
  
 | 
void vxge_hw_vpath_dynamic_tti_rtimer_set(struct __vxge_hw_fifo *fifo) 
 | 
{ 
 | 
    u64 val64 = fifo->tim_tti_cfg3_saved; 
 | 
    u64 timer = (fifo->rtimer * 1000) / 272; 
 | 
  
 | 
    val64 &= ~VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(0x3ffffff); 
 | 
    if (timer) 
 | 
        val64 |= VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(timer) | 
 | 
            VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_EVENT_SF(5); 
 | 
  
 | 
    writeq(val64, &fifo->vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_TX]); 
 | 
    /* tti_cfg3_saved is not updated again because it is 
 | 
     * initialized at one place only - init time. 
 | 
     */ 
 | 
} 
 | 
  
 | 
void vxge_hw_vpath_dynamic_rti_rtimer_set(struct __vxge_hw_ring *ring) 
 | 
{ 
 | 
    u64 val64 = ring->tim_rti_cfg3_saved; 
 | 
    u64 timer = (ring->rtimer * 1000) / 272; 
 | 
  
 | 
    val64 &= ~VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(0x3ffffff); 
 | 
    if (timer) 
 | 
        val64 |= VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(timer) | 
 | 
            VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_EVENT_SF(4); 
 | 
  
 | 
    writeq(val64, &ring->vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_RX]); 
 | 
    /* rti_cfg3_saved is not updated again because it is 
 | 
     * initialized at one place only - init time. 
 | 
     */ 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_channel_msix_mask - Mask MSIX Vector. 
 | 
 * @channel: Channel for rx or tx handle 
 | 
 * @msix_id:  MSIX ID 
 | 
 * 
 | 
 * The function masks the msix interrupt for the given msix_id 
 | 
 * 
 | 
 * Returns: 0 
 | 
 */ 
 | 
void vxge_hw_channel_msix_mask(struct __vxge_hw_channel *channel, int msix_id) 
 | 
{ 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32)vxge_bVALn(vxge_mBIT(msix_id >> 2), 0, 32), 
 | 
        &channel->common_reg->set_msix_mask_vect[msix_id%4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_channel_msix_unmask - Unmask the MSIX Vector. 
 | 
 * @channel: Channel for rx or tx handle 
 | 
 * @msix_id:  MSI ID 
 | 
 * 
 | 
 * The function unmasks the msix interrupt for the given msix_id 
 | 
 * 
 | 
 * Returns: 0 
 | 
 */ 
 | 
void 
 | 
vxge_hw_channel_msix_unmask(struct __vxge_hw_channel *channel, int msix_id) 
 | 
{ 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32)vxge_bVALn(vxge_mBIT(msix_id >> 2), 0, 32), 
 | 
        &channel->common_reg->clear_msix_mask_vect[msix_id%4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_channel_msix_clear - Unmask the MSIX Vector. 
 | 
 * @channel: Channel for rx or tx handle 
 | 
 * @msix_id:  MSI ID 
 | 
 * 
 | 
 * The function unmasks the msix interrupt for the given msix_id 
 | 
 * if configured in MSIX oneshot mode 
 | 
 * 
 | 
 * Returns: 0 
 | 
 */ 
 | 
void vxge_hw_channel_msix_clear(struct __vxge_hw_channel *channel, int msix_id) 
 | 
{ 
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32) vxge_bVALn(vxge_mBIT(msix_id >> 2), 0, 32), 
 | 
        &channel->common_reg->clr_msix_one_shot_vec[msix_id % 4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_set_intr_type - Updates the configuration 
 | 
 *        with new interrupt type. 
 | 
 * @hldev: HW device handle. 
 | 
 * @intr_mode: New interrupt type 
 | 
 */ 
 | 
u32 vxge_hw_device_set_intr_type(struct __vxge_hw_device *hldev, u32 intr_mode) 
 | 
{ 
 | 
  
 | 
    if ((intr_mode != VXGE_HW_INTR_MODE_IRQLINE) && 
 | 
       (intr_mode != VXGE_HW_INTR_MODE_MSIX) && 
 | 
       (intr_mode != VXGE_HW_INTR_MODE_MSIX_ONE_SHOT) && 
 | 
       (intr_mode != VXGE_HW_INTR_MODE_DEF)) 
 | 
        intr_mode = VXGE_HW_INTR_MODE_IRQLINE; 
 | 
  
 | 
    hldev->config.intr_mode = intr_mode; 
 | 
    return intr_mode; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_intr_enable - Enable interrupts. 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Enable Titan interrupts. The function is to be executed the last in 
 | 
 * Titan initialization sequence. 
 | 
 * 
 | 
 * See also: vxge_hw_device_intr_disable() 
 | 
 */ 
 | 
void vxge_hw_device_intr_enable(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 val64; 
 | 
    u32 val32; 
 | 
  
 | 
    vxge_hw_device_mask_all(hldev); 
 | 
  
 | 
    for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { 
 | 
  
 | 
        if (!(hldev->vpaths_deployed & vxge_mBIT(i))) 
 | 
            continue; 
 | 
  
 | 
        vxge_hw_vpath_intr_enable( 
 | 
            VXGE_HW_VIRTUAL_PATH_HANDLE(&hldev->virtual_paths[i])); 
 | 
    } 
 | 
  
 | 
    if (hldev->config.intr_mode == VXGE_HW_INTR_MODE_IRQLINE) { 
 | 
        val64 = hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_TX] | 
 | 
            hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_RX]; 
 | 
  
 | 
        if (val64 != 0) { 
 | 
            writeq(val64, &hldev->common_reg->tim_int_status0); 
 | 
  
 | 
            writeq(~val64, &hldev->common_reg->tim_int_mask0); 
 | 
        } 
 | 
  
 | 
        val32 = hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_TX] | 
 | 
            hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_RX]; 
 | 
  
 | 
        if (val32 != 0) { 
 | 
            __vxge_hw_pio_mem_write32_upper(val32, 
 | 
                    &hldev->common_reg->tim_int_status1); 
 | 
  
 | 
            __vxge_hw_pio_mem_write32_upper(~val32, 
 | 
                    &hldev->common_reg->tim_int_mask1); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    val64 = readq(&hldev->common_reg->titan_general_int_status); 
 | 
  
 | 
    vxge_hw_device_unmask_all(hldev); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_intr_disable - Disable Titan interrupts. 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Disable Titan interrupts. 
 | 
 * 
 | 
 * See also: vxge_hw_device_intr_enable() 
 | 
 */ 
 | 
void vxge_hw_device_intr_disable(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    u32 i; 
 | 
  
 | 
    vxge_hw_device_mask_all(hldev); 
 | 
  
 | 
    /* mask all the tim interrupts */ 
 | 
    writeq(VXGE_HW_INTR_MASK_ALL, &hldev->common_reg->tim_int_mask0); 
 | 
    __vxge_hw_pio_mem_write32_upper(VXGE_HW_DEFAULT_32, 
 | 
        &hldev->common_reg->tim_int_mask1); 
 | 
  
 | 
    for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { 
 | 
  
 | 
        if (!(hldev->vpaths_deployed & vxge_mBIT(i))) 
 | 
            continue; 
 | 
  
 | 
        vxge_hw_vpath_intr_disable( 
 | 
            VXGE_HW_VIRTUAL_PATH_HANDLE(&hldev->virtual_paths[i])); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_mask_all - Mask all device interrupts. 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Mask    all device interrupts. 
 | 
 * 
 | 
 * See also: vxge_hw_device_unmask_all() 
 | 
 */ 
 | 
void vxge_hw_device_mask_all(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    u64 val64; 
 | 
  
 | 
    val64 = VXGE_HW_TITAN_MASK_ALL_INT_ALARM | 
 | 
        VXGE_HW_TITAN_MASK_ALL_INT_TRAFFIC; 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(val64, 0, 32), 
 | 
                &hldev->common_reg->titan_mask_all_int); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_unmask_all - Unmask all device interrupts. 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Unmask all device interrupts. 
 | 
 * 
 | 
 * See also: vxge_hw_device_mask_all() 
 | 
 */ 
 | 
void vxge_hw_device_unmask_all(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    u64 val64 = 0; 
 | 
  
 | 
    if (hldev->config.intr_mode == VXGE_HW_INTR_MODE_IRQLINE) 
 | 
        val64 =  VXGE_HW_TITAN_MASK_ALL_INT_TRAFFIC; 
 | 
  
 | 
    __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(val64, 0, 32), 
 | 
            &hldev->common_reg->titan_mask_all_int); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_flush_io - Flush io writes. 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * The function    performs a read operation to flush io writes. 
 | 
 * 
 | 
 * Returns: void 
 | 
 */ 
 | 
void vxge_hw_device_flush_io(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    readl(&hldev->common_reg->titan_general_int_status); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * __vxge_hw_device_handle_error - Handle error 
 | 
 * @hldev: HW device 
 | 
 * @vp_id: Vpath Id 
 | 
 * @type: Error type. Please see enum vxge_hw_event{} 
 | 
 * 
 | 
 * Handle error. 
 | 
 */ 
 | 
static enum vxge_hw_status 
 | 
__vxge_hw_device_handle_error(struct __vxge_hw_device *hldev, u32 vp_id, 
 | 
                  enum vxge_hw_event type) 
 | 
{ 
 | 
    switch (type) { 
 | 
    case VXGE_HW_EVENT_UNKNOWN: 
 | 
        break; 
 | 
    case VXGE_HW_EVENT_RESET_START: 
 | 
    case VXGE_HW_EVENT_RESET_COMPLETE: 
 | 
    case VXGE_HW_EVENT_LINK_DOWN: 
 | 
    case VXGE_HW_EVENT_LINK_UP: 
 | 
        goto out; 
 | 
    case VXGE_HW_EVENT_ALARM_CLEARED: 
 | 
        goto out; 
 | 
    case VXGE_HW_EVENT_ECCERR: 
 | 
    case VXGE_HW_EVENT_MRPCIM_ECCERR: 
 | 
        goto out; 
 | 
    case VXGE_HW_EVENT_FIFO_ERR: 
 | 
    case VXGE_HW_EVENT_VPATH_ERR: 
 | 
    case VXGE_HW_EVENT_CRITICAL_ERR: 
 | 
    case VXGE_HW_EVENT_SERR: 
 | 
        break; 
 | 
    case VXGE_HW_EVENT_SRPCIM_SERR: 
 | 
    case VXGE_HW_EVENT_MRPCIM_SERR: 
 | 
        goto out; 
 | 
    case VXGE_HW_EVENT_SLOT_FREEZE: 
 | 
        break; 
 | 
    default: 
 | 
        vxge_assert(0); 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    /* notify driver */ 
 | 
    if (hldev->uld_callbacks->crit_err) 
 | 
        hldev->uld_callbacks->crit_err(hldev, 
 | 
            type, vp_id); 
 | 
out: 
 | 
  
 | 
    return VXGE_HW_OK; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * __vxge_hw_device_handle_link_down_ind 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Link down indication handler. The function is invoked by HW when 
 | 
 * Titan indicates that the link is down. 
 | 
 */ 
 | 
static enum vxge_hw_status 
 | 
__vxge_hw_device_handle_link_down_ind(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    /* 
 | 
     * If the previous link state is not down, return. 
 | 
     */ 
 | 
    if (hldev->link_state == VXGE_HW_LINK_DOWN) 
 | 
        goto exit; 
 | 
  
 | 
    hldev->link_state = VXGE_HW_LINK_DOWN; 
 | 
  
 | 
    /* notify driver */ 
 | 
    if (hldev->uld_callbacks->link_down) 
 | 
        hldev->uld_callbacks->link_down(hldev); 
 | 
exit: 
 | 
    return VXGE_HW_OK; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * __vxge_hw_device_handle_link_up_ind 
 | 
 * @hldev: HW device handle. 
 | 
 * 
 | 
 * Link up indication handler. The function is invoked by HW when 
 | 
 * Titan indicates that the link is up for programmable amount of time. 
 | 
 */ 
 | 
static enum vxge_hw_status 
 | 
__vxge_hw_device_handle_link_up_ind(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
    /* 
 | 
     * If the previous link state is not down, return. 
 | 
     */ 
 | 
    if (hldev->link_state == VXGE_HW_LINK_UP) 
 | 
        goto exit; 
 | 
  
 | 
    hldev->link_state = VXGE_HW_LINK_UP; 
 | 
  
 | 
    /* notify driver */ 
 | 
    if (hldev->uld_callbacks->link_up) 
 | 
        hldev->uld_callbacks->link_up(hldev); 
 | 
exit: 
 | 
    return VXGE_HW_OK; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * __vxge_hw_vpath_alarm_process - Process Alarms. 
 | 
 * @vpath: Virtual Path. 
 | 
 * @skip_alarms: Do not clear the alarms 
 | 
 * 
 | 
 * Process vpath alarms. 
 | 
 * 
 | 
 */ 
 | 
static enum vxge_hw_status 
 | 
__vxge_hw_vpath_alarm_process(struct __vxge_hw_virtualpath *vpath, 
 | 
                  u32 skip_alarms) 
 | 
{ 
 | 
    u64 val64; 
 | 
    u64 alarm_status; 
 | 
    u64 pic_status; 
 | 
    struct __vxge_hw_device *hldev = NULL; 
 | 
    enum vxge_hw_event alarm_event = VXGE_HW_EVENT_UNKNOWN; 
 | 
    u64 mask64; 
 | 
    struct vxge_hw_vpath_stats_sw_info *sw_stats; 
 | 
    struct vxge_hw_vpath_reg __iomem *vp_reg; 
 | 
  
 | 
    if (vpath == NULL) { 
 | 
        alarm_event = VXGE_HW_SET_LEVEL(VXGE_HW_EVENT_UNKNOWN, 
 | 
            alarm_event); 
 | 
        goto out2; 
 | 
    } 
 | 
  
 | 
    hldev = vpath->hldev; 
 | 
    vp_reg = vpath->vp_reg; 
 | 
    alarm_status = readq(&vp_reg->vpath_general_int_status); 
 | 
  
 | 
    if (alarm_status == VXGE_HW_ALL_FOXES) { 
 | 
        alarm_event = VXGE_HW_SET_LEVEL(VXGE_HW_EVENT_SLOT_FREEZE, 
 | 
            alarm_event); 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    sw_stats = vpath->sw_stats; 
 | 
  
 | 
    if (alarm_status & ~( 
 | 
        VXGE_HW_VPATH_GENERAL_INT_STATUS_PIC_INT | 
 | 
        VXGE_HW_VPATH_GENERAL_INT_STATUS_PCI_INT | 
 | 
        VXGE_HW_VPATH_GENERAL_INT_STATUS_WRDMA_INT | 
 | 
        VXGE_HW_VPATH_GENERAL_INT_STATUS_XMAC_INT)) { 
 | 
        sw_stats->error_stats.unknown_alarms++; 
 | 
  
 | 
        alarm_event = VXGE_HW_SET_LEVEL(VXGE_HW_EVENT_UNKNOWN, 
 | 
            alarm_event); 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    if (alarm_status & VXGE_HW_VPATH_GENERAL_INT_STATUS_XMAC_INT) { 
 | 
  
 | 
        val64 = readq(&vp_reg->xgmac_vp_int_status); 
 | 
  
 | 
        if (val64 & 
 | 
        VXGE_HW_XGMAC_VP_INT_STATUS_ASIC_NTWK_VP_ERR_ASIC_NTWK_VP_INT) { 
 | 
  
 | 
            val64 = readq(&vp_reg->asic_ntwk_vp_err_reg); 
 | 
  
 | 
            if (((val64 & 
 | 
                  VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT) && 
 | 
                 (!(val64 & 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK))) || 
 | 
                ((val64 & 
 | 
                 VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) && 
 | 
                 (!(val64 & 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) 
 | 
                     ))) { 
 | 
                sw_stats->error_stats.network_sustained_fault++; 
 | 
  
 | 
                writeq( 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT, 
 | 
                    &vp_reg->asic_ntwk_vp_err_mask); 
 | 
  
 | 
                __vxge_hw_device_handle_link_down_ind(hldev); 
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_LINK_DOWN, alarm_event); 
 | 
            } 
 | 
  
 | 
            if (((val64 & 
 | 
                  VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK) && 
 | 
                 (!(val64 & 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT))) || 
 | 
                ((val64 & 
 | 
                  VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) && 
 | 
                 (!(val64 & 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) 
 | 
                     ))) { 
 | 
  
 | 
                sw_stats->error_stats.network_sustained_ok++; 
 | 
  
 | 
                writeq( 
 | 
                VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK, 
 | 
                    &vp_reg->asic_ntwk_vp_err_mask); 
 | 
  
 | 
                __vxge_hw_device_handle_link_up_ind(hldev); 
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_LINK_UP, alarm_event); 
 | 
            } 
 | 
  
 | 
            writeq(VXGE_HW_INTR_MASK_ALL, 
 | 
                &vp_reg->asic_ntwk_vp_err_reg); 
 | 
  
 | 
            alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                VXGE_HW_EVENT_ALARM_CLEARED, alarm_event); 
 | 
  
 | 
            if (skip_alarms) 
 | 
                return VXGE_HW_OK; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    if (alarm_status & VXGE_HW_VPATH_GENERAL_INT_STATUS_PIC_INT) { 
 | 
  
 | 
        pic_status = readq(&vp_reg->vpath_ppif_int_status); 
 | 
  
 | 
        if (pic_status & 
 | 
            VXGE_HW_VPATH_PPIF_INT_STATUS_GENERAL_ERRORS_GENERAL_INT) { 
 | 
  
 | 
            val64 = readq(&vp_reg->general_errors_reg); 
 | 
            mask64 = readq(&vp_reg->general_errors_mask); 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_GENERAL_ERRORS_REG_INI_SERR_DET) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.ini_serr_det++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_SERR, alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_GENERAL_ERRORS_REG_DBLGEN_FIFO0_OVRFLOW) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.dblgen_fifo0_overflow++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_FIFO_ERR, alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_GENERAL_ERRORS_REG_STATSB_PIF_CHAIN_ERR) & 
 | 
                ~mask64) 
 | 
                sw_stats->error_stats.statsb_pif_chain_error++; 
 | 
  
 | 
            if ((val64 & 
 | 
               VXGE_HW_GENERAL_ERRORS_REG_STATSB_DROP_TIMEOUT_REQ) & 
 | 
                ~mask64) 
 | 
                sw_stats->error_stats.statsb_drop_timeout++; 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_GENERAL_ERRORS_REG_TGT_ILLEGAL_ACCESS) & 
 | 
                ~mask64) 
 | 
                sw_stats->error_stats.target_illegal_access++; 
 | 
  
 | 
            if (!skip_alarms) { 
 | 
                writeq(VXGE_HW_INTR_MASK_ALL, 
 | 
                    &vp_reg->general_errors_reg); 
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_ALARM_CLEARED, 
 | 
                    alarm_event); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (pic_status & 
 | 
            VXGE_HW_VPATH_PPIF_INT_STATUS_KDFCCTL_ERRORS_KDFCCTL_INT) { 
 | 
  
 | 
            val64 = readq(&vp_reg->kdfcctl_errors_reg); 
 | 
            mask64 = readq(&vp_reg->kdfcctl_errors_mask); 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO0_OVRWR) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.kdfcctl_fifo0_overwrite++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_FIFO_ERR, 
 | 
                    alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO0_POISON) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.kdfcctl_fifo0_poison++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_FIFO_ERR, 
 | 
                    alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & 
 | 
                VXGE_HW_KDFCCTL_ERRORS_REG_KDFCCTL_FIFO0_DMA_ERR) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.kdfcctl_fifo0_dma_error++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_FIFO_ERR, 
 | 
                    alarm_event); 
 | 
            } 
 | 
  
 | 
            if (!skip_alarms) { 
 | 
                writeq(VXGE_HW_INTR_MASK_ALL, 
 | 
                    &vp_reg->kdfcctl_errors_reg); 
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_ALARM_CLEARED, 
 | 
                    alarm_event); 
 | 
            } 
 | 
        } 
 | 
  
 | 
    } 
 | 
  
 | 
    if (alarm_status & VXGE_HW_VPATH_GENERAL_INT_STATUS_WRDMA_INT) { 
 | 
  
 | 
        val64 = readq(&vp_reg->wrdma_alarm_status); 
 | 
  
 | 
        if (val64 & VXGE_HW_WRDMA_ALARM_STATUS_PRC_ALARM_PRC_INT) { 
 | 
  
 | 
            val64 = readq(&vp_reg->prc_alarm_reg); 
 | 
            mask64 = readq(&vp_reg->prc_alarm_mask); 
 | 
  
 | 
            if ((val64 & VXGE_HW_PRC_ALARM_REG_PRC_RING_BUMP)& 
 | 
                ~mask64) 
 | 
                sw_stats->error_stats.prc_ring_bumps++; 
 | 
  
 | 
            if ((val64 & VXGE_HW_PRC_ALARM_REG_PRC_RXDCM_SC_ERR) & 
 | 
                ~mask64) { 
 | 
                sw_stats->error_stats.prc_rxdcm_sc_err++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_VPATH_ERR, 
 | 
                    alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & VXGE_HW_PRC_ALARM_REG_PRC_RXDCM_SC_ABORT) 
 | 
                & ~mask64) { 
 | 
                sw_stats->error_stats.prc_rxdcm_sc_abort++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                        VXGE_HW_EVENT_VPATH_ERR, 
 | 
                        alarm_event); 
 | 
            } 
 | 
  
 | 
            if ((val64 & VXGE_HW_PRC_ALARM_REG_PRC_QUANTA_SIZE_ERR) 
 | 
                 & ~mask64) { 
 | 
                sw_stats->error_stats.prc_quanta_size_err++; 
 | 
  
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                    VXGE_HW_EVENT_VPATH_ERR, 
 | 
                    alarm_event); 
 | 
            } 
 | 
  
 | 
            if (!skip_alarms) { 
 | 
                writeq(VXGE_HW_INTR_MASK_ALL, 
 | 
                    &vp_reg->prc_alarm_reg); 
 | 
                alarm_event = VXGE_HW_SET_LEVEL( 
 | 
                        VXGE_HW_EVENT_ALARM_CLEARED, 
 | 
                        alarm_event); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
out: 
 | 
    hldev->stats.sw_dev_err_stats.vpath_alarms++; 
 | 
out2: 
 | 
    if ((alarm_event == VXGE_HW_EVENT_ALARM_CLEARED) || 
 | 
        (alarm_event == VXGE_HW_EVENT_UNKNOWN)) 
 | 
        return VXGE_HW_OK; 
 | 
  
 | 
    __vxge_hw_device_handle_error(hldev, vpath->vp_id, alarm_event); 
 | 
  
 | 
    if (alarm_event == VXGE_HW_EVENT_SERR) 
 | 
        return VXGE_HW_ERR_CRITICAL; 
 | 
  
 | 
    return (alarm_event == VXGE_HW_EVENT_SLOT_FREEZE) ? 
 | 
        VXGE_HW_ERR_SLOT_FREEZE : 
 | 
        (alarm_event == VXGE_HW_EVENT_FIFO_ERR) ? VXGE_HW_ERR_FIFO : 
 | 
        VXGE_HW_ERR_VPATH; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_begin_irq - Begin IRQ processing. 
 | 
 * @hldev: HW device handle. 
 | 
 * @skip_alarms: Do not clear the alarms 
 | 
 * @reason: "Reason" for the interrupt, the value of Titan's 
 | 
 *    general_int_status register. 
 | 
 * 
 | 
 * The function    performs two actions, It first checks whether (shared IRQ) the 
 | 
 * interrupt was raised    by the device. Next, it    masks the device interrupts. 
 | 
 * 
 | 
 * Note: 
 | 
 * vxge_hw_device_begin_irq() does not flush MMIO writes through the 
 | 
 * bridge. Therefore, two back-to-back interrupts are potentially possible. 
 | 
 * 
 | 
 * Returns: 0, if the interrupt    is not "ours" (note that in this case the 
 | 
 * device remain enabled). 
 | 
 * Otherwise, vxge_hw_device_begin_irq() returns 64bit general adapter 
 | 
 * status. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_device_begin_irq(struct __vxge_hw_device *hldev, 
 | 
                         u32 skip_alarms, u64 *reason) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 val64; 
 | 
    u64 adapter_status; 
 | 
    u64 vpath_mask; 
 | 
    enum vxge_hw_status ret = VXGE_HW_OK; 
 | 
  
 | 
    val64 = readq(&hldev->common_reg->titan_general_int_status); 
 | 
  
 | 
    if (unlikely(!val64)) { 
 | 
        /* not Titan interrupt    */ 
 | 
        *reason    = 0; 
 | 
        ret = VXGE_HW_ERR_WRONG_IRQ; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    if (unlikely(val64 == VXGE_HW_ALL_FOXES)) { 
 | 
  
 | 
        adapter_status = readq(&hldev->common_reg->adapter_status); 
 | 
  
 | 
        if (adapter_status == VXGE_HW_ALL_FOXES) { 
 | 
  
 | 
            __vxge_hw_device_handle_error(hldev, 
 | 
                NULL_VPID, VXGE_HW_EVENT_SLOT_FREEZE); 
 | 
            *reason    = 0; 
 | 
            ret = VXGE_HW_ERR_SLOT_FREEZE; 
 | 
            goto exit; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    hldev->stats.sw_dev_info_stats.total_intr_cnt++; 
 | 
  
 | 
    *reason    = val64; 
 | 
  
 | 
    vpath_mask = hldev->vpaths_deployed >> 
 | 
                (64 - VXGE_HW_MAX_VIRTUAL_PATHS); 
 | 
  
 | 
    if (val64 & 
 | 
        VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_TRAFFIC_INT(vpath_mask)) { 
 | 
        hldev->stats.sw_dev_info_stats.traffic_intr_cnt++; 
 | 
  
 | 
        return VXGE_HW_OK; 
 | 
    } 
 | 
  
 | 
    hldev->stats.sw_dev_info_stats.not_traffic_intr_cnt++; 
 | 
  
 | 
    if (unlikely(val64 & 
 | 
            VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_ALARM_INT)) { 
 | 
  
 | 
        enum vxge_hw_status error_level = VXGE_HW_OK; 
 | 
  
 | 
        hldev->stats.sw_dev_err_stats.vpath_alarms++; 
 | 
  
 | 
        for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { 
 | 
  
 | 
            if (!(hldev->vpaths_deployed & vxge_mBIT(i))) 
 | 
                continue; 
 | 
  
 | 
            ret = __vxge_hw_vpath_alarm_process( 
 | 
                &hldev->virtual_paths[i], skip_alarms); 
 | 
  
 | 
            error_level = VXGE_HW_SET_LEVEL(ret, error_level); 
 | 
  
 | 
            if (unlikely((ret == VXGE_HW_ERR_CRITICAL) || 
 | 
                (ret == VXGE_HW_ERR_SLOT_FREEZE))) 
 | 
                break; 
 | 
        } 
 | 
  
 | 
        ret = error_level; 
 | 
    } 
 | 
exit: 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_device_clear_tx_rx - Acknowledge (that is, clear) the 
 | 
 * condition that has caused the Tx and RX interrupt. 
 | 
 * @hldev: HW device. 
 | 
 * 
 | 
 * Acknowledge (that is, clear) the condition that has caused 
 | 
 * the Tx and Rx interrupt. 
 | 
 * See also: vxge_hw_device_begin_irq(), 
 | 
 * vxge_hw_device_mask_tx_rx(), vxge_hw_device_unmask_tx_rx(). 
 | 
 */ 
 | 
void vxge_hw_device_clear_tx_rx(struct __vxge_hw_device *hldev) 
 | 
{ 
 | 
  
 | 
    if ((hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
       (hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        writeq((hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_TX] | 
 | 
                 hldev->tim_int_mask0[VXGE_HW_VPATH_INTR_RX]), 
 | 
                &hldev->common_reg->tim_int_status0); 
 | 
    } 
 | 
  
 | 
    if ((hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
       (hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        __vxge_hw_pio_mem_write32_upper( 
 | 
                (hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_TX] | 
 | 
                 hldev->tim_int_mask1[VXGE_HW_VPATH_INTR_RX]), 
 | 
                &hldev->common_reg->tim_int_status1); 
 | 
    } 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_alloc - Allocate a dtr from the channel 
 | 
 * @channel: Channel 
 | 
 * @dtrh: Buffer to return the DTR pointer 
 | 
 * 
 | 
 * Allocates a dtr from the reserve array. If the reserve array is empty, 
 | 
 * it swaps the reserve and free arrays. 
 | 
 * 
 | 
 */ 
 | 
static enum vxge_hw_status 
 | 
vxge_hw_channel_dtr_alloc(struct __vxge_hw_channel *channel, void **dtrh) 
 | 
{ 
 | 
    if (channel->reserve_ptr - channel->reserve_top > 0) { 
 | 
_alloc_after_swap: 
 | 
        *dtrh =    channel->reserve_arr[--channel->reserve_ptr]; 
 | 
  
 | 
        return VXGE_HW_OK; 
 | 
    } 
 | 
  
 | 
    /* switch between empty    and full arrays    */ 
 | 
  
 | 
    /* the idea behind such    a design is that by having free    and reserved 
 | 
     * arrays separated we basically separated irq and non-irq parts. 
 | 
     * i.e.    no additional lock need    to be done when    we free    a resource */ 
 | 
  
 | 
    if (channel->length - channel->free_ptr > 0) { 
 | 
        swap(channel->reserve_arr, channel->free_arr); 
 | 
        channel->reserve_ptr = channel->length; 
 | 
        channel->reserve_top = channel->free_ptr; 
 | 
        channel->free_ptr = channel->length; 
 | 
  
 | 
        channel->stats->reserve_free_swaps_cnt++; 
 | 
  
 | 
        goto _alloc_after_swap; 
 | 
    } 
 | 
  
 | 
    channel->stats->full_cnt++; 
 | 
  
 | 
    *dtrh =    NULL; 
 | 
    return VXGE_HW_INF_OUT_OF_DESCRIPTORS; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_post - Post a dtr to the channel 
 | 
 * @channelh: Channel 
 | 
 * @dtrh: DTR pointer 
 | 
 * 
 | 
 * Posts a dtr to work array. 
 | 
 * 
 | 
 */ 
 | 
static void 
 | 
vxge_hw_channel_dtr_post(struct __vxge_hw_channel *channel, void *dtrh) 
 | 
{ 
 | 
    vxge_assert(channel->work_arr[channel->post_index] == NULL); 
 | 
  
 | 
    channel->work_arr[channel->post_index++] = dtrh; 
 | 
  
 | 
    /* wrap-around */ 
 | 
    if (channel->post_index    == channel->length) 
 | 
        channel->post_index = 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_try_complete - Returns next completed dtr 
 | 
 * @channel: Channel 
 | 
 * @dtr: Buffer to return the next completed DTR pointer 
 | 
 * 
 | 
 * Returns the next completed dtr with out removing it from work array 
 | 
 * 
 | 
 */ 
 | 
void 
 | 
vxge_hw_channel_dtr_try_complete(struct __vxge_hw_channel *channel, void **dtrh) 
 | 
{ 
 | 
    vxge_assert(channel->compl_index < channel->length); 
 | 
  
 | 
    *dtrh =    channel->work_arr[channel->compl_index]; 
 | 
    prefetch(*dtrh); 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_complete - Removes next completed dtr from the work array 
 | 
 * @channel: Channel handle 
 | 
 * 
 | 
 * Removes the next completed dtr from work array 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_channel_dtr_complete(struct __vxge_hw_channel *channel) 
 | 
{ 
 | 
    channel->work_arr[channel->compl_index]    = NULL; 
 | 
  
 | 
    /* wrap-around */ 
 | 
    if (++channel->compl_index == channel->length) 
 | 
        channel->compl_index = 0; 
 | 
  
 | 
    channel->stats->total_compl_cnt++; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_free - Frees a dtr 
 | 
 * @channel: Channel handle 
 | 
 * @dtr:  DTR pointer 
 | 
 * 
 | 
 * Returns the dtr to free array 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_channel_dtr_free(struct __vxge_hw_channel *channel, void *dtrh) 
 | 
{ 
 | 
    channel->free_arr[--channel->free_ptr] = dtrh; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_channel_dtr_count 
 | 
 * @channel: Channel handle. Obtained via vxge_hw_channel_open(). 
 | 
 * 
 | 
 * Retrieve number of DTRs available. This function can not be called 
 | 
 * from data path. ring_initial_replenishi() is the only user. 
 | 
 */ 
 | 
int vxge_hw_channel_dtr_count(struct __vxge_hw_channel *channel) 
 | 
{ 
 | 
    return (channel->reserve_ptr - channel->reserve_top) + 
 | 
        (channel->length - channel->free_ptr); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_reserve    - Reserve ring descriptor. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Reserved descriptor. On success HW fills this "out" parameter 
 | 
 * with a valid handle. 
 | 
 * 
 | 
 * Reserve Rx descriptor for the subsequent filling-in driver 
 | 
 * and posting on the corresponding channel (@channelh) 
 | 
 * via vxge_hw_ring_rxd_post(). 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK - success. 
 | 
 * VXGE_HW_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available. 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_ring_rxd_reserve(struct __vxge_hw_ring *ring, 
 | 
    void **rxdh) 
 | 
{ 
 | 
    enum vxge_hw_status status; 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &ring->channel; 
 | 
  
 | 
    status = vxge_hw_channel_dtr_alloc(channel, rxdh); 
 | 
  
 | 
    if (status == VXGE_HW_OK) { 
 | 
        struct vxge_hw_ring_rxd_1 *rxdp = 
 | 
            (struct vxge_hw_ring_rxd_1 *)*rxdh; 
 | 
  
 | 
        rxdp->control_0    = rxdp->control_1 = 0; 
 | 
    } 
 | 
  
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_free - Free descriptor. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. 
 | 
 * 
 | 
 * Free    the reserved descriptor. This operation is "symmetrical" to 
 | 
 * vxge_hw_ring_rxd_reserve. The "free-ing" completes the descriptor's 
 | 
 * lifecycle. 
 | 
 * 
 | 
 * After free-ing (see vxge_hw_ring_rxd_free()) the descriptor again can 
 | 
 * be: 
 | 
 * 
 | 
 * - reserved (vxge_hw_ring_rxd_reserve); 
 | 
 * 
 | 
 * - posted    (vxge_hw_ring_rxd_post); 
 | 
 * 
 | 
 * - completed (vxge_hw_ring_rxd_next_completed); 
 | 
 * 
 | 
 * - and recycled again    (vxge_hw_ring_rxd_free). 
 | 
 * 
 | 
 * For alternative state transitions and more details please refer to 
 | 
 * the design doc. 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_ring_rxd_free(struct __vxge_hw_ring *ring, void *rxdh) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &ring->channel; 
 | 
  
 | 
    vxge_hw_channel_dtr_free(channel, rxdh); 
 | 
  
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_pre_post - Prepare rxd and post 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. 
 | 
 * 
 | 
 * This routine prepares a rxd and posts 
 | 
 */ 
 | 
void vxge_hw_ring_rxd_pre_post(struct __vxge_hw_ring *ring, void *rxdh) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &ring->channel; 
 | 
  
 | 
    vxge_hw_channel_dtr_post(channel, rxdh); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_post_post - Process rxd after post. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. 
 | 
 * 
 | 
 * Processes rxd after post 
 | 
 */ 
 | 
void vxge_hw_ring_rxd_post_post(struct __vxge_hw_ring *ring, void *rxdh) 
 | 
{ 
 | 
    struct vxge_hw_ring_rxd_1 *rxdp = (struct vxge_hw_ring_rxd_1 *)rxdh; 
 | 
  
 | 
    rxdp->control_0    = VXGE_HW_RING_RXD_LIST_OWN_ADAPTER; 
 | 
  
 | 
    if (ring->stats->common_stats.usage_cnt > 0) 
 | 
        ring->stats->common_stats.usage_cnt--; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_post - Post descriptor on the ring. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor obtained via vxge_hw_ring_rxd_reserve(). 
 | 
 * 
 | 
 * Post    descriptor on the ring. 
 | 
 * Prior to posting the    descriptor should be filled in accordance with 
 | 
 * Host/Titan interface specification for a given service (LL, etc.). 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_ring_rxd_post(struct __vxge_hw_ring *ring, void *rxdh) 
 | 
{ 
 | 
    struct vxge_hw_ring_rxd_1 *rxdp = (struct vxge_hw_ring_rxd_1 *)rxdh; 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &ring->channel; 
 | 
  
 | 
    wmb(); 
 | 
    rxdp->control_0    = VXGE_HW_RING_RXD_LIST_OWN_ADAPTER; 
 | 
  
 | 
    vxge_hw_channel_dtr_post(channel, rxdh); 
 | 
  
 | 
    if (ring->stats->common_stats.usage_cnt > 0) 
 | 
        ring->stats->common_stats.usage_cnt--; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_post_post_wmb - Process rxd after post with memory barrier. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. 
 | 
 * 
 | 
 * Processes rxd after post with memory barrier. 
 | 
 */ 
 | 
void vxge_hw_ring_rxd_post_post_wmb(struct __vxge_hw_ring *ring, void *rxdh) 
 | 
{ 
 | 
    wmb(); 
 | 
    vxge_hw_ring_rxd_post_post(ring, rxdh); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_rxd_next_completed - Get the _next_ completed descriptor. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. Returned by HW. 
 | 
 * @t_code:    Transfer code, as per Titan User Guide, 
 | 
 *     Receive Descriptor Format. Returned by HW. 
 | 
 * 
 | 
 * Retrieve the    _next_ completed descriptor. 
 | 
 * HW uses ring callback (*vxge_hw_ring_callback_f) to notifiy 
 | 
 * driver of new completed descriptors. After that 
 | 
 * the driver can use vxge_hw_ring_rxd_next_completed to retrieve the rest 
 | 
 * completions (the very first completion is passed by HW via 
 | 
 * vxge_hw_ring_callback_f). 
 | 
 * 
 | 
 * Implementation-wise, the driver is free to call 
 | 
 * vxge_hw_ring_rxd_next_completed either immediately from inside the 
 | 
 * ring callback, or in a deferred fashion and separate (from HW) 
 | 
 * context. 
 | 
 * 
 | 
 * Non-zero @t_code means failure to fill-in receive buffer(s) 
 | 
 * of the descriptor. 
 | 
 * For instance, parity    error detected during the data transfer. 
 | 
 * In this case    Titan will complete the descriptor and indicate 
 | 
 * for the host    that the received data is not to be used. 
 | 
 * For details please refer to Titan User Guide. 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK - success. 
 | 
 * VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS - No completed descriptors 
 | 
 * are currently available for processing. 
 | 
 * 
 | 
 * See also: vxge_hw_ring_callback_f{}, 
 | 
 * vxge_hw_fifo_rxd_next_completed(), enum vxge_hw_status{}. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_ring_rxd_next_completed( 
 | 
    struct __vxge_hw_ring *ring, void **rxdh, u8 *t_code) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
    struct vxge_hw_ring_rxd_1 *rxdp; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
    u64 control_0, own; 
 | 
  
 | 
    channel = &ring->channel; 
 | 
  
 | 
    vxge_hw_channel_dtr_try_complete(channel, rxdh); 
 | 
  
 | 
    rxdp = *rxdh; 
 | 
    if (rxdp == NULL) { 
 | 
        status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    control_0 = rxdp->control_0; 
 | 
    own = control_0 & VXGE_HW_RING_RXD_LIST_OWN_ADAPTER; 
 | 
    *t_code    = (u8)VXGE_HW_RING_RXD_T_CODE_GET(control_0); 
 | 
  
 | 
    /* check whether it is not the end */ 
 | 
    if (!own || *t_code == VXGE_HW_RING_T_CODE_FRM_DROP) { 
 | 
  
 | 
        vxge_assert((rxdp)->host_control != 
 | 
                0); 
 | 
  
 | 
        ++ring->cmpl_cnt; 
 | 
        vxge_hw_channel_dtr_complete(channel); 
 | 
  
 | 
        vxge_assert(*t_code != VXGE_HW_RING_RXD_T_CODE_UNUSED); 
 | 
  
 | 
        ring->stats->common_stats.usage_cnt++; 
 | 
        if (ring->stats->common_stats.usage_max < 
 | 
                ring->stats->common_stats.usage_cnt) 
 | 
            ring->stats->common_stats.usage_max = 
 | 
                ring->stats->common_stats.usage_cnt; 
 | 
  
 | 
        status = VXGE_HW_OK; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    /* reset it. since we don't want to return 
 | 
     * garbage to the driver */ 
 | 
    *rxdh =    NULL; 
 | 
    status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS; 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_ring_handle_tcode - Handle transfer code. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * @rxdh: Descriptor handle. 
 | 
 * @t_code: One of the enumerated (and documented in the Titan user guide) 
 | 
 * "transfer codes". 
 | 
 * 
 | 
 * Handle descriptor's transfer code. The latter comes with each completed 
 | 
 * descriptor. 
 | 
 * 
 | 
 * Returns: one of the enum vxge_hw_status{} enumerated types. 
 | 
 * VXGE_HW_OK            - for success. 
 | 
 * VXGE_HW_ERR_CRITICAL         - when encounters critical error. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_ring_handle_tcode( 
 | 
    struct __vxge_hw_ring *ring, void *rxdh, u8 t_code) 
 | 
{ 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    /* If the t_code is not supported and if the 
 | 
     * t_code is other than 0x5 (unparseable packet 
 | 
     * such as unknown UPV6 header), Drop it !!! 
 | 
     */ 
 | 
  
 | 
    if (t_code ==  VXGE_HW_RING_T_CODE_OK || 
 | 
        t_code == VXGE_HW_RING_T_CODE_L3_PKT_ERR) { 
 | 
        status = VXGE_HW_OK; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    if (t_code > VXGE_HW_RING_T_CODE_MULTI_ERR) { 
 | 
        status = VXGE_HW_ERR_INVALID_TCODE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    ring->stats->rxd_t_code_err_cnt[t_code]++; 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * __vxge_hw_non_offload_db_post - Post non offload doorbell 
 | 
 * 
 | 
 * @fifo: fifohandle 
 | 
 * @txdl_ptr: The starting location of the TxDL in host memory 
 | 
 * @num_txds: The highest TxD in this TxDL (0 to 255 means 1 to 256) 
 | 
 * @no_snoop: No snoop flags 
 | 
 * 
 | 
 * This function posts a non-offload doorbell to doorbell FIFO 
 | 
 * 
 | 
 */ 
 | 
static void __vxge_hw_non_offload_db_post(struct __vxge_hw_fifo *fifo, 
 | 
    u64 txdl_ptr, u32 num_txds, u32 no_snoop) 
 | 
{ 
 | 
    writeq(VXGE_HW_NODBW_TYPE(VXGE_HW_NODBW_TYPE_NODBW) | 
 | 
        VXGE_HW_NODBW_LAST_TXD_NUMBER(num_txds) | 
 | 
        VXGE_HW_NODBW_GET_NO_SNOOP(no_snoop), 
 | 
        &fifo->nofl_db->control_0); 
 | 
  
 | 
    writeq(txdl_ptr, &fifo->nofl_db->txdl_ptr); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_free_txdl_count_get - returns the number of txdls available in 
 | 
 * the fifo 
 | 
 * @fifoh: Handle to the fifo object used for non offload send 
 | 
 */ 
 | 
u32 vxge_hw_fifo_free_txdl_count_get(struct __vxge_hw_fifo *fifoh) 
 | 
{ 
 | 
    return vxge_hw_channel_dtr_count(&fifoh->channel); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_txdl_reserve - Reserve fifo descriptor. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Reserved descriptor. On success HW fills this "out" parameter 
 | 
 *        with a valid handle. 
 | 
 * @txdl_priv: Buffer to return the pointer to per txdl space 
 | 
 * 
 | 
 * Reserve a single TxDL (that is, fifo descriptor) 
 | 
 * for the subsequent filling-in by driver) 
 | 
 * and posting on the corresponding channel (@channelh) 
 | 
 * via vxge_hw_fifo_txdl_post(). 
 | 
 * 
 | 
 * Note: it is the responsibility of driver to reserve multiple descriptors 
 | 
 * for lengthy (e.g., LSO) transmit operation. A single fifo descriptor 
 | 
 * carries up to configured number (fifo.max_frags) of contiguous buffers. 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK - success; 
 | 
 * VXGE_HW_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_fifo_txdl_reserve( 
 | 
    struct __vxge_hw_fifo *fifo, 
 | 
    void **txdlh, void **txdl_priv) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
    enum vxge_hw_status status; 
 | 
    int i; 
 | 
  
 | 
    channel = &fifo->channel; 
 | 
  
 | 
    status = vxge_hw_channel_dtr_alloc(channel, txdlh); 
 | 
  
 | 
    if (status == VXGE_HW_OK) { 
 | 
        struct vxge_hw_fifo_txd *txdp = 
 | 
            (struct vxge_hw_fifo_txd *)*txdlh; 
 | 
        struct __vxge_hw_fifo_txdl_priv *priv; 
 | 
  
 | 
        priv = __vxge_hw_fifo_txdl_priv(fifo, txdp); 
 | 
  
 | 
        /* reset the TxDL's private */ 
 | 
        priv->align_dma_offset = 0; 
 | 
        priv->align_vaddr_start = priv->align_vaddr; 
 | 
        priv->align_used_frags = 0; 
 | 
        priv->frags = 0; 
 | 
        priv->alloc_frags = fifo->config->max_frags; 
 | 
        priv->next_txdl_priv = NULL; 
 | 
  
 | 
        *txdl_priv = (void *)(size_t)txdp->host_control; 
 | 
  
 | 
        for (i = 0; i < fifo->config->max_frags; i++) { 
 | 
            txdp = ((struct vxge_hw_fifo_txd *)*txdlh) + i; 
 | 
            txdp->control_0 = txdp->control_1 = 0; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_txdl_buffer_set - Set transmit buffer pointer in the 
 | 
 * descriptor. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Descriptor handle. 
 | 
 * @frag_idx: Index of the data buffer in the caller's scatter-gather list 
 | 
 *            (of buffers). 
 | 
 * @dma_pointer: DMA address of the data buffer referenced by @frag_idx. 
 | 
 * @size: Size of the data buffer (in bytes). 
 | 
 * 
 | 
 * This API is part of the preparation of the transmit descriptor for posting 
 | 
 * (via vxge_hw_fifo_txdl_post()). The related "preparation" APIs include 
 | 
 * vxge_hw_fifo_txdl_mss_set() and vxge_hw_fifo_txdl_cksum_set_bits(). 
 | 
 * All three APIs fill in the fields of the fifo descriptor, 
 | 
 * in accordance with the Titan specification. 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_fifo_txdl_buffer_set(struct __vxge_hw_fifo *fifo, 
 | 
                  void *txdlh, u32 frag_idx, 
 | 
                  dma_addr_t dma_pointer, u32 size) 
 | 
{ 
 | 
    struct __vxge_hw_fifo_txdl_priv *txdl_priv; 
 | 
    struct vxge_hw_fifo_txd *txdp, *txdp_last; 
 | 
  
 | 
    txdl_priv = __vxge_hw_fifo_txdl_priv(fifo, txdlh); 
 | 
    txdp = (struct vxge_hw_fifo_txd *)txdlh  +  txdl_priv->frags; 
 | 
  
 | 
    if (frag_idx != 0) 
 | 
        txdp->control_0 = txdp->control_1 = 0; 
 | 
    else { 
 | 
        txdp->control_0 |= VXGE_HW_FIFO_TXD_GATHER_CODE( 
 | 
            VXGE_HW_FIFO_TXD_GATHER_CODE_FIRST); 
 | 
        txdp->control_1 |= fifo->interrupt_type; 
 | 
        txdp->control_1 |= VXGE_HW_FIFO_TXD_INT_NUMBER( 
 | 
            fifo->tx_intr_num); 
 | 
        if (txdl_priv->frags) { 
 | 
            txdp_last = (struct vxge_hw_fifo_txd *)txdlh  + 
 | 
            (txdl_priv->frags - 1); 
 | 
            txdp_last->control_0 |= VXGE_HW_FIFO_TXD_GATHER_CODE( 
 | 
                VXGE_HW_FIFO_TXD_GATHER_CODE_LAST); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    vxge_assert(frag_idx < txdl_priv->alloc_frags); 
 | 
  
 | 
    txdp->buffer_pointer = (u64)dma_pointer; 
 | 
    txdp->control_0 |= VXGE_HW_FIFO_TXD_BUFFER_SIZE(size); 
 | 
    fifo->stats->total_buffers++; 
 | 
    txdl_priv->frags++; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_txdl_post - Post descriptor on the fifo channel. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Descriptor obtained via vxge_hw_fifo_txdl_reserve() 
 | 
 * 
 | 
 * Post descriptor on the 'fifo' type channel for transmission. 
 | 
 * Prior to posting the descriptor should be filled in accordance with 
 | 
 * Host/Titan interface specification for a given service (LL, etc.). 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_fifo_txdl_post(struct __vxge_hw_fifo *fifo, void *txdlh) 
 | 
{ 
 | 
    struct __vxge_hw_fifo_txdl_priv *txdl_priv; 
 | 
    struct vxge_hw_fifo_txd *txdp_last; 
 | 
    struct vxge_hw_fifo_txd *txdp_first; 
 | 
  
 | 
    txdl_priv = __vxge_hw_fifo_txdl_priv(fifo, txdlh); 
 | 
    txdp_first = txdlh; 
 | 
  
 | 
    txdp_last = (struct vxge_hw_fifo_txd *)txdlh  +  (txdl_priv->frags - 1); 
 | 
    txdp_last->control_0 |= 
 | 
          VXGE_HW_FIFO_TXD_GATHER_CODE(VXGE_HW_FIFO_TXD_GATHER_CODE_LAST); 
 | 
    txdp_first->control_0 |= VXGE_HW_FIFO_TXD_LIST_OWN_ADAPTER; 
 | 
  
 | 
    vxge_hw_channel_dtr_post(&fifo->channel, txdlh); 
 | 
  
 | 
    __vxge_hw_non_offload_db_post(fifo, 
 | 
        (u64)txdl_priv->dma_addr, 
 | 
        txdl_priv->frags - 1, 
 | 
        fifo->no_snoop_bits); 
 | 
  
 | 
    fifo->stats->total_posts++; 
 | 
    fifo->stats->common_stats.usage_cnt++; 
 | 
    if (fifo->stats->common_stats.usage_max < 
 | 
        fifo->stats->common_stats.usage_cnt) 
 | 
        fifo->stats->common_stats.usage_max = 
 | 
            fifo->stats->common_stats.usage_cnt; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_txdl_next_completed - Retrieve next completed descriptor. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Descriptor handle. Returned by HW. 
 | 
 * @t_code: Transfer code, as per Titan User Guide, 
 | 
 *          Transmit Descriptor Format. 
 | 
 *          Returned by HW. 
 | 
 * 
 | 
 * Retrieve the _next_ completed descriptor. 
 | 
 * HW uses channel callback (*vxge_hw_channel_callback_f) to notifiy 
 | 
 * driver of new completed descriptors. After that 
 | 
 * the driver can use vxge_hw_fifo_txdl_next_completed to retrieve the rest 
 | 
 * completions (the very first completion is passed by HW via 
 | 
 * vxge_hw_channel_callback_f). 
 | 
 * 
 | 
 * Implementation-wise, the driver is free to call 
 | 
 * vxge_hw_fifo_txdl_next_completed either immediately from inside the 
 | 
 * channel callback, or in a deferred fashion and separate (from HW) 
 | 
 * context. 
 | 
 * 
 | 
 * Non-zero @t_code means failure to process the descriptor. 
 | 
 * The failure could happen, for instance, when the link is 
 | 
 * down, in which case Titan completes the descriptor because it 
 | 
 * is not able to send the data out. 
 | 
 * 
 | 
 * For details please refer to Titan User Guide. 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK - success. 
 | 
 * VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS - No completed descriptors 
 | 
 * are currently available for processing. 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_fifo_txdl_next_completed( 
 | 
    struct __vxge_hw_fifo *fifo, void **txdlh, 
 | 
    enum vxge_hw_fifo_tcode *t_code) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
    struct vxge_hw_fifo_txd *txdp; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    channel = &fifo->channel; 
 | 
  
 | 
    vxge_hw_channel_dtr_try_complete(channel, txdlh); 
 | 
  
 | 
    txdp = *txdlh; 
 | 
    if (txdp == NULL) { 
 | 
        status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    /* check whether host owns it */ 
 | 
    if (!(txdp->control_0 & VXGE_HW_FIFO_TXD_LIST_OWN_ADAPTER)) { 
 | 
  
 | 
        vxge_assert(txdp->host_control != 0); 
 | 
  
 | 
        vxge_hw_channel_dtr_complete(channel); 
 | 
  
 | 
        *t_code = (u8)VXGE_HW_FIFO_TXD_T_CODE_GET(txdp->control_0); 
 | 
  
 | 
        if (fifo->stats->common_stats.usage_cnt > 0) 
 | 
            fifo->stats->common_stats.usage_cnt--; 
 | 
  
 | 
        status = VXGE_HW_OK; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    /* no more completions */ 
 | 
    *txdlh = NULL; 
 | 
    status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS; 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_handle_tcode - Handle transfer code. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Descriptor handle. 
 | 
 * @t_code: One of the enumerated (and documented in the Titan user guide) 
 | 
 *          "transfer codes". 
 | 
 * 
 | 
 * Handle descriptor's transfer code. The latter comes with each completed 
 | 
 * descriptor. 
 | 
 * 
 | 
 * Returns: one of the enum vxge_hw_status{} enumerated types. 
 | 
 * VXGE_HW_OK - for success. 
 | 
 * VXGE_HW_ERR_CRITICAL - when encounters critical error. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_fifo_handle_tcode(struct __vxge_hw_fifo *fifo, 
 | 
                          void *txdlh, 
 | 
                          enum vxge_hw_fifo_tcode t_code) 
 | 
{ 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (((t_code & 0x7) < 0) || ((t_code & 0x7) > 0x4)) { 
 | 
        status = VXGE_HW_ERR_INVALID_TCODE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    fifo->stats->txd_t_code_err_cnt[t_code]++; 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_fifo_txdl_free - Free descriptor. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @txdlh: Descriptor handle. 
 | 
 * 
 | 
 * Free the reserved descriptor. This operation is "symmetrical" to 
 | 
 * vxge_hw_fifo_txdl_reserve. The "free-ing" completes the descriptor's 
 | 
 * lifecycle. 
 | 
 * 
 | 
 * After free-ing (see vxge_hw_fifo_txdl_free()) the descriptor again can 
 | 
 * be: 
 | 
 * 
 | 
 * - reserved (vxge_hw_fifo_txdl_reserve); 
 | 
 * 
 | 
 * - posted (vxge_hw_fifo_txdl_post); 
 | 
 * 
 | 
 * - completed (vxge_hw_fifo_txdl_next_completed); 
 | 
 * 
 | 
 * - and recycled again (vxge_hw_fifo_txdl_free). 
 | 
 * 
 | 
 * For alternative state transitions and more details please refer to 
 | 
 * the design doc. 
 | 
 * 
 | 
 */ 
 | 
void vxge_hw_fifo_txdl_free(struct __vxge_hw_fifo *fifo, void *txdlh) 
 | 
{ 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &fifo->channel; 
 | 
  
 | 
    vxge_hw_channel_dtr_free(channel, txdlh); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mac_addr_add - Add the mac address entry for this vpath to MAC address table. 
 | 
 * @vp: Vpath handle. 
 | 
 * @macaddr: MAC address to be added for this vpath into the list 
 | 
 * @macaddr_mask: MAC address mask for macaddr 
 | 
 * @duplicate_mode: Duplicate MAC address add mode. Please see 
 | 
 *             enum vxge_hw_vpath_mac_addr_add_mode{} 
 | 
 * 
 | 
 * Adds the given mac address and mac address mask into the list for this 
 | 
 * vpath. 
 | 
 * see also: vxge_hw_vpath_mac_addr_delete, vxge_hw_vpath_mac_addr_get and 
 | 
 * vxge_hw_vpath_mac_addr_get_next 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_mac_addr_add( 
 | 
    struct __vxge_hw_vpath_handle *vp, 
 | 
    u8 *macaddr, 
 | 
    u8 *macaddr_mask, 
 | 
    enum vxge_hw_vpath_mac_addr_add_mode duplicate_mode) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 data1 = 0ULL; 
 | 
    u64 data2 = 0ULL; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < ETH_ALEN; i++) { 
 | 
        data1 <<= 8; 
 | 
        data1 |= (u8)macaddr[i]; 
 | 
  
 | 
        data2 <<= 8; 
 | 
        data2 |= (u8)macaddr_mask[i]; 
 | 
    } 
 | 
  
 | 
    switch (duplicate_mode) { 
 | 
    case VXGE_HW_VPATH_MAC_ADDR_ADD_DUPLICATE: 
 | 
        i = 0; 
 | 
        break; 
 | 
    case VXGE_HW_VPATH_MAC_ADDR_DISCARD_DUPLICATE: 
 | 
        i = 1; 
 | 
        break; 
 | 
    case VXGE_HW_VPATH_MAC_ADDR_REPLACE_DUPLICATE: 
 | 
        i = 2; 
 | 
        break; 
 | 
    default: 
 | 
        i = 0; 
 | 
        break; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_set(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_ADD_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA, 
 | 
            0, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_DATA0_DA_MAC_ADDR(data1), 
 | 
            VXGE_HW_RTS_ACCESS_STEER_DATA1_DA_MAC_ADDR_MASK(data2)| 
 | 
            VXGE_HW_RTS_ACCESS_STEER_DATA1_DA_MAC_ADDR_MODE(i)); 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mac_addr_get - Get the first mac address entry 
 | 
 * @vp: Vpath handle. 
 | 
 * @macaddr: First MAC address entry for this vpath in the list 
 | 
 * @macaddr_mask: MAC address mask for macaddr 
 | 
 * 
 | 
 * Get the first mac address entry for this vpath from MAC address table. 
 | 
 * Return: the first mac address and mac address mask in the list for this 
 | 
 * vpath. 
 | 
 * see also: vxge_hw_vpath_mac_addr_get_next 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_mac_addr_get( 
 | 
    struct __vxge_hw_vpath_handle *vp, 
 | 
    u8 *macaddr, 
 | 
    u8 *macaddr_mask) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 data1 = 0ULL; 
 | 
    u64 data2 = 0ULL; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_get(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_LIST_FIRST_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA, 
 | 
            0, &data1, &data2); 
 | 
  
 | 
    if (status != VXGE_HW_OK) 
 | 
        goto exit; 
 | 
  
 | 
    data1 = VXGE_HW_RTS_ACCESS_STEER_DATA0_GET_DA_MAC_ADDR(data1); 
 | 
  
 | 
    data2 = VXGE_HW_RTS_ACCESS_STEER_DATA1_GET_DA_MAC_ADDR_MASK(data2); 
 | 
  
 | 
    for (i = ETH_ALEN; i > 0; i--) { 
 | 
        macaddr[i-1] = (u8)(data1 & 0xFF); 
 | 
        data1 >>= 8; 
 | 
  
 | 
        macaddr_mask[i-1] = (u8)(data2 & 0xFF); 
 | 
        data2 >>= 8; 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mac_addr_get_next - Get the next mac address entry 
 | 
 * @vp: Vpath handle. 
 | 
 * @macaddr: Next MAC address entry for this vpath in the list 
 | 
 * @macaddr_mask: MAC address mask for macaddr 
 | 
 * 
 | 
 * Get the next mac address entry for this vpath from MAC address table. 
 | 
 * Return: the next mac address and mac address mask in the list for this 
 | 
 * vpath. 
 | 
 * see also: vxge_hw_vpath_mac_addr_get 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_mac_addr_get_next( 
 | 
    struct __vxge_hw_vpath_handle *vp, 
 | 
    u8 *macaddr, 
 | 
    u8 *macaddr_mask) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 data1 = 0ULL; 
 | 
    u64 data2 = 0ULL; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_get(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_LIST_NEXT_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA, 
 | 
            0, &data1, &data2); 
 | 
  
 | 
    if (status != VXGE_HW_OK) 
 | 
        goto exit; 
 | 
  
 | 
    data1 = VXGE_HW_RTS_ACCESS_STEER_DATA0_GET_DA_MAC_ADDR(data1); 
 | 
  
 | 
    data2 = VXGE_HW_RTS_ACCESS_STEER_DATA1_GET_DA_MAC_ADDR_MASK(data2); 
 | 
  
 | 
    for (i = ETH_ALEN; i > 0; i--) { 
 | 
        macaddr[i-1] = (u8)(data1 & 0xFF); 
 | 
        data1 >>= 8; 
 | 
  
 | 
        macaddr_mask[i-1] = (u8)(data2 & 0xFF); 
 | 
        data2 >>= 8; 
 | 
    } 
 | 
  
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mac_addr_delete - Delete the mac address entry for this vpath to MAC address table. 
 | 
 * @vp: Vpath handle. 
 | 
 * @macaddr: MAC address to be added for this vpath into the list 
 | 
 * @macaddr_mask: MAC address mask for macaddr 
 | 
 * 
 | 
 * Delete the given mac address and mac address mask into the list for this 
 | 
 * vpath. 
 | 
 * see also: vxge_hw_vpath_mac_addr_add, vxge_hw_vpath_mac_addr_get and 
 | 
 * vxge_hw_vpath_mac_addr_get_next 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_mac_addr_delete( 
 | 
    struct __vxge_hw_vpath_handle *vp, 
 | 
    u8 *macaddr, 
 | 
    u8 *macaddr_mask) 
 | 
{ 
 | 
    u32 i; 
 | 
    u64 data1 = 0ULL; 
 | 
    u64 data2 = 0ULL; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < ETH_ALEN; i++) { 
 | 
        data1 <<= 8; 
 | 
        data1 |= (u8)macaddr[i]; 
 | 
  
 | 
        data2 <<= 8; 
 | 
        data2 |= (u8)macaddr_mask[i]; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_set(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_DELETE_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA, 
 | 
            0, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_DATA0_DA_MAC_ADDR(data1), 
 | 
            VXGE_HW_RTS_ACCESS_STEER_DATA1_DA_MAC_ADDR_MASK(data2)); 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_vid_add - Add the vlan id entry for this vpath to vlan id table. 
 | 
 * @vp: Vpath handle. 
 | 
 * @vid: vlan id to be added for this vpath into the list 
 | 
 * 
 | 
 * Adds the given vlan id into the list for this  vpath. 
 | 
 * see also: vxge_hw_vpath_vid_delete 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_vid_add(struct __vxge_hw_vpath_handle *vp, u64 vid) 
 | 
{ 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_set(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_ADD_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_VID, 
 | 
            0, VXGE_HW_RTS_ACCESS_STEER_DATA0_VLAN_ID(vid), 0); 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_vid_delete - Delete the vlan id entry for this vpath 
 | 
 *               to vlan id table. 
 | 
 * @vp: Vpath handle. 
 | 
 * @vid: vlan id to be added for this vpath into the list 
 | 
 * 
 | 
 * Adds the given vlan id into the list for this  vpath. 
 | 
 * see also: vxge_hw_vpath_vid_add 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_vid_delete(struct __vxge_hw_vpath_handle *vp, u64 vid) 
 | 
{ 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_rts_table_set(vp, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_ACTION_DELETE_ENTRY, 
 | 
            VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_VID, 
 | 
            0, VXGE_HW_RTS_ACCESS_STEER_DATA0_VLAN_ID(vid), 0); 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_promisc_enable - Enable promiscuous mode. 
 | 
 * @vp: Vpath handle. 
 | 
 * 
 | 
 * Enable promiscuous mode of Titan-e operation. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_promisc_disable(). 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_promisc_enable( 
 | 
            struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if ((vp == NULL) || (vp->vpath->ringh == NULL)) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    /* Enable promiscuous mode for function 0 only */ 
 | 
    if (!(vpath->hldev->access_rights & 
 | 
        VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)) 
 | 
        return VXGE_HW_OK; 
 | 
  
 | 
    val64 = readq(&vpath->vp_reg->rxmac_vcfg0); 
 | 
  
 | 
    if (!(val64 & VXGE_HW_RXMAC_VCFG0_UCAST_ALL_ADDR_EN)) { 
 | 
  
 | 
        val64 |= VXGE_HW_RXMAC_VCFG0_UCAST_ALL_ADDR_EN | 
 | 
             VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN | 
 | 
             VXGE_HW_RXMAC_VCFG0_BCAST_EN | 
 | 
             VXGE_HW_RXMAC_VCFG0_ALL_VID_EN; 
 | 
  
 | 
        writeq(val64, &vpath->vp_reg->rxmac_vcfg0); 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_promisc_disable - Disable promiscuous mode. 
 | 
 * @vp: Vpath handle. 
 | 
 * 
 | 
 * Disable promiscuous mode of Titan-e operation. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_promisc_enable(). 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_promisc_disable( 
 | 
            struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if ((vp == NULL) || (vp->vpath->ringh == NULL)) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    val64 = readq(&vpath->vp_reg->rxmac_vcfg0); 
 | 
  
 | 
    if (val64 & VXGE_HW_RXMAC_VCFG0_UCAST_ALL_ADDR_EN) { 
 | 
  
 | 
        val64 &= ~(VXGE_HW_RXMAC_VCFG0_UCAST_ALL_ADDR_EN | 
 | 
               VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN | 
 | 
               VXGE_HW_RXMAC_VCFG0_ALL_VID_EN); 
 | 
  
 | 
        writeq(val64, &vpath->vp_reg->rxmac_vcfg0); 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_vpath_bcast_enable - Enable broadcast 
 | 
 * @vp: Vpath handle. 
 | 
 * 
 | 
 * Enable receiving broadcasts. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_bcast_enable( 
 | 
            struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if ((vp == NULL) || (vp->vpath->ringh == NULL)) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    val64 = readq(&vpath->vp_reg->rxmac_vcfg0); 
 | 
  
 | 
    if (!(val64 & VXGE_HW_RXMAC_VCFG0_BCAST_EN)) { 
 | 
        val64 |= VXGE_HW_RXMAC_VCFG0_BCAST_EN; 
 | 
        writeq(val64, &vpath->vp_reg->rxmac_vcfg0); 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mcast_enable - Enable multicast addresses. 
 | 
 * @vp: Vpath handle. 
 | 
 * 
 | 
 * Enable Titan-e multicast addresses. 
 | 
 * Returns: VXGE_HW_OK on success. 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_mcast_enable( 
 | 
            struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if ((vp == NULL) || (vp->vpath->ringh == NULL)) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    val64 = readq(&vpath->vp_reg->rxmac_vcfg0); 
 | 
  
 | 
    if (!(val64 & VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN)) { 
 | 
        val64 |= VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN; 
 | 
        writeq(val64, &vpath->vp_reg->rxmac_vcfg0); 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_mcast_disable - Disable  multicast addresses. 
 | 
 * @vp: Vpath handle. 
 | 
 * 
 | 
 * Disable Titan-e multicast addresses. 
 | 
 * Returns: VXGE_HW_OK - success. 
 | 
 * VXGE_HW_ERR_INVALID_HANDLE - Invalid handle 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status 
 | 
vxge_hw_vpath_mcast_disable(struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if ((vp == NULL) || (vp->vpath->ringh == NULL)) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    vpath = vp->vpath; 
 | 
  
 | 
    val64 = readq(&vpath->vp_reg->rxmac_vcfg0); 
 | 
  
 | 
    if (val64 & VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN) { 
 | 
        val64 &= ~VXGE_HW_RXMAC_VCFG0_MCAST_ALL_ADDR_EN; 
 | 
        writeq(val64, &vpath->vp_reg->rxmac_vcfg0); 
 | 
    } 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * vxge_hw_vpath_alarm_process - Process Alarms. 
 | 
 * @vpath: Virtual Path. 
 | 
 * @skip_alarms: Do not clear the alarms 
 | 
 * 
 | 
 * Process vpath alarms. 
 | 
 * 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_alarm_process( 
 | 
            struct __vxge_hw_vpath_handle *vp, 
 | 
            u32 skip_alarms) 
 | 
{ 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
  
 | 
    if (vp == NULL) { 
 | 
        status = VXGE_HW_ERR_INVALID_HANDLE; 
 | 
        goto exit; 
 | 
    } 
 | 
  
 | 
    status = __vxge_hw_vpath_alarm_process(vp->vpath, skip_alarms); 
 | 
exit: 
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_msix_set - Associate MSIX vectors with TIM interrupts and 
 | 
 *                            alrms 
 | 
 * @vp: Virtual Path handle. 
 | 
 * @tim_msix_id: MSIX vectors associated with VXGE_HW_MAX_INTR_PER_VP number of 
 | 
 *             interrupts(Can be repeated). If fifo or ring are not enabled 
 | 
 *             the MSIX vector for that should be set to 0 
 | 
 * @alarm_msix_id: MSIX vector for alarm. 
 | 
 * 
 | 
 * This API will associate a given MSIX vector numbers with the four TIM 
 | 
 * interrupts and alarm interrupt. 
 | 
 */ 
 | 
void 
 | 
vxge_hw_vpath_msix_set(struct __vxge_hw_vpath_handle *vp, int *tim_msix_id, 
 | 
               int alarm_msix_id) 
 | 
{ 
 | 
    u64 val64; 
 | 
    struct __vxge_hw_virtualpath *vpath = vp->vpath; 
 | 
    struct vxge_hw_vpath_reg __iomem *vp_reg = vpath->vp_reg; 
 | 
    u32 vp_id = vp->vpath->vp_id; 
 | 
  
 | 
    val64 =  VXGE_HW_INTERRUPT_CFG0_GROUP0_MSIX_FOR_TXTI( 
 | 
          (vp_id * 4) + tim_msix_id[0]) | 
 | 
         VXGE_HW_INTERRUPT_CFG0_GROUP1_MSIX_FOR_TXTI( 
 | 
          (vp_id * 4) + tim_msix_id[1]); 
 | 
  
 | 
    writeq(val64, &vp_reg->interrupt_cfg0); 
 | 
  
 | 
    writeq(VXGE_HW_INTERRUPT_CFG2_ALARM_MAP_TO_MSG( 
 | 
            (vpath->hldev->first_vp_id * 4) + alarm_msix_id), 
 | 
            &vp_reg->interrupt_cfg2); 
 | 
  
 | 
    if (vpath->hldev->config.intr_mode == 
 | 
                    VXGE_HW_INTR_MODE_MSIX_ONE_SHOT) { 
 | 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( 
 | 
                VXGE_HW_ONE_SHOT_VECT0_EN_ONE_SHOT_VECT0_EN, 
 | 
                0, 32), &vp_reg->one_shot_vect0_en); 
 | 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( 
 | 
                VXGE_HW_ONE_SHOT_VECT1_EN_ONE_SHOT_VECT1_EN, 
 | 
                0, 32), &vp_reg->one_shot_vect1_en); 
 | 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( 
 | 
                VXGE_HW_ONE_SHOT_VECT2_EN_ONE_SHOT_VECT2_EN, 
 | 
                0, 32), &vp_reg->one_shot_vect2_en); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_msix_mask - Mask MSIX Vector. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * @msix_id:  MSIX ID 
 | 
 * 
 | 
 * The function masks the msix interrupt for the given msix_id 
 | 
 * 
 | 
 * Returns: 0, 
 | 
 * Otherwise, VXGE_HW_ERR_WRONG_IRQ if the msix index is out of range 
 | 
 * status. 
 | 
 * See also: 
 | 
 */ 
 | 
void 
 | 
vxge_hw_vpath_msix_mask(struct __vxge_hw_vpath_handle *vp, int msix_id) 
 | 
{ 
 | 
    struct __vxge_hw_device *hldev = vp->vpath->hldev; 
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
        (u32) vxge_bVALn(vxge_mBIT(msix_id  >> 2), 0, 32), 
 | 
        &hldev->common_reg->set_msix_mask_vect[msix_id % 4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_msix_clear - Clear MSIX Vector. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * @msix_id:  MSI ID 
 | 
 * 
 | 
 * The function clears the msix interrupt for the given msix_id 
 | 
 * 
 | 
 * Returns: 0, 
 | 
 * Otherwise, VXGE_HW_ERR_WRONG_IRQ if the msix index is out of range 
 | 
 * status. 
 | 
 * See also: 
 | 
 */ 
 | 
void vxge_hw_vpath_msix_clear(struct __vxge_hw_vpath_handle *vp, int msix_id) 
 | 
{ 
 | 
    struct __vxge_hw_device *hldev = vp->vpath->hldev; 
 | 
  
 | 
    if (hldev->config.intr_mode == VXGE_HW_INTR_MODE_MSIX_ONE_SHOT) 
 | 
        __vxge_hw_pio_mem_write32_upper( 
 | 
            (u32) vxge_bVALn(vxge_mBIT((msix_id >> 2)), 0, 32), 
 | 
            &hldev->common_reg->clr_msix_one_shot_vec[msix_id % 4]); 
 | 
    else 
 | 
        __vxge_hw_pio_mem_write32_upper( 
 | 
            (u32) vxge_bVALn(vxge_mBIT((msix_id >> 2)), 0, 32), 
 | 
            &hldev->common_reg->clear_msix_mask_vect[msix_id % 4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_msix_unmask - Unmask the MSIX Vector. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * @msix_id:  MSI ID 
 | 
 * 
 | 
 * The function unmasks the msix interrupt for the given msix_id 
 | 
 * 
 | 
 * Returns: 0, 
 | 
 * Otherwise, VXGE_HW_ERR_WRONG_IRQ if the msix index is out of range 
 | 
 * status. 
 | 
 * See also: 
 | 
 */ 
 | 
void 
 | 
vxge_hw_vpath_msix_unmask(struct __vxge_hw_vpath_handle *vp, int msix_id) 
 | 
{ 
 | 
    struct __vxge_hw_device *hldev = vp->vpath->hldev; 
 | 
    __vxge_hw_pio_mem_write32_upper( 
 | 
            (u32)vxge_bVALn(vxge_mBIT(msix_id >> 2), 0, 32), 
 | 
            &hldev->common_reg->clear_msix_mask_vect[msix_id%4]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_inta_mask_tx_rx - Mask Tx and Rx interrupts. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * 
 | 
 * Mask Tx and Rx vpath interrupts. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_inta_mask_tx_rx() 
 | 
 */ 
 | 
void vxge_hw_vpath_inta_mask_tx_rx(struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64    tim_int_mask0[4] = {[0 ...3] = 0}; 
 | 
    u32    tim_int_mask1[4] = {[0 ...3] = 0}; 
 | 
    u64    val64; 
 | 
    struct __vxge_hw_device *hldev = vp->vpath->hldev; 
 | 
  
 | 
    VXGE_HW_DEVICE_TIM_INT_MASK_SET(tim_int_mask0, 
 | 
        tim_int_mask1, vp->vpath->vp_id); 
 | 
  
 | 
    val64 = readq(&hldev->common_reg->tim_int_mask0); 
 | 
  
 | 
    if ((tim_int_mask0[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
        (tim_int_mask0[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        writeq((tim_int_mask0[VXGE_HW_VPATH_INTR_TX] | 
 | 
            tim_int_mask0[VXGE_HW_VPATH_INTR_RX] | val64), 
 | 
            &hldev->common_reg->tim_int_mask0); 
 | 
    } 
 | 
  
 | 
    val64 = readl(&hldev->common_reg->tim_int_mask1); 
 | 
  
 | 
    if ((tim_int_mask1[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
        (tim_int_mask1[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        __vxge_hw_pio_mem_write32_upper( 
 | 
            (tim_int_mask1[VXGE_HW_VPATH_INTR_TX] | 
 | 
            tim_int_mask1[VXGE_HW_VPATH_INTR_RX] | val64), 
 | 
            &hldev->common_reg->tim_int_mask1); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_inta_unmask_tx_rx - Unmask Tx and Rx interrupts. 
 | 
 * @vp: Virtual Path handle. 
 | 
 * 
 | 
 * Unmask Tx and Rx vpath interrupts. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_inta_mask_tx_rx() 
 | 
 */ 
 | 
void vxge_hw_vpath_inta_unmask_tx_rx(struct __vxge_hw_vpath_handle *vp) 
 | 
{ 
 | 
    u64    tim_int_mask0[4] = {[0 ...3] = 0}; 
 | 
    u32    tim_int_mask1[4] = {[0 ...3] = 0}; 
 | 
    u64    val64; 
 | 
    struct __vxge_hw_device *hldev = vp->vpath->hldev; 
 | 
  
 | 
    VXGE_HW_DEVICE_TIM_INT_MASK_SET(tim_int_mask0, 
 | 
        tim_int_mask1, vp->vpath->vp_id); 
 | 
  
 | 
    val64 = readq(&hldev->common_reg->tim_int_mask0); 
 | 
  
 | 
    if ((tim_int_mask0[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
       (tim_int_mask0[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        writeq((~(tim_int_mask0[VXGE_HW_VPATH_INTR_TX] | 
 | 
            tim_int_mask0[VXGE_HW_VPATH_INTR_RX])) & val64, 
 | 
            &hldev->common_reg->tim_int_mask0); 
 | 
    } 
 | 
  
 | 
    if ((tim_int_mask1[VXGE_HW_VPATH_INTR_TX] != 0) || 
 | 
       (tim_int_mask1[VXGE_HW_VPATH_INTR_RX] != 0)) { 
 | 
        __vxge_hw_pio_mem_write32_upper( 
 | 
            (~(tim_int_mask1[VXGE_HW_VPATH_INTR_TX] | 
 | 
              tim_int_mask1[VXGE_HW_VPATH_INTR_RX])) & val64, 
 | 
            &hldev->common_reg->tim_int_mask1); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_poll_rx - Poll Rx Virtual Path for completed 
 | 
 * descriptors and process the same. 
 | 
 * @ring: Handle to the ring object used for receive 
 | 
 * 
 | 
 * The function    polls the Rx for the completed    descriptors and    calls 
 | 
 * the driver via supplied completion    callback. 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK, if the polling is completed successful. 
 | 
 * VXGE_HW_COMPLETIONS_REMAIN: There are still more completed 
 | 
 * descriptors available which are yet to be processed. 
 | 
 * 
 | 
 * See also: vxge_hw_vpath_poll_rx() 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) 
 | 
{ 
 | 
    u8 t_code; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
    void *first_rxdh; 
 | 
    int new_count = 0; 
 | 
  
 | 
    ring->cmpl_cnt = 0; 
 | 
  
 | 
    status = vxge_hw_ring_rxd_next_completed(ring, &first_rxdh, &t_code); 
 | 
    if (status == VXGE_HW_OK) 
 | 
        ring->callback(ring, first_rxdh, 
 | 
            t_code, ring->channel.userdata); 
 | 
  
 | 
    if (ring->cmpl_cnt != 0) { 
 | 
        ring->doorbell_cnt += ring->cmpl_cnt; 
 | 
        if (ring->doorbell_cnt >= ring->rxds_limit) { 
 | 
            /* 
 | 
             * Each RxD is of 4 qwords, update the number of 
 | 
             * qwords replenished 
 | 
             */ 
 | 
            new_count = (ring->doorbell_cnt * 4); 
 | 
  
 | 
            /* For each block add 4 more qwords */ 
 | 
            ring->total_db_cnt += ring->doorbell_cnt; 
 | 
            if (ring->total_db_cnt >= ring->rxds_per_block) { 
 | 
                new_count += 4; 
 | 
                /* Reset total count */ 
 | 
                ring->total_db_cnt %= ring->rxds_per_block; 
 | 
            } 
 | 
            writeq(VXGE_HW_PRC_RXD_DOORBELL_NEW_QW_CNT(new_count), 
 | 
                &ring->vp_reg->prc_rxd_doorbell); 
 | 
            readl(&ring->common_reg->titan_general_int_status); 
 | 
            ring->doorbell_cnt = 0; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return status; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * vxge_hw_vpath_poll_tx - Poll Tx for completed descriptors and process the same. 
 | 
 * @fifo: Handle to the fifo object used for non offload send 
 | 
 * @skb_ptr: pointer to skb 
 | 
 * @nr_skb: number of skbs 
 | 
 * @more: more is coming 
 | 
 * 
 | 
 * The function polls the Tx for the completed descriptors and calls 
 | 
 * the driver via supplied completion callback. 
 | 
 * 
 | 
 * Returns: VXGE_HW_OK, if the polling is completed successful. 
 | 
 * VXGE_HW_COMPLETIONS_REMAIN: There are still more completed 
 | 
 * descriptors available which are yet to be processed. 
 | 
 */ 
 | 
enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo, 
 | 
                    struct sk_buff ***skb_ptr, int nr_skb, 
 | 
                    int *more) 
 | 
{ 
 | 
    enum vxge_hw_fifo_tcode t_code; 
 | 
    void *first_txdlh; 
 | 
    enum vxge_hw_status status = VXGE_HW_OK; 
 | 
    struct __vxge_hw_channel *channel; 
 | 
  
 | 
    channel = &fifo->channel; 
 | 
  
 | 
    status = vxge_hw_fifo_txdl_next_completed(fifo, 
 | 
                &first_txdlh, &t_code); 
 | 
    if (status == VXGE_HW_OK) 
 | 
        if (fifo->callback(fifo, first_txdlh, t_code, 
 | 
            channel->userdata, skb_ptr, nr_skb, more) != VXGE_HW_OK) 
 | 
            status = VXGE_HW_COMPLETIONS_REMAIN; 
 | 
  
 | 
    return status; 
 | 
} 
 |