mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2428 lines
64 KiB
2428 lines
64 KiB
/****************************************************************************** |
|
* 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; |
|
}
|
|
|