| /* |
| * vxge-traffic.c: iPXE driver for Neterion Inc's X3100 Series 10GbE |
| * PCIe I/O Virtualized Server Adapter. |
| * |
| * Copyright(c) 2002-2010 Neterion Inc. |
| * |
| * 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. |
| * |
| */ |
| |
| FILE_LICENCE(GPL2_ONLY); |
| |
| #include <ipxe/netdevice.h> |
| #include <errno.h> |
| |
| #include "vxge_traffic.h" |
| #include "vxge_config.h" |
| #include "vxge_main.h" |
| |
| /* |
| * vxge_hw_vpath_intr_enable - Enable vpath interrupts. |
| * @vpath: 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_virtualpath *vpath) |
| { |
| struct vxge_hw_vpath_reg *vp_reg; |
| enum vxge_hw_status status = VXGE_HW_OK; |
| |
| 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. |
| * @vpath: 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_virtualpath *vpath) |
| { |
| enum vxge_hw_status status = VXGE_HW_OK; |
| struct vxge_hw_vpath_reg __iomem *vp_reg; |
| |
| 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; |
| } |
| |
| /** |
| * 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); |
| |
| return; |
| } |
| |
| /** |
| * 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 = 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); |
| |
| return; |
| } |
| |
| /** |
| * 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) |
| { |
| u64 val64; |
| u32 val32; |
| |
| vxge_hw_device_mask_all(hldev); |
| |
| vxge_hw_vpath_intr_enable(&hldev->virtual_path); |
| |
| 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); |
| |
| /* We have not enabled the top level interrupt yet. |
| * This will be controlled from vxge_irq() entry api. |
| */ |
| return; |
| } |
| |
| /** |
| * 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) |
| { |
| 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); |
| |
| vxge_hw_vpath_intr_disable(&hldev->virtual_path); |
| |
| return; |
| } |
| |
| /** |
| * 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 __unused, |
| struct vxge_hw_ring_rxd_1 *rxdp) |
| { |
| rxdp->control_0 = VXGE_HW_RING_RXD_LIST_OWN_ADAPTER; |
| } |
| |
| /** |
| * __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) |
| * |
| * 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) |
| { |
| writeq(VXGE_HW_NODBW_TYPE(VXGE_HW_NODBW_TYPE_NODBW) | |
| VXGE_HW_NODBW_LAST_TXD_NUMBER(num_txds), |
| &fifo->nofl_db->control_0); |
| |
| wmb(); |
| |
| writeq(txdl_ptr, &fifo->nofl_db->txdl_ptr); |
| |
| wmb(); |
| } |
| |
| /** |
| * vxge_hw_fifo_free_txdl_get: fetch next available txd in the fifo |
| * |
| * @fifo: tx channel handle |
| */ |
| struct vxge_hw_fifo_txd * |
| vxge_hw_fifo_free_txdl_get(struct __vxge_hw_fifo *fifo) |
| { |
| struct vxge_hw_fifo_txd *txdp; |
| |
| txdp = fifo->txdl + fifo->sw_offset; |
| if (txdp->control_0 & VXGE_HW_FIFO_TXD_LIST_OWN_ADAPTER) { |
| vxge_debug(VXGE_ERR, "%s:%d, error: txd(%d) owned by hw\n", |
| __func__, __LINE__, fifo->sw_offset); |
| return NULL; |
| } |
| |
| return txdp; |
| } |
| /** |
| * 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. |
| * @iob: data buffer. |
| */ |
| void vxge_hw_fifo_txdl_buffer_set(struct __vxge_hw_fifo *fifo, |
| struct vxge_hw_fifo_txd *txdp, |
| struct io_buffer *iob) |
| { |
| txdp->control_0 = VXGE_HW_FIFO_TXD_GATHER_CODE( |
| VXGE_HW_FIFO_GATHER_CODE_FIRST_LAST); |
| txdp->control_0 |= VXGE_HW_FIFO_TXD_BUFFER_SIZE(iob_len(iob)); |
| |
| txdp->control_1 = VXGE_HW_FIFO_TXD_INT_NUMBER(fifo->tx_intr_num); |
| txdp->control_1 |= VXGE_HW_FIFO_TXD_INT_TYPE_PER_LIST; |
| |
| txdp->host_control = (intptr_t)iob; |
| txdp->buffer_pointer = virt_to_bus(iob->data); |
| } |
| |
| /** |
| * vxge_hw_fifo_txdl_post - Post descriptor on the fifo channel. |
| * @fifo: Handle to the fifo object used for non offload send |
| * @txdp: Tx Descriptor |
| * |
| * 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, |
| struct vxge_hw_fifo_txd *txdp) |
| { |
| txdp->control_0 |= VXGE_HW_FIFO_TXD_LIST_OWN_ADAPTER; |
| |
| __vxge_hw_non_offload_db_post(fifo, (u64) virt_to_bus(txdp), 0); |
| |
| vxge_hw_fifo_txd_offset_up(&fifo->sw_offset); |
| } |
| |
| /* |
| * __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) |
| { |
| u64 val64; |
| u64 alarm_status; |
| enum vxge_hw_status status = VXGE_HW_OK; |
| struct __vxge_hw_device *hldev = NULL; |
| struct vxge_hw_vpath_reg *vp_reg; |
| |
| hldev = vpath->hldev; |
| vp_reg = vpath->vp_reg; |
| alarm_status = readq(&vp_reg->vpath_general_int_status); |
| |
| if (alarm_status == VXGE_HW_ALL_FOXES) { |
| |
| vxge_debug(VXGE_ERR, "%s: %s:%d, slot freeze error\n", |
| hldev->ndev->name, __func__, __LINE__); |
| status = VXGE_HW_ERR_SLOT_FREEZE; |
| goto out; |
| } |
| |
| 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)) { |
| |
| vxge_debug(VXGE_ERR, "%s: %s:%d, Unknown vpath alarm\n", |
| hldev->ndev->name, __func__, __LINE__); |
| status = VXGE_HW_FAIL; |
| 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) |
| ))) { |
| writeq(VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT, |
| &vp_reg->asic_ntwk_vp_err_mask); |
| |
| netdev_link_down(hldev->ndev); |
| vxge_debug(VXGE_INTR, "%s: %s:%d link down\n", |
| hldev->ndev->name, __func__, __LINE__); |
| } |
| |
| 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) |
| ))) { |
| writeq(VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK, |
| &vp_reg->asic_ntwk_vp_err_mask); |
| |
| netdev_link_up(hldev->ndev); |
| vxge_debug(VXGE_INTR, "%s: %s:%d link up\n", |
| hldev->ndev->name, __func__, __LINE__); |
| } |
| |
| writeq(VXGE_HW_INTR_MASK_ALL, |
| &vp_reg->asic_ntwk_vp_err_reg); |
| } |
| } else { |
| vxge_debug(VXGE_INFO, "%s: %s:%d unhandled alarm %llx\n", |
| hldev->ndev->name, __func__, __LINE__, |
| alarm_status); |
| } |
| out: |
| return status; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| return; |
| } |
| |
| |
| /** |
| * vxge_hw_device_begin_irq - Begin IRQ processing. |
| * @hldev: HW device handle. |
| * |
| * 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) |
| { |
| 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 (!val64) { |
| ret = VXGE_HW_ERR_WRONG_IRQ; |
| goto exit; |
| } |
| |
| if (val64 == VXGE_HW_ALL_FOXES) { |
| |
| adapter_status = readq(&hldev->common_reg->adapter_status); |
| |
| if (adapter_status == VXGE_HW_ALL_FOXES) { |
| |
| vxge_debug(VXGE_ERR, "%s: %s:%d critical error " |
| "occurred\n", hldev->ndev->name, |
| __func__, __LINE__); |
| ret = VXGE_HW_ERR_SLOT_FREEZE; |
| goto exit; |
| } |
| } |
| |
| vpath_mask = hldev->vpaths_deployed >> |
| (64 - VXGE_HW_MAX_VIRTUAL_PATHS); |
| if (val64 & VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_TRAFFIC_INT( |
| vpath_mask)) |
| vxge_hw_device_clear_tx_rx(hldev); |
| |
| if (val64 & VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_ALARM_INT) |
| ret = __vxge_hw_vpath_alarm_process(&hldev->virtual_path); |
| |
| exit: |
| return ret; |
| } |
| |
| /** |
| * vxge_hw_vpath_doorbell_rx - Indicates to hw the qwords of receive |
| * descriptors posted. |
| * @ring: Handle to the ring object used for receive |
| * |
| * The function writes the number of qwords of rxds posted during replishment. |
| * Since the function is called frequently, a flush is not required to post the |
| * write transaction. At the very least, the previous write will be flushed |
| * once the subsequent write is made. |
| * |
| * Returns: None. |
| */ |
| void vxge_hw_vpath_doorbell_rx(struct __vxge_hw_ring *ring) |
| { |
| u32 rxds_qw_per_block = VXGE_HW_MAX_RXDS_PER_BLOCK_1 * |
| VXGE_HW_RING_RXD_QWORDS_MODE_1; |
| |
| ring->doorbell_cnt += VXGE_HW_RING_RXD_QWORDS_MODE_1; |
| |
| ring->total_db_cnt += VXGE_HW_RING_RXD_QWORDS_MODE_1; |
| |
| if (ring->total_db_cnt >= rxds_qw_per_block) { |
| /* For each block add 4 more qwords */ |
| ring->doorbell_cnt += VXGE_HW_RING_RXD_QWORDS_MODE_1; |
| |
| /* Reset total count */ |
| ring->total_db_cnt -= rxds_qw_per_block; |
| } |
| |
| if (ring->doorbell_cnt >= ring->rxd_qword_limit) { |
| wmb(); |
| writeq(VXGE_HW_PRC_RXD_DOORBELL_NEW_QW_CNT( |
| ring->doorbell_cnt), |
| &ring->vp_reg->prc_rxd_doorbell); |
| ring->doorbell_cnt = 0; |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| #define ETH_FCS_LEN 4 |
| enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) |
| { |
| struct __vxge_hw_device *hldev; |
| enum vxge_hw_status status = VXGE_HW_OK; |
| struct vxge_hw_ring_rxd_1 *rxd; |
| unsigned int len; |
| enum vxge_hw_ring_tcode tcode; |
| struct io_buffer *rx_iob, *iobuf = NULL; |
| u16 poll_count = 0; |
| |
| hldev = ring->vpathh->hldev; |
| |
| do { |
| rxd = &ring->rxdl->rxd[ring->rxd_offset]; |
| tcode = VXGE_HW_RING_RXD_T_CODE_GET(rxd->control_0); |
| |
| /* if tcode is VXGE_HW_RING_T_CODE_FRM_DROP, it is |
| * possible the ownership bit still set to adapter |
| */ |
| if ((rxd->control_0 & VXGE_HW_RING_RXD_LIST_OWN_ADAPTER) |
| && (tcode == VXGE_HW_RING_T_CODE_OK)) { |
| |
| status = VXGE_HW_INF_NO_MORE_COMPLETED_DESCRIPTORS; |
| goto err0; |
| } |
| |
| vxge_debug(VXGE_INFO, "%s: rx frame received at offset %d\n", |
| hldev->ndev->name, ring->rxd_offset); |
| |
| iobuf = (struct io_buffer *)(intptr_t)rxd->host_control; |
| |
| if (tcode != VXGE_HW_RING_T_CODE_OK) { |
| netdev_rx_err(hldev->ndev, NULL, -EINVAL); |
| vxge_debug(VXGE_ERR, "%s:%d, rx error tcode %d\n", |
| __func__, __LINE__, tcode); |
| status = VXGE_HW_FAIL; |
| goto err1; |
| } |
| |
| len = VXGE_HW_RING_RXD_1_BUFFER0_SIZE_GET(rxd->control_1); |
| len -= ETH_FCS_LEN; |
| |
| rx_iob = alloc_iob(len); |
| if (!rx_iob) { |
| netdev_rx_err(hldev->ndev, NULL, -ENOMEM); |
| vxge_debug(VXGE_ERR, "%s:%d, alloc_iob error\n", |
| __func__, __LINE__); |
| status = VXGE_HW_ERR_OUT_OF_MEMORY; |
| goto err1; |
| } |
| |
| memcpy(iob_put(rx_iob, len), iobuf->data, len); |
| /* Add this packet to the receive queue. */ |
| netdev_rx(hldev->ndev, rx_iob); |
| |
| err1: |
| /* repost the rxd */ |
| rxd->control_0 = rxd->control_1 = 0; |
| vxge_hw_ring_rxd_1b_set(rxd, iobuf, |
| VXGE_LL_MAX_FRAME_SIZE(hldev->vdev)); |
| vxge_hw_ring_rxd_post(ring, rxd); |
| |
| /* repost the qword count for doorbell */ |
| vxge_hw_vpath_doorbell_rx(ring); |
| |
| /* increment the descriptor offset */ |
| vxge_hw_ring_rxd_offset_up(&ring->rxd_offset); |
| |
| } while (++poll_count < ring->rx_poll_weight); |
| err0: |
| 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 |
| * |
| * The function polls the Tx for the completed descriptors and calls |
| * the driver via supplied completion callback. |
| */ |
| enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo) |
| { |
| enum vxge_hw_status status = VXGE_HW_OK; |
| struct vxge_hw_fifo_txd *txdp; |
| |
| txdp = fifo->txdl + fifo->hw_offset; |
| if (!(txdp->control_0 & VXGE_HW_FIFO_TXD_LIST_OWN_ADAPTER) |
| && (txdp->host_control)) { |
| |
| vxge_xmit_compl(fifo, txdp, |
| VXGE_HW_FIFO_TXD_T_CODE_GET(txdp->control_0)); |
| |
| vxge_hw_fifo_txd_offset_up(&fifo->hw_offset); |
| } |
| |
| return status; |
| } |