Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: GPL-2.0
#
# Cavium Liquidio ethernet device driver
#
obj-$(CONFIG_LIQUIDIO_CORE) += liquidio-core.o
liquidio-core-y := \
lio_ethtool.o \
lio_core.o \
request_manager.o \
response_manager.o \
octeon_device.o \
cn66xx_device.o \
cn68xx_device.o \
cn23xx_pf_device.o \
cn23xx_vf_device.o \
octeon_mailbox.o \
octeon_mem_ops.o \
octeon_droq.o \
octeon_nic.o
obj-$(CONFIG_LIQUIDIO) += liquidio.o
liquidio-y := lio_main.o octeon_console.o lio_vf_rep.o
obj-$(CONFIG_LIQUIDIO_VF) += liquidio_vf.o
liquidio_vf-y := lio_vf_main.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn23xx_device.h
* \brief Host Driver: Routines that perform CN23XX specific operations.
*/
#ifndef __CN23XX_PF_DEVICE_H__
#define __CN23XX_PF_DEVICE_H__
#include "cn23xx_pf_regs.h"
/* Register address and configuration for a CN23XX devices.
* If device specific changes need to be made then add a struct to include
* device specific fields as shown in the commented section
*/
struct octeon_cn23xx_pf {
/** PCI interrupt summary register */
u8 __iomem *intr_sum_reg64;
/** PCI interrupt enable register */
u8 __iomem *intr_enb_reg64;
/** The PCI interrupt mask used by interrupt handler */
u64 intr_mask64;
struct octeon_config *conf;
};
#define CN23XX_SLI_DEF_BP 0x40
struct oct_vf_stats {
u64 rx_packets;
u64 tx_packets;
u64 rx_bytes;
u64 tx_bytes;
u64 broadcast;
u64 multicast;
};
int setup_cn23xx_octeon_pf_device(struct octeon_device *oct);
u32 cn23xx_pf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us);
int cn23xx_sriov_config(struct octeon_device *oct);
int cn23xx_fw_loaded(struct octeon_device *oct);
void cn23xx_tell_vf_its_macaddr_changed(struct octeon_device *oct, int vfidx,
u8 *mac);
int cn23xx_get_vf_stats(struct octeon_device *oct, int ifidx,
struct oct_vf_stats *stats);
#endif
@@ -0,0 +1,599 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn23xx_regs.h
* \brief Host Driver: Register Address and Register Mask values for
* Octeon CN23XX devices.
*/
#ifndef __CN23XX_PF_REGS_H__
#define __CN23XX_PF_REGS_H__
#define CN23XX_CONFIG_VENDOR_ID 0x00
#define CN23XX_CONFIG_DEVICE_ID 0x02
#define CN23XX_CONFIG_XPANSION_BAR 0x38
#define CN23XX_CONFIG_MSIX_CAP 0x50
#define CN23XX_CONFIG_MSIX_LMSI 0x54
#define CN23XX_CONFIG_MSIX_UMSI 0x58
#define CN23XX_CONFIG_MSIX_MSIMD 0x5C
#define CN23XX_CONFIG_MSIX_MSIMM 0x60
#define CN23XX_CONFIG_MSIX_MSIMP 0x64
#define CN23XX_CONFIG_PCIE_CAP 0x70
#define CN23XX_CONFIG_PCIE_DEVCAP 0x74
#define CN23XX_CONFIG_PCIE_DEVCTL 0x78
#define CN23XX_CONFIG_PCIE_LINKCAP 0x7C
#define CN23XX_CONFIG_PCIE_LINKCTL 0x80
#define CN23XX_CONFIG_PCIE_SLOTCAP 0x84
#define CN23XX_CONFIG_PCIE_SLOTCTL 0x88
#define CN23XX_CONFIG_PCIE_DEVCTL2 0x98
#define CN23XX_CONFIG_PCIE_LINKCTL2 0xA0
#define CN23XX_CONFIG_PCIE_UNCORRECT_ERR_MASK 0x108
#define CN23XX_CONFIG_PCIE_CORRECT_ERR_STATUS 0x110
#define CN23XX_CONFIG_PCIE_DEVCTL_MASK 0x00040000
#define CN23XX_PCIE_SRIOV_FDL 0x188
#define CN23XX_PCIE_SRIOV_FDL_BIT_POS 0x10
#define CN23XX_PCIE_SRIOV_FDL_MASK 0xFF
#define CN23XX_CONFIG_PCIE_FLTMSK 0x720
#define CN23XX_CONFIG_SRIOV_VFDEVID 0x190
#define CN23XX_CONFIG_SRIOV_BAR_START 0x19C
#define CN23XX_CONFIG_SRIOV_BARX(i) \
(CN23XX_CONFIG_SRIOV_BAR_START + ((i) * 4))
#define CN23XX_CONFIG_SRIOV_BAR_PF 0x08
#define CN23XX_CONFIG_SRIOV_BAR_64BIT 0x04
#define CN23XX_CONFIG_SRIOV_BAR_IO 0x01
/* ############## BAR0 Registers ################ */
#define CN23XX_SLI_CTL_PORT_START 0x286E0
#define CN23XX_PORT_OFFSET 0x10
#define CN23XX_SLI_CTL_PORT(p) \
(CN23XX_SLI_CTL_PORT_START + ((p) * CN23XX_PORT_OFFSET))
/* 2 scatch registers (64-bit) */
#define CN23XX_SLI_WINDOW_CTL 0x282E0
#define CN23XX_SLI_SCRATCH1 0x283C0
#define CN23XX_SLI_SCRATCH2 0x283D0
#define CN23XX_SLI_WINDOW_CTL_DEFAULT 0x200000ULL
/* 1 registers (64-bit) - SLI_CTL_STATUS */
#define CN23XX_SLI_CTL_STATUS 0x28570
/* SLI Packet Input Jabber Register (64 bit register)
* <31:0> for Byte count for limiting sizes of packet sizes
* that are allowed for sli packet inbound packets.
* the default value is 0xFA00(=64000).
*/
#define CN23XX_SLI_PKT_IN_JABBER 0x29170
/* The input jabber is used to determine the TSO max size.
* Due to H/W limitation, this needs to be reduced to 60000
* in order to use H/W TSO and avoid the WQE malformation
* PKO_BUG_24989_WQE_LEN
*/
#define CN23XX_DEFAULT_INPUT_JABBER 0xEA60 /*60000*/
#define CN23XX_WIN_WR_ADDR_LO 0x20000
#define CN23XX_WIN_WR_ADDR_HI 0x20004
#define CN23XX_WIN_WR_ADDR64 CN23XX_WIN_WR_ADDR_LO
#define CN23XX_WIN_RD_ADDR_LO 0x20010
#define CN23XX_WIN_RD_ADDR_HI 0x20014
#define CN23XX_WIN_RD_ADDR64 CN23XX_WIN_RD_ADDR_LO
#define CN23XX_WIN_WR_DATA_LO 0x20020
#define CN23XX_WIN_WR_DATA_HI 0x20024
#define CN23XX_WIN_WR_DATA64 CN23XX_WIN_WR_DATA_LO
#define CN23XX_WIN_RD_DATA_LO 0x20040
#define CN23XX_WIN_RD_DATA_HI 0x20044
#define CN23XX_WIN_RD_DATA64 CN23XX_WIN_RD_DATA_LO
#define CN23XX_WIN_WR_MASK_LO 0x20030
#define CN23XX_WIN_WR_MASK_HI 0x20034
#define CN23XX_WIN_WR_MASK_REG CN23XX_WIN_WR_MASK_LO
#define CN23XX_SLI_MAC_CREDIT_CNT 0x23D70
/* 4 registers (64-bit) for mapping IOQs to MACs(PEMs)-
* SLI_PKT_MAC(0..3)_PF(0..1)_RINFO
*/
#define CN23XX_SLI_PKT_MAC_RINFO_START64 0x29030
/*1 register (64-bit) to determine whether IOQs are in reset. */
#define CN23XX_SLI_PKT_IOQ_RING_RST 0x291E0
/* Each Input Queue register is at a 16-byte Offset in BAR0 */
#define CN23XX_IQ_OFFSET 0x20000
#define CN23XX_MAC_RINFO_OFFSET 0x20
#define CN23XX_PF_RINFO_OFFSET 0x10
#define CN23XX_SLI_PKT_MAC_RINFO64(mac, pf) \
(CN23XX_SLI_PKT_MAC_RINFO_START64 + \
((mac) * CN23XX_MAC_RINFO_OFFSET) + \
((pf) * CN23XX_PF_RINFO_OFFSET))
/** mask for total rings, setting TRS to base */
#define CN23XX_PKT_MAC_CTL_RINFO_TRS BIT_ULL(16)
/** mask for starting ring number: setting SRN <6:0> = 0x7F */
#define CN23XX_PKT_MAC_CTL_RINFO_SRN (0x7F)
/* Starting bit of the TRS field in CN23XX_SLI_PKT_MAC_RINFO64 register */
#define CN23XX_PKT_MAC_CTL_RINFO_TRS_BIT_POS 16
/* Starting bit of SRN field in CN23XX_SLI_PKT_MAC_RINFO64 register */
#define CN23XX_PKT_MAC_CTL_RINFO_SRN_BIT_POS 0
/* Starting bit of RPVF field in CN23XX_SLI_PKT_MAC_RINFO64 register */
#define CN23XX_PKT_MAC_CTL_RINFO_RPVF_BIT_POS 32
/* Starting bit of NVFS field in CN23XX_SLI_PKT_MAC_RINFO64 register */
#define CN23XX_PKT_MAC_CTL_RINFO_NVFS_BIT_POS 48
/*###################### REQUEST QUEUE #########################*/
/* 64 registers for Input Queue Instr Count - SLI_PKT_IN_DONE0_CNTS */
#define CN23XX_SLI_IQ_INSTR_COUNT_START64 0x10040
/* 64 registers for Input Queues Start Addr - SLI_PKT0_INSTR_BADDR */
#define CN23XX_SLI_IQ_BASE_ADDR_START64 0x10010
/* 64 registers for Input Doorbell - SLI_PKT0_INSTR_BAOFF_DBELL */
#define CN23XX_SLI_IQ_DOORBELL_START 0x10020
/* 64 registers for Input Queue size - SLI_PKT0_INSTR_FIFO_RSIZE */
#define CN23XX_SLI_IQ_SIZE_START 0x10030
/* 64 registers (64-bit) - ES, RO, NS, Arbitration for Input Queue Data &
* gather list fetches. SLI_PKT(0..63)_INPUT_CONTROL.
*/
#define CN23XX_SLI_IQ_PKT_CONTROL_START64 0x10000
/*------- Request Queue Macros ---------*/
#define CN23XX_SLI_IQ_PKT_CONTROL64(iq) \
(CN23XX_SLI_IQ_PKT_CONTROL_START64 + ((iq) * CN23XX_IQ_OFFSET))
#define CN23XX_SLI_IQ_BASE_ADDR64(iq) \
(CN23XX_SLI_IQ_BASE_ADDR_START64 + ((iq) * CN23XX_IQ_OFFSET))
#define CN23XX_SLI_IQ_SIZE(iq) \
(CN23XX_SLI_IQ_SIZE_START + ((iq) * CN23XX_IQ_OFFSET))
#define CN23XX_SLI_IQ_DOORBELL(iq) \
(CN23XX_SLI_IQ_DOORBELL_START + ((iq) * CN23XX_IQ_OFFSET))
#define CN23XX_SLI_IQ_INSTR_COUNT64(iq) \
(CN23XX_SLI_IQ_INSTR_COUNT_START64 + ((iq) * CN23XX_IQ_OFFSET))
/*------------------ Masks ----------------*/
#define CN23XX_PKT_INPUT_CTL_VF_NUM BIT_ULL(32)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM BIT(29)
/* Number of instructions to be read in one MAC read request.
* setting to Max value(4)
*/
#define CN23XX_PKT_INPUT_CTL_RDSIZE (3 << 25)
#define CN23XX_PKT_INPUT_CTL_IS_64B BIT(24)
#define CN23XX_PKT_INPUT_CTL_RST BIT(23)
#define CN23XX_PKT_INPUT_CTL_QUIET BIT(28)
#define CN23XX_PKT_INPUT_CTL_RING_ENB BIT(22)
#define CN23XX_PKT_INPUT_CTL_DATA_NS BIT(8)
#define CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP BIT(6)
#define CN23XX_PKT_INPUT_CTL_DATA_RO BIT(5)
#define CN23XX_PKT_INPUT_CTL_USE_CSR BIT(4)
#define CN23XX_PKT_INPUT_CTL_GATHER_NS BIT(3)
#define CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP (2)
#define CN23XX_PKT_INPUT_CTL_GATHER_RO (1)
/** Rings per Virtual Function **/
#define CN23XX_PKT_INPUT_CTL_RPVF_MASK (0x3F)
#define CN23XX_PKT_INPUT_CTL_RPVF_POS (48)
/** These bits[47:44] select the Physical function number within the MAC */
#define CN23XX_PKT_INPUT_CTL_PF_NUM_MASK (0x7)
#define CN23XX_PKT_INPUT_CTL_PF_NUM_POS (45)
/** These bits[43:32] select the function number within the PF */
#define CN23XX_PKT_INPUT_CTL_VF_NUM_MASK (0x1FFF)
#define CN23XX_PKT_INPUT_CTL_VF_NUM_POS (32)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM_MASK (0x3)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM_POS (29)
#define CN23XX_PKT_IN_DONE_WMARK_MASK (0xFFFFULL)
#define CN23XX_PKT_IN_DONE_WMARK_BIT_POS (32)
#define CN23XX_PKT_IN_DONE_CNT_MASK (0x00000000FFFFFFFFULL)
#ifdef __LITTLE_ENDIAN_BITFIELD
#define CN23XX_PKT_INPUT_CTL_MASK \
(CN23XX_PKT_INPUT_CTL_RDSIZE | \
CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP | \
CN23XX_PKT_INPUT_CTL_USE_CSR)
#else
#define CN23XX_PKT_INPUT_CTL_MASK \
(CN23XX_PKT_INPUT_CTL_RDSIZE | \
CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP | \
CN23XX_PKT_INPUT_CTL_USE_CSR | \
CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP)
#endif
/** Masks for SLI_PKT_IN_DONE(0..63)_CNTS Register */
#define CN23XX_IN_DONE_CNTS_PI_INT BIT_ULL(62)
#define CN23XX_IN_DONE_CNTS_CINT_ENB BIT_ULL(48)
/*############################ OUTPUT QUEUE #########################*/
/* 64 registers for Output queue control - SLI_PKT(0..63)_OUTPUT_CONTROL */
#define CN23XX_SLI_OQ_PKT_CONTROL_START 0x10050
/* 64 registers for Output queue buffer and info size - SLI_PKT0_OUT_SIZE */
#define CN23XX_SLI_OQ0_BUFF_INFO_SIZE 0x10060
/* 64 registers for Output Queue Start Addr - SLI_PKT0_SLIST_BADDR */
#define CN23XX_SLI_OQ_BASE_ADDR_START64 0x10070
/* 64 registers for Output Queue Packet Credits - SLI_PKT0_SLIST_BAOFF_DBELL */
#define CN23XX_SLI_OQ_PKT_CREDITS_START 0x10080
/* 64 registers for Output Queue size - SLI_PKT0_SLIST_FIFO_RSIZE */
#define CN23XX_SLI_OQ_SIZE_START 0x10090
/* 64 registers for Output Queue Packet Count - SLI_PKT0_CNTS */
#define CN23XX_SLI_OQ_PKT_SENT_START 0x100B0
/* 64 registers for Output Queue INT Levels - SLI_PKT0_INT_LEVELS */
#define CN23XX_SLI_OQ_PKT_INT_LEVELS_START64 0x100A0
/* Each Output Queue register is at a 16-byte Offset in BAR0 */
#define CN23XX_OQ_OFFSET 0x20000
/* 1 (64-bit register) for Output Queue backpressure across all rings. */
#define CN23XX_SLI_OQ_WMARK 0x29180
/* Global pkt control register */
#define CN23XX_SLI_GBL_CONTROL 0x29210
/* Backpressure enable register for PF0 */
#define CN23XX_SLI_OUT_BP_EN_W1S 0x29260
/* Backpressure enable register for PF1 */
#define CN23XX_SLI_OUT_BP_EN2_W1S 0x29270
/* Backpressure disable register for PF0 */
#define CN23XX_SLI_OUT_BP_EN_W1C 0x29280
/* Backpressure disable register for PF1 */
#define CN23XX_SLI_OUT_BP_EN2_W1C 0x29290
/*------- Output Queue Macros ---------*/
#define CN23XX_SLI_OQ_PKT_CONTROL(oq) \
(CN23XX_SLI_OQ_PKT_CONTROL_START + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_BASE_ADDR64(oq) \
(CN23XX_SLI_OQ_BASE_ADDR_START64 + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_SIZE(oq) \
(CN23XX_SLI_OQ_SIZE_START + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_BUFF_INFO_SIZE(oq) \
(CN23XX_SLI_OQ0_BUFF_INFO_SIZE + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_PKTS_SENT(oq) \
(CN23XX_SLI_OQ_PKT_SENT_START + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_PKTS_CREDIT(oq) \
(CN23XX_SLI_OQ_PKT_CREDITS_START + ((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_PKT_INT_LEVELS(oq) \
(CN23XX_SLI_OQ_PKT_INT_LEVELS_START64 + \
((oq) * CN23XX_OQ_OFFSET))
/*Macro's for accessing CNT and TIME separately from INT_LEVELS*/
#define CN23XX_SLI_OQ_PKT_INT_LEVELS_CNT(oq) \
(CN23XX_SLI_OQ_PKT_INT_LEVELS_START64 + \
((oq) * CN23XX_OQ_OFFSET))
#define CN23XX_SLI_OQ_PKT_INT_LEVELS_TIME(oq) \
(CN23XX_SLI_OQ_PKT_INT_LEVELS_START64 + \
((oq) * CN23XX_OQ_OFFSET) + 4)
/*------------------ Masks ----------------*/
#define CN23XX_PKT_OUTPUT_CTL_TENB BIT(13)
#define CN23XX_PKT_OUTPUT_CTL_CENB BIT(12)
#define CN23XX_PKT_OUTPUT_CTL_IPTR BIT(11)
#define CN23XX_PKT_OUTPUT_CTL_ES BIT(9)
#define CN23XX_PKT_OUTPUT_CTL_NSR BIT(8)
#define CN23XX_PKT_OUTPUT_CTL_ROR BIT(7)
#define CN23XX_PKT_OUTPUT_CTL_DPTR BIT(6)
#define CN23XX_PKT_OUTPUT_CTL_BMODE BIT(5)
#define CN23XX_PKT_OUTPUT_CTL_ES_P BIT(3)
#define CN23XX_PKT_OUTPUT_CTL_NSR_P BIT(2)
#define CN23XX_PKT_OUTPUT_CTL_ROR_P BIT(1)
#define CN23XX_PKT_OUTPUT_CTL_RING_ENB BIT(0)
/*######################### Mailbox Reg Macros ########################*/
#define CN23XX_SLI_PKT_MBOX_INT_START 0x10210
#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START 0x10200
#define CN23XX_SLI_MAC_PF_MBOX_INT_START 0x27380
#define CN23XX_SLI_MBOX_OFFSET 0x20000
#define CN23XX_SLI_MBOX_SIG_IDX_OFFSET 0x8
#define CN23XX_SLI_PKT_MBOX_INT(q) \
(CN23XX_SLI_PKT_MBOX_INT_START + ((q) * CN23XX_SLI_MBOX_OFFSET))
#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG(q, idx) \
(CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START + \
((q) * CN23XX_SLI_MBOX_OFFSET + \
(idx) * CN23XX_SLI_MBOX_SIG_IDX_OFFSET))
#define CN23XX_SLI_MAC_PF_MBOX_INT(mac, pf) \
(CN23XX_SLI_MAC_PF_MBOX_INT_START + \
((mac) * CN23XX_MAC_INT_OFFSET + \
(pf) * CN23XX_PF_INT_OFFSET))
/*######################### DMA Counters #########################*/
/* 2 registers (64-bit) - DMA Count - 1 for each DMA counter 0/1. */
#define CN23XX_DMA_CNT_START 0x28400
/* 2 registers (64-bit) - DMA Timer 0/1, contains DMA timer values */
/* SLI_DMA_0_TIM */
#define CN23XX_DMA_TIM_START 0x28420
/* 2 registers (64-bit) - DMA count & Time Interrupt threshold -
* SLI_DMA_0_INT_LEVEL
*/
#define CN23XX_DMA_INT_LEVEL_START 0x283E0
/* Each DMA register is at a 16-byte Offset in BAR0 */
#define CN23XX_DMA_OFFSET 0x10
/*---------- DMA Counter Macros ---------*/
#define CN23XX_DMA_CNT(dq) \
(CN23XX_DMA_CNT_START + ((dq) * CN23XX_DMA_OFFSET))
#define CN23XX_DMA_INT_LEVEL(dq) \
(CN23XX_DMA_INT_LEVEL_START + ((dq) * CN23XX_DMA_OFFSET))
#define CN23XX_DMA_PKT_INT_LEVEL(dq) \
(CN23XX_DMA_INT_LEVEL_START + ((dq) * CN23XX_DMA_OFFSET))
#define CN23XX_DMA_TIME_INT_LEVEL(dq) \
(CN23XX_DMA_INT_LEVEL_START + 4 + ((dq) * CN23XX_DMA_OFFSET))
#define CN23XX_DMA_TIM(dq) \
(CN23XX_DMA_TIM_START + ((dq) * CN23XX_DMA_OFFSET))
/*######################## MSIX TABLE #########################*/
#define CN23XX_MSIX_TABLE_ADDR_START 0x0
#define CN23XX_MSIX_TABLE_DATA_START 0x8
#define CN23XX_MSIX_TABLE_SIZE 0x10
#define CN23XX_MSIX_TABLE_ENTRIES 0x41
#define CN23XX_MSIX_ENTRY_VECTOR_CTL BIT_ULL(32)
#define CN23XX_MSIX_TABLE_ADDR(idx) \
(CN23XX_MSIX_TABLE_ADDR_START + ((idx) * CN23XX_MSIX_TABLE_SIZE))
#define CN23XX_MSIX_TABLE_DATA(idx) \
(CN23XX_MSIX_TABLE_DATA_START + ((idx) * CN23XX_MSIX_TABLE_SIZE))
/*######################## INTERRUPTS #########################*/
#define CN23XX_MAC_INT_OFFSET 0x20
#define CN23XX_PF_INT_OFFSET 0x10
/* 1 register (64-bit) for Interrupt Summary */
#define CN23XX_SLI_INT_SUM64 0x27000
/* 4 registers (64-bit) for Interrupt Enable for each Port */
#define CN23XX_SLI_INT_ENB64 0x27080
#define CN23XX_SLI_MAC_PF_INT_SUM64(mac, pf) \
(CN23XX_SLI_INT_SUM64 + \
((mac) * CN23XX_MAC_INT_OFFSET) + \
((pf) * CN23XX_PF_INT_OFFSET))
#define CN23XX_SLI_MAC_PF_INT_ENB64(mac, pf) \
(CN23XX_SLI_INT_ENB64 + \
((mac) * CN23XX_MAC_INT_OFFSET) + \
((pf) * CN23XX_PF_INT_OFFSET))
/* 1 register (64-bit) to indicate which Output Queue reached pkt threshold */
#define CN23XX_SLI_PKT_CNT_INT 0x29130
/* 1 register (64-bit) to indicate which Output Queue reached time threshold */
#define CN23XX_SLI_PKT_TIME_INT 0x29140
/*------------------ Interrupt Masks ----------------*/
#define CN23XX_INTR_PO_INT BIT_ULL(63)
#define CN23XX_INTR_PI_INT BIT_ULL(62)
#define CN23XX_INTR_MBOX_INT BIT_ULL(61)
#define CN23XX_INTR_RESEND BIT_ULL(60)
#define CN23XX_INTR_CINT_ENB BIT_ULL(48)
#define CN23XX_INTR_MBOX_ENB BIT(0)
#define CN23XX_INTR_RML_TIMEOUT_ERR (1)
#define CN23XX_INTR_MIO_INT BIT(1)
#define CN23XX_INTR_RESERVED1 (3 << 2)
#define CN23XX_INTR_PKT_COUNT BIT(4)
#define CN23XX_INTR_PKT_TIME BIT(5)
#define CN23XX_INTR_RESERVED2 (3 << 6)
#define CN23XX_INTR_M0UPB0_ERR BIT(8)
#define CN23XX_INTR_M0UPWI_ERR BIT(9)
#define CN23XX_INTR_M0UNB0_ERR BIT(10)
#define CN23XX_INTR_M0UNWI_ERR BIT(11)
#define CN23XX_INTR_RESERVED3 (0xFFFFFULL << 12)
#define CN23XX_INTR_DMA0_FORCE BIT_ULL(32)
#define CN23XX_INTR_DMA1_FORCE BIT_ULL(33)
#define CN23XX_INTR_DMA0_COUNT BIT_ULL(34)
#define CN23XX_INTR_DMA1_COUNT BIT_ULL(35)
#define CN23XX_INTR_DMA0_TIME BIT_ULL(36)
#define CN23XX_INTR_DMA1_TIME BIT_ULL(37)
#define CN23XX_INTR_RESERVED4 (0x7FFFFULL << 38)
#define CN23XX_INTR_VF_MBOX BIT_ULL(57)
#define CN23XX_INTR_DMAVF_ERR BIT_ULL(58)
#define CN23XX_INTR_DMAPF_ERR BIT_ULL(59)
#define CN23XX_INTR_PKTVF_ERR BIT_ULL(60)
#define CN23XX_INTR_PKTPF_ERR BIT_ULL(61)
#define CN23XX_INTR_PPVF_ERR BIT_ULL(62)
#define CN23XX_INTR_PPPF_ERR BIT_ULL(63)
#define CN23XX_INTR_DMA0_DATA (CN23XX_INTR_DMA0_TIME)
#define CN23XX_INTR_DMA1_DATA (CN23XX_INTR_DMA1_TIME)
#define CN23XX_INTR_DMA_DATA \
(CN23XX_INTR_DMA0_DATA | CN23XX_INTR_DMA1_DATA)
/* By fault only TIME based */
#define CN23XX_INTR_PKT_DATA (CN23XX_INTR_PKT_TIME)
/* For both COUNT and TIME based */
/* #define CN23XX_INTR_PKT_DATA \
* (CN23XX_INTR_PKT_COUNT | CN23XX_INTR_PKT_TIME)
*/
/* Sum of interrupts for all PCI-Express Data Interrupts */
#define CN23XX_INTR_PCIE_DATA \
(CN23XX_INTR_DMA_DATA | CN23XX_INTR_PKT_DAT)
/* Sum of interrupts for error events */
#define CN23XX_INTR_ERR \
(CN23XX_INTR_M0UPB0_ERR | \
CN23XX_INTR_M0UPWI_ERR | \
CN23XX_INTR_M0UNB0_ERR | \
CN23XX_INTR_M0UNWI_ERR | \
CN23XX_INTR_DMAVF_ERR | \
CN23XX_INTR_DMAPF_ERR | \
CN23XX_INTR_PKTPF_ERR | \
CN23XX_INTR_PPPF_ERR | \
CN23XX_INTR_PPVF_ERR)
/* Programmed Mask for Interrupt Sum */
#define CN23XX_INTR_MASK \
(CN23XX_INTR_DMA_DATA | \
CN23XX_INTR_DMA0_FORCE | \
CN23XX_INTR_DMA1_FORCE | \
CN23XX_INTR_MIO_INT | \
CN23XX_INTR_ERR)
/* 4 Registers (64 - bit) */
#define CN23XX_SLI_S2M_PORT_CTL_START 0x23D80
#define CN23XX_SLI_S2M_PORTX_CTL(port) \
(CN23XX_SLI_S2M_PORT_CTL_START + ((port) * 0x10))
#define CN23XX_SLI_MAC_NUMBER 0x20050
/** PEM(0..3)_BAR1_INDEX(0..15)address is defined as
* addr = (0x00011800C0000100 |port <<24 |idx <<3 )
* Here, port is PEM(0..3) & idx is INDEX(0..15)
*/
#define CN23XX_PEM_BAR1_INDEX_START 0x00011800C0000100ULL
#define CN23XX_PEM_OFFSET 24
#define CN23XX_BAR1_INDEX_OFFSET 3
#define CN23XX_PEM_BAR1_INDEX_REG(port, idx) \
(CN23XX_PEM_BAR1_INDEX_START + (((u64)port) << CN23XX_PEM_OFFSET) + \
((idx) << CN23XX_BAR1_INDEX_OFFSET))
/*############################ DPI #########################*/
/* 1 register (64-bit) - provides DMA Enable */
#define CN23XX_DPI_CTL 0x0001df0000000040ULL
/* 1 register (64-bit) - Controls the DMA IO Operation */
#define CN23XX_DPI_DMA_CONTROL 0x0001df0000000048ULL
/* 1 register (64-bit) - Provides DMA Instr'n Queue Enable */
#define CN23XX_DPI_REQ_GBL_ENB 0x0001df0000000050ULL
/* 1 register (64-bit) - DPI_REQ_ERR_RSP
* Indicates which Instr'n Queue received error response from the IO sub-system
*/
#define CN23XX_DPI_REQ_ERR_RSP 0x0001df0000000058ULL
/* 1 register (64-bit) - DPI_REQ_ERR_RST
* Indicates which Instr'n Queue dropped an Instr'n
*/
#define CN23XX_DPI_REQ_ERR_RST 0x0001df0000000060ULL
/* 6 register (64-bit) - DPI_DMA_ENG(0..5)_EN
* Provides DMA Engine Queue Enable
*/
#define CN23XX_DPI_DMA_ENG0_ENB 0x0001df0000000080ULL
#define CN23XX_DPI_DMA_ENG_ENB(eng) (CN23XX_DPI_DMA_ENG0_ENB + ((eng) * 8))
/* 8 register (64-bit) - DPI_DMA(0..7)_REQQ_CTL
* Provides control bits for transaction on 8 Queues
*/
#define CN23XX_DPI_DMA_REQQ0_CTL 0x0001df0000000180ULL
#define CN23XX_DPI_DMA_REQQ_CTL(q_no) \
(CN23XX_DPI_DMA_REQQ0_CTL + ((q_no) * 8))
/* 6 register (64-bit) - DPI_ENG(0..5)_BUF
* Provides DMA Engine FIFO (Queue) Size
*/
#define CN23XX_DPI_DMA_ENG0_BUF 0x0001df0000000880ULL
#define CN23XX_DPI_DMA_ENG_BUF(eng) \
(CN23XX_DPI_DMA_ENG0_BUF + ((eng) * 8))
/* 4 Registers (64-bit) */
#define CN23XX_DPI_SLI_PRT_CFG_START 0x0001df0000000900ULL
#define CN23XX_DPI_SLI_PRTX_CFG(port) \
(CN23XX_DPI_SLI_PRT_CFG_START + ((port) * 0x8))
/* Masks for DPI_DMA_CONTROL Register */
#define CN23XX_DPI_DMA_COMMIT_MODE BIT_ULL(58)
#define CN23XX_DPI_DMA_PKT_EN BIT_ULL(56)
#define CN23XX_DPI_DMA_ENB (0x0FULL << 48)
/* Set the DMA Control, to update packet count not byte count sent by DMA,
* when we use Interrupt Coalescing (CA mode)
*/
#define CN23XX_DPI_DMA_O_ADD1 BIT(19)
/*selecting 64-bit Byte Swap Mode */
#define CN23XX_DPI_DMA_O_ES BIT(15)
#define CN23XX_DPI_DMA_O_MODE BIT(14)
#define CN23XX_DPI_DMA_CTL_MASK \
(CN23XX_DPI_DMA_COMMIT_MODE | \
CN23XX_DPI_DMA_PKT_EN | \
CN23XX_DPI_DMA_O_ES | \
CN23XX_DPI_DMA_O_MODE)
/*############################ RST #########################*/
#define CN23XX_RST_BOOT 0x0001180006001600ULL
#define CN23XX_RST_SOFT_RST 0x0001180006001680ULL
#define CN23XX_LMC0_RESET_CTL 0x0001180088000180ULL
#define CN23XX_LMC0_RESET_CTL_DDR3RST_MASK 0x0000000000000001ULL
#endif
@@ -0,0 +1,683 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "cn23xx_vf_device.h"
#include "octeon_main.h"
#include "octeon_mailbox.h"
u32 cn23xx_vf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us)
{
/* This gives the SLI clock per microsec */
u32 oqticks_per_us = (u32)oct->pfvf_hsword.coproc_tics_per_us;
/* This gives the clock cycles per millisecond */
oqticks_per_us *= 1000;
/* This gives the oq ticks (1024 core clock cycles) per millisecond */
oqticks_per_us /= 1024;
/* time_intr is in microseconds. The next 2 steps gives the oq ticks
* corressponding to time_intr.
*/
oqticks_per_us *= time_intr_in_us;
oqticks_per_us /= 1000;
return oqticks_per_us;
}
static int cn23xx_vf_reset_io_queues(struct octeon_device *oct, u32 num_queues)
{
u32 loop = BUSY_READING_REG_VF_LOOP_COUNT;
int ret_val = 0;
u32 q_no;
u64 d64;
for (q_no = 0; q_no < num_queues; q_no++) {
/* set RST bit to 1. This bit applies to both IQ and OQ */
d64 = octeon_read_csr64(oct,
CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no));
d64 |= CN23XX_PKT_INPUT_CTL_RST;
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no),
d64);
}
/* wait until the RST bit is clear or the RST and QUIET bits are set */
for (q_no = 0; q_no < num_queues; q_no++) {
u64 reg_val = octeon_read_csr64(oct,
CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no));
while ((READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_RST) &&
!(READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_QUIET) &&
loop) {
WRITE_ONCE(reg_val, octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)));
loop--;
}
if (!loop) {
dev_err(&oct->pci_dev->dev,
"clearing the reset reg failed or setting the quiet reg failed for qno: %u\n",
q_no);
return -1;
}
WRITE_ONCE(reg_val, READ_ONCE(reg_val) &
~CN23XX_PKT_INPUT_CTL_RST);
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no),
READ_ONCE(reg_val));
WRITE_ONCE(reg_val, octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)));
if (READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_RST) {
dev_err(&oct->pci_dev->dev,
"clearing the reset failed for qno: %u\n",
q_no);
ret_val = -1;
}
}
return ret_val;
}
static int cn23xx_vf_setup_global_input_regs(struct octeon_device *oct)
{
struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip;
struct octeon_instr_queue *iq;
u64 q_no, intr_threshold;
u64 d64;
if (cn23xx_vf_reset_io_queues(oct, oct->sriov_info.rings_per_vf))
return -1;
for (q_no = 0; q_no < (oct->sriov_info.rings_per_vf); q_no++) {
void __iomem *inst_cnt_reg;
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_DOORBELL(q_no),
0xFFFFFFFF);
iq = oct->instr_queue[q_no];
if (iq)
inst_cnt_reg = iq->inst_cnt_reg;
else
inst_cnt_reg = (u8 *)oct->mmio[0].hw_addr +
CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no);
d64 = octeon_read_csr64(oct,
CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no));
d64 &= 0xEFFFFFFFFFFFFFFFL;
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no),
d64);
/* Select ES, RO, NS, RDSIZE,DPTR Fomat#0 for
* the Input Queues
*/
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no),
CN23XX_PKT_INPUT_CTL_MASK);
/* set the wmark level to trigger PI_INT */
intr_threshold = CFG_GET_IQ_INTR_PKT(cn23xx->conf) &
CN23XX_PKT_IN_DONE_WMARK_MASK;
writeq((readq(inst_cnt_reg) &
~(CN23XX_PKT_IN_DONE_WMARK_MASK <<
CN23XX_PKT_IN_DONE_WMARK_BIT_POS)) |
(intr_threshold << CN23XX_PKT_IN_DONE_WMARK_BIT_POS),
inst_cnt_reg);
}
return 0;
}
static void cn23xx_vf_setup_global_output_regs(struct octeon_device *oct)
{
u32 reg_val;
u32 q_no;
for (q_no = 0; q_no < (oct->sriov_info.rings_per_vf); q_no++) {
octeon_write_csr(oct, CN23XX_VF_SLI_OQ_PKTS_CREDIT(q_no),
0xFFFFFFFF);
reg_val =
octeon_read_csr(oct, CN23XX_VF_SLI_OQ_PKTS_SENT(q_no));
reg_val &= 0xEFFFFFFFFFFFFFFFL;
reg_val =
octeon_read_csr(oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no));
/* clear IPTR */
reg_val &= ~CN23XX_PKT_OUTPUT_CTL_IPTR;
/* set DPTR */
reg_val |= CN23XX_PKT_OUTPUT_CTL_DPTR;
/* reset BMODE */
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_BMODE);
/* No Relaxed Ordering, No Snoop, 64-bit Byte swap
* for Output Queue ScatterList reset ROR_P, NSR_P
*/
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ROR_P);
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_NSR_P);
#ifdef __LITTLE_ENDIAN_BITFIELD
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ES_P);
#else
reg_val |= (CN23XX_PKT_OUTPUT_CTL_ES_P);
#endif
/* No Relaxed Ordering, No Snoop, 64-bit Byte swap
* for Output Queue Data reset ROR, NSR
*/
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ROR);
reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_NSR);
/* set the ES bit */
reg_val |= (CN23XX_PKT_OUTPUT_CTL_ES);
/* write all the selected settings */
octeon_write_csr(oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no),
reg_val);
}
}
static int cn23xx_setup_vf_device_regs(struct octeon_device *oct)
{
if (cn23xx_vf_setup_global_input_regs(oct))
return -1;
cn23xx_vf_setup_global_output_regs(oct);
return 0;
}
static void cn23xx_setup_vf_iq_regs(struct octeon_device *oct, u32 iq_no)
{
struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
u64 pkt_in_done;
/* Write the start of the input queue's ring and its size */
octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_BASE_ADDR64(iq_no),
iq->base_addr_dma);
octeon_write_csr(oct, CN23XX_VF_SLI_IQ_SIZE(iq_no), iq->max_count);
/* Remember the doorbell & instruction count register addr
* for this queue
*/
iq->doorbell_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_IQ_DOORBELL(iq_no);
iq->inst_cnt_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_IQ_INSTR_COUNT64(iq_no);
dev_dbg(&oct->pci_dev->dev, "InstQ[%d]:dbell reg @ 0x%p instcnt_reg @ 0x%p\n",
iq_no, iq->doorbell_reg, iq->inst_cnt_reg);
/* Store the current instruction counter (used in flush_iq
* calculation)
*/
pkt_in_done = readq(iq->inst_cnt_reg);
if (oct->msix_on) {
/* Set CINT_ENB to enable IQ interrupt */
writeq((pkt_in_done | CN23XX_INTR_CINT_ENB),
iq->inst_cnt_reg);
}
iq->reset_instr_cnt = 0;
}
static void cn23xx_setup_vf_oq_regs(struct octeon_device *oct, u32 oq_no)
{
struct octeon_droq *droq = oct->droq[oq_no];
octeon_write_csr64(oct, CN23XX_VF_SLI_OQ_BASE_ADDR64(oq_no),
droq->desc_ring_dma);
octeon_write_csr(oct, CN23XX_VF_SLI_OQ_SIZE(oq_no), droq->max_count);
octeon_write_csr(oct, CN23XX_VF_SLI_OQ_BUFF_INFO_SIZE(oq_no),
droq->buffer_size);
/* Get the mapped address of the pkt_sent and pkts_credit regs */
droq->pkts_sent_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_OQ_PKTS_SENT(oq_no);
droq->pkts_credit_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_OQ_PKTS_CREDIT(oq_no);
}
static void cn23xx_vf_mbox_thread(struct work_struct *work)
{
struct cavium_wk *wk = (struct cavium_wk *)work;
struct octeon_mbox *mbox = (struct octeon_mbox *)wk->ctxptr;
octeon_mbox_process_message(mbox);
}
static int cn23xx_free_vf_mbox(struct octeon_device *oct)
{
cancel_delayed_work_sync(&oct->mbox[0]->mbox_poll_wk.work);
vfree(oct->mbox[0]);
return 0;
}
static int cn23xx_setup_vf_mbox(struct octeon_device *oct)
{
struct octeon_mbox *mbox = NULL;
mbox = vzalloc(sizeof(*mbox));
if (!mbox)
return 1;
spin_lock_init(&mbox->lock);
mbox->oct_dev = oct;
mbox->q_no = 0;
mbox->state = OCTEON_MBOX_STATE_IDLE;
/* VF mbox interrupt reg */
mbox->mbox_int_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_PKT_MBOX_INT(0);
/* VF reads from SIG0 reg */
mbox->mbox_read_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(0, 0);
/* VF writes into SIG1 reg */
mbox->mbox_write_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(0, 1);
INIT_DELAYED_WORK(&mbox->mbox_poll_wk.work,
cn23xx_vf_mbox_thread);
mbox->mbox_poll_wk.ctxptr = mbox;
oct->mbox[0] = mbox;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
return 0;
}
static int cn23xx_enable_vf_io_queues(struct octeon_device *oct)
{
u32 q_no;
for (q_no = 0; q_no < oct->num_iqs; q_no++) {
u64 reg_val;
/* set the corresponding IQ IS_64B bit */
if (oct->io_qmask.iq64B & BIT_ULL(q_no)) {
reg_val = octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no));
reg_val |= CN23XX_PKT_INPUT_CTL_IS_64B;
octeon_write_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), reg_val);
}
/* set the corresponding IQ ENB bit */
if (oct->io_qmask.iq & BIT_ULL(q_no)) {
reg_val = octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no));
reg_val |= CN23XX_PKT_INPUT_CTL_RING_ENB;
octeon_write_csr64(
oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), reg_val);
}
}
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
u32 reg_val;
/* set the corresponding OQ ENB bit */
if (oct->io_qmask.oq & BIT_ULL(q_no)) {
reg_val = octeon_read_csr(
oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no));
reg_val |= CN23XX_PKT_OUTPUT_CTL_RING_ENB;
octeon_write_csr(
oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no), reg_val);
}
}
return 0;
}
static void cn23xx_disable_vf_io_queues(struct octeon_device *oct)
{
u32 num_queues = oct->num_iqs;
/* per HRM, rings can only be disabled via reset operation,
* NOT via SLI_PKT()_INPUT/OUTPUT_CONTROL[ENB]
*/
if (num_queues < oct->num_oqs)
num_queues = oct->num_oqs;
cn23xx_vf_reset_io_queues(oct, num_queues);
}
void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct)
{
struct octeon_mbox_cmd mbox_cmd;
mbox_cmd.msg.u64 = 0;
mbox_cmd.msg.s.type = OCTEON_MBOX_REQUEST;
mbox_cmd.msg.s.resp_needed = 0;
mbox_cmd.msg.s.cmd = OCTEON_VF_FLR_REQUEST;
mbox_cmd.msg.s.len = 1;
mbox_cmd.q_no = 0;
mbox_cmd.recv_len = 0;
mbox_cmd.recv_status = 0;
mbox_cmd.fn = NULL;
mbox_cmd.fn_arg = NULL;
octeon_mbox_write(oct, &mbox_cmd);
}
EXPORT_SYMBOL_GPL(cn23xx_vf_ask_pf_to_do_flr);
static void octeon_pfvf_hs_callback(struct octeon_device *oct,
struct octeon_mbox_cmd *cmd,
void *arg)
{
u32 major = 0;
memcpy((uint8_t *)&oct->pfvf_hsword, cmd->msg.s.params,
CN23XX_MAILBOX_MSGPARAM_SIZE);
if (cmd->recv_len > 1) {
major = ((struct lio_version *)(cmd->data))->major;
major = major << 16;
}
atomic_set((atomic_t *)arg, major | 1);
}
int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct)
{
struct octeon_mbox_cmd mbox_cmd;
u32 q_no, count = 0;
atomic_t status;
u32 pfmajor;
u32 vfmajor;
u32 ret;
/* Sending VF_ACTIVE indication to the PF driver */
dev_dbg(&oct->pci_dev->dev, "requesting info from pf\n");
mbox_cmd.msg.u64 = 0;
mbox_cmd.msg.s.type = OCTEON_MBOX_REQUEST;
mbox_cmd.msg.s.resp_needed = 1;
mbox_cmd.msg.s.cmd = OCTEON_VF_ACTIVE;
mbox_cmd.msg.s.len = 2;
mbox_cmd.data[0] = 0;
((struct lio_version *)&mbox_cmd.data[0])->major =
LIQUIDIO_BASE_MAJOR_VERSION;
((struct lio_version *)&mbox_cmd.data[0])->minor =
LIQUIDIO_BASE_MINOR_VERSION;
((struct lio_version *)&mbox_cmd.data[0])->micro =
LIQUIDIO_BASE_MICRO_VERSION;
mbox_cmd.q_no = 0;
mbox_cmd.recv_len = 0;
mbox_cmd.recv_status = 0;
mbox_cmd.fn = octeon_pfvf_hs_callback;
mbox_cmd.fn_arg = &status;
octeon_mbox_write(oct, &mbox_cmd);
atomic_set(&status, 0);
do {
schedule_timeout_uninterruptible(1);
} while ((!atomic_read(&status)) && (count++ < 100000));
ret = atomic_read(&status);
if (!ret) {
dev_err(&oct->pci_dev->dev, "octeon_pfvf_handshake timeout\n");
return 1;
}
for (q_no = 0 ; q_no < oct->num_iqs ; q_no++)
oct->instr_queue[q_no]->txpciq.s.pkind = oct->pfvf_hsword.pkind;
vfmajor = LIQUIDIO_BASE_MAJOR_VERSION;
pfmajor = ret >> 16;
if (pfmajor != vfmajor) {
dev_err(&oct->pci_dev->dev,
"VF Liquidio driver (major version %d) is not compatible with Liquidio PF driver (major version %d)\n",
vfmajor, pfmajor);
return 1;
}
dev_dbg(&oct->pci_dev->dev,
"VF Liquidio driver (major version %d), Liquidio PF driver (major version %d)\n",
vfmajor, pfmajor);
dev_dbg(&oct->pci_dev->dev, "got data from pf pkind is %d\n",
oct->pfvf_hsword.pkind);
return 0;
}
EXPORT_SYMBOL_GPL(cn23xx_octeon_pfvf_handshake);
static void cn23xx_handle_vf_mbox_intr(struct octeon_ioq_vector *ioq_vector)
{
struct octeon_device *oct = ioq_vector->oct_dev;
u64 mbox_int_val;
if (!ioq_vector->droq_index) {
/* read and clear by writing 1 */
mbox_int_val = readq(oct->mbox[0]->mbox_int_reg);
writeq(mbox_int_val, oct->mbox[0]->mbox_int_reg);
if (octeon_mbox_read(oct->mbox[0]))
schedule_delayed_work(&oct->mbox[0]->mbox_poll_wk.work,
msecs_to_jiffies(0));
}
}
static u64 cn23xx_vf_msix_interrupt_handler(void *dev)
{
struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev;
struct octeon_device *oct = ioq_vector->oct_dev;
struct octeon_droq *droq = oct->droq[ioq_vector->droq_index];
u64 pkts_sent;
u64 ret = 0;
dev_dbg(&oct->pci_dev->dev, "In %s octeon_dev @ %p\n", __func__, oct);
pkts_sent = readq(droq->pkts_sent_reg);
/* If our device has interrupted, then proceed. Also check
* for all f's if interrupt was triggered on an error
* and the PCI read fails.
*/
if (!pkts_sent || (pkts_sent == 0xFFFFFFFFFFFFFFFFULL))
return ret;
/* Write count reg in sli_pkt_cnts to clear these int. */
if ((pkts_sent & CN23XX_INTR_PO_INT) ||
(pkts_sent & CN23XX_INTR_PI_INT)) {
if (pkts_sent & CN23XX_INTR_PO_INT)
ret |= MSIX_PO_INT;
}
if (pkts_sent & CN23XX_INTR_PI_INT)
/* We will clear the count when we update the read_index. */
ret |= MSIX_PI_INT;
if (pkts_sent & CN23XX_INTR_MBOX_INT) {
cn23xx_handle_vf_mbox_intr(ioq_vector);
ret |= MSIX_MBOX_INT;
}
return ret;
}
static u32 cn23xx_update_read_index(struct octeon_instr_queue *iq)
{
u32 pkt_in_done = readl(iq->inst_cnt_reg);
u32 last_done;
u32 new_idx;
last_done = pkt_in_done - iq->pkt_in_done;
iq->pkt_in_done = pkt_in_done;
/* Modulo of the new index with the IQ size will give us
* the new index. The iq->reset_instr_cnt is always zero for
* cn23xx, so no extra adjustments are needed.
*/
new_idx = (iq->octeon_read_index +
(u32)(last_done & CN23XX_PKT_IN_DONE_CNT_MASK)) %
iq->max_count;
return new_idx;
}
static void cn23xx_enable_vf_interrupt(struct octeon_device *oct, u8 intr_flag)
{
struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip;
u32 q_no, time_threshold;
if (intr_flag & OCTEON_OUTPUT_INTR) {
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
/* Set up interrupt packet and time thresholds
* for all the OQs
*/
time_threshold = cn23xx_vf_get_oq_ticks(
oct, (u32)CFG_GET_OQ_INTR_TIME(cn23xx->conf));
octeon_write_csr64(
oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no),
(CFG_GET_OQ_INTR_PKT(cn23xx->conf) |
((u64)time_threshold << 32)));
}
}
if (intr_flag & OCTEON_INPUT_INTR) {
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
/* Set CINT_ENB to enable IQ interrupt */
octeon_write_csr64(
oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no),
((octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no)) &
~CN23XX_PKT_IN_DONE_CNT_MASK) |
CN23XX_INTR_CINT_ENB));
}
}
/* Set queue-0 MBOX_ENB to enable VF mailbox interrupt */
if (intr_flag & OCTEON_MBOX_INTR) {
octeon_write_csr64(
oct, CN23XX_VF_SLI_PKT_MBOX_INT(0),
(octeon_read_csr64(oct, CN23XX_VF_SLI_PKT_MBOX_INT(0)) |
CN23XX_INTR_MBOX_ENB));
}
}
static void cn23xx_disable_vf_interrupt(struct octeon_device *oct, u8 intr_flag)
{
u32 q_no;
if (intr_flag & OCTEON_OUTPUT_INTR) {
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
/* Write all 1's in INT_LEVEL reg to disable PO_INT */
octeon_write_csr64(
oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no),
0x3fffffffffffff);
}
}
if (intr_flag & OCTEON_INPUT_INTR) {
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
octeon_write_csr64(
oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no),
(octeon_read_csr64(
oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no)) &
~(CN23XX_INTR_CINT_ENB |
CN23XX_PKT_IN_DONE_CNT_MASK)));
}
}
if (intr_flag & OCTEON_MBOX_INTR) {
octeon_write_csr64(
oct, CN23XX_VF_SLI_PKT_MBOX_INT(0),
(octeon_read_csr64(oct, CN23XX_VF_SLI_PKT_MBOX_INT(0)) &
~CN23XX_INTR_MBOX_ENB));
}
}
int cn23xx_setup_octeon_vf_device(struct octeon_device *oct)
{
struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip;
u32 rings_per_vf;
u64 reg_val;
if (octeon_map_pci_barx(oct, 0, 0))
return 1;
/* INPUT_CONTROL[RPVF] gives the VF IOq count */
reg_val = octeon_read_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(0));
oct->pf_num = (reg_val >> CN23XX_PKT_INPUT_CTL_PF_NUM_POS) &
CN23XX_PKT_INPUT_CTL_PF_NUM_MASK;
oct->vf_num = (reg_val >> CN23XX_PKT_INPUT_CTL_VF_NUM_POS) &
CN23XX_PKT_INPUT_CTL_VF_NUM_MASK;
reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS;
rings_per_vf = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK;
cn23xx->conf = oct_get_config_info(oct, LIO_23XX);
if (!cn23xx->conf) {
dev_err(&oct->pci_dev->dev, "%s No Config found for CN23XX\n",
__func__);
octeon_unmap_pci_barx(oct, 0);
return 1;
}
if (oct->sriov_info.rings_per_vf > rings_per_vf) {
dev_warn(&oct->pci_dev->dev,
"num_queues:%d greater than PF configured rings_per_vf:%d. Reducing to %d.\n",
oct->sriov_info.rings_per_vf, rings_per_vf,
rings_per_vf);
oct->sriov_info.rings_per_vf = rings_per_vf;
} else {
if (rings_per_vf > num_present_cpus()) {
dev_warn(&oct->pci_dev->dev,
"PF configured rings_per_vf:%d greater than num_cpu:%d. Using rings_per_vf:%d equal to num cpus\n",
rings_per_vf,
num_present_cpus(),
num_present_cpus());
oct->sriov_info.rings_per_vf =
num_present_cpus();
} else {
oct->sriov_info.rings_per_vf = rings_per_vf;
}
}
oct->fn_list.setup_iq_regs = cn23xx_setup_vf_iq_regs;
oct->fn_list.setup_oq_regs = cn23xx_setup_vf_oq_regs;
oct->fn_list.setup_mbox = cn23xx_setup_vf_mbox;
oct->fn_list.free_mbox = cn23xx_free_vf_mbox;
oct->fn_list.msix_interrupt_handler = cn23xx_vf_msix_interrupt_handler;
oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs;
oct->fn_list.update_iq_read_idx = cn23xx_update_read_index;
oct->fn_list.enable_interrupt = cn23xx_enable_vf_interrupt;
oct->fn_list.disable_interrupt = cn23xx_disable_vf_interrupt;
oct->fn_list.enable_io_queues = cn23xx_enable_vf_io_queues;
oct->fn_list.disable_io_queues = cn23xx_disable_vf_io_queues;
return 0;
}
EXPORT_SYMBOL_GPL(cn23xx_setup_octeon_vf_device);
@@ -0,0 +1,46 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn23xx_device.h
* \brief Host Driver: Routines that perform CN23XX specific operations.
*/
#ifndef __CN23XX_VF_DEVICE_H__
#define __CN23XX_VF_DEVICE_H__
#include "cn23xx_vf_regs.h"
/* Register address and configuration for a CN23XX devices.
* If device specific changes need to be made then add a struct to include
* device specific fields as shown in the commented section
*/
struct octeon_cn23xx_vf {
struct octeon_config *conf;
};
#define BUSY_READING_REG_VF_LOOP_COUNT 10000
#define CN23XX_MAILBOX_MSGPARAM_SIZE 6
void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct);
int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct);
int cn23xx_setup_octeon_vf_device(struct octeon_device *oct);
u32 cn23xx_vf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us);
#endif
@@ -0,0 +1,274 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn23xx_vf_regs.h
* \brief Host Driver: Register Address and Register Mask values for
* Octeon CN23XX vf functions.
*/
#ifndef __CN23XX_VF_REGS_H__
#define __CN23XX_VF_REGS_H__
#define CN23XX_CONFIG_XPANSION_BAR 0x38
#define CN23XX_CONFIG_PCIE_CAP 0x70
#define CN23XX_CONFIG_PCIE_DEVCAP 0x74
#define CN23XX_CONFIG_PCIE_DEVCTL 0x78
#define CN23XX_CONFIG_PCIE_LINKCAP 0x7C
#define CN23XX_CONFIG_PCIE_LINKCTL 0x80
#define CN23XX_CONFIG_PCIE_SLOTCAP 0x84
#define CN23XX_CONFIG_PCIE_SLOTCTL 0x88
#define CN23XX_CONFIG_PCIE_FLTMSK 0x720
/* The input jabber is used to determine the TSO max size.
* Due to H/W limitation, this needs to be reduced to 60000
* in order to use H/W TSO and avoid the WQE malformation
* PKO_BUG_24989_WQE_LEN
*/
#define CN23XX_DEFAULT_INPUT_JABBER 0xEA60 /*60000*/
/* ############## BAR0 Registers ################ */
/* Each Input Queue register is at a 16-byte Offset in BAR0 */
#define CN23XX_VF_IQ_OFFSET 0x20000
/*###################### REQUEST QUEUE #########################*/
/* 64 registers for Input Queue Instr Count - SLI_PKT_IN_DONE0_CNTS */
#define CN23XX_VF_SLI_IQ_INSTR_COUNT_START64 0x10040
/* 64 registers for Input Queues Start Addr - SLI_PKT0_INSTR_BADDR */
#define CN23XX_VF_SLI_IQ_BASE_ADDR_START64 0x10010
/* 64 registers for Input Doorbell - SLI_PKT0_INSTR_BAOFF_DBELL */
#define CN23XX_VF_SLI_IQ_DOORBELL_START 0x10020
/* 64 registers for Input Queue size - SLI_PKT0_INSTR_FIFO_RSIZE */
#define CN23XX_VF_SLI_IQ_SIZE_START 0x10030
/* 64 registers (64-bit) - ES, RO, NS, Arbitration for Input Queue Data &
* gather list fetches. SLI_PKT(0..63)_INPUT_CONTROL.
*/
#define CN23XX_VF_SLI_IQ_PKT_CONTROL_START64 0x10000
/*------- Request Queue Macros ---------*/
#define CN23XX_VF_SLI_IQ_PKT_CONTROL64(iq) \
(CN23XX_VF_SLI_IQ_PKT_CONTROL_START64 + ((iq) * CN23XX_VF_IQ_OFFSET))
#define CN23XX_VF_SLI_IQ_BASE_ADDR64(iq) \
(CN23XX_VF_SLI_IQ_BASE_ADDR_START64 + ((iq) * CN23XX_VF_IQ_OFFSET))
#define CN23XX_VF_SLI_IQ_SIZE(iq) \
(CN23XX_VF_SLI_IQ_SIZE_START + ((iq) * CN23XX_VF_IQ_OFFSET))
#define CN23XX_VF_SLI_IQ_DOORBELL(iq) \
(CN23XX_VF_SLI_IQ_DOORBELL_START + ((iq) * CN23XX_VF_IQ_OFFSET))
#define CN23XX_VF_SLI_IQ_INSTR_COUNT64(iq) \
(CN23XX_VF_SLI_IQ_INSTR_COUNT_START64 + ((iq) * CN23XX_VF_IQ_OFFSET))
/*------------------ Masks ----------------*/
#define CN23XX_PKT_INPUT_CTL_VF_NUM BIT_ULL(32)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM BIT(29)
/* Number of instructions to be read in one MAC read request.
* setting to Max value(4)
*/
#define CN23XX_PKT_INPUT_CTL_RDSIZE (3 << 25)
#define CN23XX_PKT_INPUT_CTL_IS_64B BIT(24)
#define CN23XX_PKT_INPUT_CTL_RST BIT(23)
#define CN23XX_PKT_INPUT_CTL_QUIET BIT(28)
#define CN23XX_PKT_INPUT_CTL_RING_ENB BIT(22)
#define CN23XX_PKT_INPUT_CTL_DATA_NS BIT(8)
#define CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP BIT(6)
#define CN23XX_PKT_INPUT_CTL_DATA_RO BIT(5)
#define CN23XX_PKT_INPUT_CTL_USE_CSR BIT(4)
#define CN23XX_PKT_INPUT_CTL_GATHER_NS BIT(3)
#define CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP (2)
#define CN23XX_PKT_INPUT_CTL_GATHER_RO (1)
/** Rings per Virtual Function [RO] **/
#define CN23XX_PKT_INPUT_CTL_RPVF_MASK (0x3F)
#define CN23XX_PKT_INPUT_CTL_RPVF_POS (48)
/* These bits[47:44][RO] give the Physical function number info within the MAC*/
#define CN23XX_PKT_INPUT_CTL_PF_NUM_MASK (0x7)
#define CN23XX_PKT_INPUT_CTL_PF_NUM_POS (45)
/** These bits[43:32][RO] give the virtual function number info within the PF*/
#define CN23XX_PKT_INPUT_CTL_VF_NUM_MASK (0x1FFF)
#define CN23XX_PKT_INPUT_CTL_VF_NUM_POS (32)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM_MASK (0x3)
#define CN23XX_PKT_INPUT_CTL_MAC_NUM_POS (29)
#define CN23XX_PKT_IN_DONE_WMARK_MASK (0xFFFFULL)
#define CN23XX_PKT_IN_DONE_WMARK_BIT_POS (32)
#define CN23XX_PKT_IN_DONE_CNT_MASK (0x00000000FFFFFFFFULL)
#ifdef __LITTLE_ENDIAN_BITFIELD
#define CN23XX_PKT_INPUT_CTL_MASK \
(CN23XX_PKT_INPUT_CTL_RDSIZE \
| CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP \
| CN23XX_PKT_INPUT_CTL_USE_CSR)
#else
#define CN23XX_PKT_INPUT_CTL_MASK \
(CN23XX_PKT_INPUT_CTL_RDSIZE \
| CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP \
| CN23XX_PKT_INPUT_CTL_USE_CSR \
| CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP)
#endif
/** Masks for SLI_PKT_IN_DONE(0..63)_CNTS Register */
#define CN23XX_IN_DONE_CNTS_PI_INT BIT_ULL(62)
#define CN23XX_IN_DONE_CNTS_CINT_ENB BIT_ULL(48)
/*############################ OUTPUT QUEUE #########################*/
/* 64 registers for Output queue control - SLI_PKT(0..63)_OUTPUT_CONTROL */
#define CN23XX_VF_SLI_OQ_PKT_CONTROL_START 0x10050
/* 64 registers for Output queue buffer and info size - SLI_PKT0_OUT_SIZE */
#define CN23XX_VF_SLI_OQ0_BUFF_INFO_SIZE 0x10060
/* 64 registers for Output Queue Start Addr - SLI_PKT0_SLIST_BADDR */
#define CN23XX_VF_SLI_OQ_BASE_ADDR_START64 0x10070
/* 64 registers for Output Queue Packet Credits - SLI_PKT0_SLIST_BAOFF_DBELL */
#define CN23XX_VF_SLI_OQ_PKT_CREDITS_START 0x10080
/* 64 registers for Output Queue size - SLI_PKT0_SLIST_FIFO_RSIZE */
#define CN23XX_VF_SLI_OQ_SIZE_START 0x10090
/* 64 registers for Output Queue Packet Count - SLI_PKT0_CNTS */
#define CN23XX_VF_SLI_OQ_PKT_SENT_START 0x100B0
/* 64 registers for Output Queue INT Levels - SLI_PKT0_INT_LEVELS */
#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 0x100A0
/* Each Output Queue register is at a 16-byte Offset in BAR0 */
#define CN23XX_VF_OQ_OFFSET 0x20000
/*------- Output Queue Macros ---------*/
#define CN23XX_VF_SLI_OQ_PKT_CONTROL(oq) \
(CN23XX_VF_SLI_OQ_PKT_CONTROL_START + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_BASE_ADDR64(oq) \
(CN23XX_VF_SLI_OQ_BASE_ADDR_START64 + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_SIZE(oq) \
(CN23XX_VF_SLI_OQ_SIZE_START + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_BUFF_INFO_SIZE(oq) \
(CN23XX_VF_SLI_OQ0_BUFF_INFO_SIZE + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_PKTS_SENT(oq) \
(CN23XX_VF_SLI_OQ_PKT_SENT_START + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_PKTS_CREDIT(oq) \
(CN23XX_VF_SLI_OQ_PKT_CREDITS_START + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(oq) \
(CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + ((oq) * CN23XX_VF_OQ_OFFSET))
/* Macro's for accessing CNT and TIME separately from INT_LEVELS */
#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_CNT(oq) \
(CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + ((oq) * CN23XX_VF_OQ_OFFSET))
#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_TIME(oq) \
(CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + \
((oq) * CN23XX_VF_OQ_OFFSET) + 4)
/*------------------ Masks ----------------*/
#define CN23XX_PKT_OUTPUT_CTL_TENB BIT(13)
#define CN23XX_PKT_OUTPUT_CTL_CENB BIT(12)
#define CN23XX_PKT_OUTPUT_CTL_IPTR BIT(11)
#define CN23XX_PKT_OUTPUT_CTL_ES BIT(9)
#define CN23XX_PKT_OUTPUT_CTL_NSR BIT(8)
#define CN23XX_PKT_OUTPUT_CTL_ROR BIT(7)
#define CN23XX_PKT_OUTPUT_CTL_DPTR BIT(6)
#define CN23XX_PKT_OUTPUT_CTL_BMODE BIT(5)
#define CN23XX_PKT_OUTPUT_CTL_ES_P BIT(3)
#define CN23XX_PKT_OUTPUT_CTL_NSR_P BIT(2)
#define CN23XX_PKT_OUTPUT_CTL_ROR_P BIT(1)
#define CN23XX_PKT_OUTPUT_CTL_RING_ENB BIT(0)
/*######################### Mailbox Reg Macros ########################*/
#define CN23XX_VF_SLI_PKT_MBOX_INT_START 0x10210
#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START 0x10200
#define CN23XX_SLI_MBOX_OFFSET 0x20000
#define CN23XX_SLI_MBOX_SIG_IDX_OFFSET 0x8
#define CN23XX_VF_SLI_PKT_MBOX_INT(q) \
(CN23XX_VF_SLI_PKT_MBOX_INT_START + ((q) * CN23XX_SLI_MBOX_OFFSET))
#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG(q, idx) \
(CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START + \
((q) * CN23XX_SLI_MBOX_OFFSET + \
(idx) * CN23XX_SLI_MBOX_SIG_IDX_OFFSET))
/*######################## INTERRUPTS #########################*/
#define CN23XX_VF_SLI_INT_SUM_START 0x100D0
#define CN23XX_VF_SLI_INT_SUM(q) \
(CN23XX_VF_SLI_INT_SUM_START + ((q) * CN23XX_VF_IQ_OFFSET))
/*------------------ Interrupt Masks ----------------*/
#define CN23XX_INTR_PO_INT BIT_ULL(63)
#define CN23XX_INTR_PI_INT BIT_ULL(62)
#define CN23XX_INTR_MBOX_INT BIT_ULL(61)
#define CN23XX_INTR_RESEND BIT_ULL(60)
#define CN23XX_INTR_CINT_ENB BIT_ULL(48)
#define CN23XX_INTR_MBOX_ENB BIT(0)
/*############################ MIO #########################*/
#define CN23XX_MIO_PTP_CLOCK_CFG 0x0001070000000f00ULL
#define CN23XX_MIO_PTP_CLOCK_LO 0x0001070000000f08ULL
#define CN23XX_MIO_PTP_CLOCK_HI 0x0001070000000f10ULL
#define CN23XX_MIO_PTP_CLOCK_COMP 0x0001070000000f18ULL
#define CN23XX_MIO_PTP_TIMESTAMP 0x0001070000000f20ULL
#define CN23XX_MIO_PTP_EVT_CNT 0x0001070000000f28ULL
#define CN23XX_MIO_PTP_CKOUT_THRESH_LO 0x0001070000000f30ULL
#define CN23XX_MIO_PTP_CKOUT_THRESH_HI 0x0001070000000f38ULL
#define CN23XX_MIO_PTP_CKOUT_HI_INCR 0x0001070000000f40ULL
#define CN23XX_MIO_PTP_CKOUT_LO_INCR 0x0001070000000f48ULL
#define CN23XX_MIO_PTP_PPS_THRESH_LO 0x0001070000000f50ULL
#define CN23XX_MIO_PTP_PPS_THRESH_HI 0x0001070000000f58ULL
#define CN23XX_MIO_PTP_PPS_HI_INCR 0x0001070000000f60ULL
#define CN23XX_MIO_PTP_PPS_LO_INCR 0x0001070000000f68ULL
/*############################ RST #########################*/
#define CN23XX_RST_BOOT 0x0001180006001600ULL
/*######################## MSIX TABLE #########################*/
#define CN23XX_MSIX_TABLE_ADDR_START 0x0
#define CN23XX_MSIX_TABLE_DATA_START 0x8
#define CN23XX_MSIX_TABLE_SIZE 0x10
#define CN23XX_MSIX_TABLE_ENTRIES 0x41
#define CN23XX_MSIX_ENTRY_VECTOR_CTL BIT_ULL(32)
#define CN23XX_MSIX_TABLE_ADDR(idx) \
(CN23XX_MSIX_TABLE_ADDR_START + ((idx) * CN23XX_MSIX_TABLE_SIZE))
#define CN23XX_MSIX_TABLE_DATA(idx) \
(CN23XX_MSIX_TABLE_DATA_START + ((idx) * CN23XX_MSIX_TABLE_SIZE))
#endif
@@ -0,0 +1,738 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
#include "cn66xx_regs.h"
#include "cn66xx_device.h"
int lio_cn6xxx_soft_reset(struct octeon_device *oct)
{
octeon_write_csr64(oct, CN6XXX_WIN_WR_MASK_REG, 0xFF);
dev_dbg(&oct->pci_dev->dev, "BIST enabled for soft reset\n");
lio_pci_writeq(oct, 1, CN6XXX_CIU_SOFT_BIST);
octeon_write_csr64(oct, CN6XXX_SLI_SCRATCH1, 0x1234ULL);
lio_pci_readq(oct, CN6XXX_CIU_SOFT_RST);
lio_pci_writeq(oct, 1, CN6XXX_CIU_SOFT_RST);
/* Wait for 10ms as Octeon resets. */
mdelay(100);
if (octeon_read_csr64(oct, CN6XXX_SLI_SCRATCH1)) {
dev_err(&oct->pci_dev->dev, "Soft reset failed\n");
return 1;
}
dev_dbg(&oct->pci_dev->dev, "Reset completed\n");
octeon_write_csr64(oct, CN6XXX_WIN_WR_MASK_REG, 0xFF);
return 0;
}
void lio_cn6xxx_enable_error_reporting(struct octeon_device *oct)
{
u32 val;
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, &val);
if (val & 0x000c0000) {
dev_err(&oct->pci_dev->dev, "PCI-E Link error detected: 0x%08x\n",
val & 0x000c0000);
}
val |= 0xf; /* Enable Link error reporting */
dev_dbg(&oct->pci_dev->dev, "Enabling PCI-E error reporting..\n");
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, val);
}
void lio_cn6xxx_setup_pcie_mps(struct octeon_device *oct,
enum octeon_pcie_mps mps)
{
u32 val;
u64 r64;
/* Read config register for MPS */
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, &val);
if (mps == PCIE_MPS_DEFAULT) {
mps = ((val & (0x7 << 5)) >> 5);
} else {
val &= ~(0x7 << 5); /* Turn off any MPS bits */
val |= (mps << 5); /* Set MPS */
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, val);
}
/* Set MPS in DPI_SLI_PRT0_CFG to the same value. */
r64 = lio_pci_readq(oct, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
r64 |= (mps << 4);
lio_pci_writeq(oct, r64, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
}
void lio_cn6xxx_setup_pcie_mrrs(struct octeon_device *oct,
enum octeon_pcie_mrrs mrrs)
{
u32 val;
u64 r64;
/* Read config register for MRRS */
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, &val);
if (mrrs == PCIE_MRRS_DEFAULT) {
mrrs = ((val & (0x7 << 12)) >> 12);
} else {
val &= ~(0x7 << 12); /* Turn off any MRRS bits */
val |= (mrrs << 12); /* Set MRRS */
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, val);
}
/* Set MRRS in SLI_S2M_PORT0_CTL to the same value. */
r64 = octeon_read_csr64(oct, CN6XXX_SLI_S2M_PORTX_CTL(oct->pcie_port));
r64 |= mrrs;
octeon_write_csr64(oct, CN6XXX_SLI_S2M_PORTX_CTL(oct->pcie_port), r64);
/* Set MRRS in DPI_SLI_PRT0_CFG to the same value. */
r64 = lio_pci_readq(oct, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
r64 |= mrrs;
lio_pci_writeq(oct, r64, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
}
u32 lio_cn6xxx_coprocessor_clock(struct octeon_device *oct)
{
/* Bits 29:24 of MIO_RST_BOOT holds the ref. clock multiplier
* for SLI.
*/
return ((lio_pci_readq(oct, CN6XXX_MIO_RST_BOOT) >> 24) & 0x3f) * 50;
}
u32 lio_cn6xxx_get_oq_ticks(struct octeon_device *oct,
u32 time_intr_in_us)
{
/* This gives the SLI clock per microsec */
u32 oqticks_per_us = lio_cn6xxx_coprocessor_clock(oct);
/* core clock per us / oq ticks will be fractional. TO avoid that
* we use the method below.
*/
/* This gives the clock cycles per millisecond */
oqticks_per_us *= 1000;
/* This gives the oq ticks (1024 core clock cycles) per millisecond */
oqticks_per_us /= 1024;
/* time_intr is in microseconds. The next 2 steps gives the oq ticks
* corressponding to time_intr.
*/
oqticks_per_us *= time_intr_in_us;
oqticks_per_us /= 1000;
return oqticks_per_us;
}
void lio_cn6xxx_setup_global_input_regs(struct octeon_device *oct)
{
/* Select Round-Robin Arb, ES, RO, NS for Input Queues */
octeon_write_csr(oct, CN6XXX_SLI_PKT_INPUT_CONTROL,
CN6XXX_INPUT_CTL_MASK);
/* Instruction Read Size - Max 4 instructions per PCIE Read */
octeon_write_csr64(oct, CN6XXX_SLI_PKT_INSTR_RD_SIZE,
0xFFFFFFFFFFFFFFFFULL);
/* Select PCIE Port for all Input rings. */
octeon_write_csr64(oct, CN6XXX_SLI_IN_PCIE_PORT,
(oct->pcie_port * 0x5555555555555555ULL));
}
static void lio_cn66xx_setup_pkt_ctl_regs(struct octeon_device *oct)
{
u64 pktctl;
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
pktctl = octeon_read_csr64(oct, CN6XXX_SLI_PKT_CTL);
/* 66XX SPECIFIC */
if (CFG_GET_OQ_MAX_Q(cn6xxx->conf) <= 4)
/* Disable RING_EN if only upto 4 rings are used. */
pktctl &= ~(1 << 4);
else
pktctl |= (1 << 4);
if (CFG_GET_IS_SLI_BP_ON(cn6xxx->conf))
pktctl |= 0xF;
else
/* Disable per-port backpressure. */
pktctl &= ~0xF;
octeon_write_csr64(oct, CN6XXX_SLI_PKT_CTL, pktctl);
}
void lio_cn6xxx_setup_global_output_regs(struct octeon_device *oct)
{
u32 time_threshold;
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
/* / Select PCI-E Port for all Output queues */
octeon_write_csr64(oct, CN6XXX_SLI_PKT_PCIE_PORT64,
(oct->pcie_port * 0x5555555555555555ULL));
if (CFG_GET_IS_SLI_BP_ON(cn6xxx->conf)) {
octeon_write_csr64(oct, CN6XXX_SLI_OQ_WMARK, 32);
} else {
/* / Set Output queue watermark to 0 to disable backpressure */
octeon_write_csr64(oct, CN6XXX_SLI_OQ_WMARK, 0);
}
/* / Select Packet count instead of bytes for SLI_PKTi_CNTS[CNT] */
octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_BMODE, 0);
/* Select ES, RO, NS setting from register for Output Queue Packet
* Address
*/
octeon_write_csr(oct, CN6XXX_SLI_PKT_DPADDR, 0xFFFFFFFF);
/* No Relaxed Ordering, No Snoop, 64-bit swap for Output
* Queue ScatterList
*/
octeon_write_csr(oct, CN6XXX_SLI_PKT_SLIST_ROR, 0);
octeon_write_csr(oct, CN6XXX_SLI_PKT_SLIST_NS, 0);
/* / ENDIAN_SPECIFIC CHANGES - 0 works for LE. */
#ifdef __BIG_ENDIAN_BITFIELD
octeon_write_csr64(oct, CN6XXX_SLI_PKT_SLIST_ES64,
0x5555555555555555ULL);
#else
octeon_write_csr64(oct, CN6XXX_SLI_PKT_SLIST_ES64, 0ULL);
#endif
/* / No Relaxed Ordering, No Snoop, 64-bit swap for Output Queue Data */
octeon_write_csr(oct, CN6XXX_SLI_PKT_DATA_OUT_ROR, 0);
octeon_write_csr(oct, CN6XXX_SLI_PKT_DATA_OUT_NS, 0);
octeon_write_csr64(oct, CN6XXX_SLI_PKT_DATA_OUT_ES64,
0x5555555555555555ULL);
/* / Set up interrupt packet and time threshold */
octeon_write_csr(oct, CN6XXX_SLI_OQ_INT_LEVEL_PKTS,
(u32)CFG_GET_OQ_INTR_PKT(cn6xxx->conf));
time_threshold =
lio_cn6xxx_get_oq_ticks(oct, (u32)
CFG_GET_OQ_INTR_TIME(cn6xxx->conf));
octeon_write_csr(oct, CN6XXX_SLI_OQ_INT_LEVEL_TIME, time_threshold);
}
static int lio_cn6xxx_setup_device_regs(struct octeon_device *oct)
{
lio_cn6xxx_setup_pcie_mps(oct, PCIE_MPS_DEFAULT);
lio_cn6xxx_setup_pcie_mrrs(oct, PCIE_MRRS_512B);
lio_cn6xxx_enable_error_reporting(oct);
lio_cn6xxx_setup_global_input_regs(oct);
lio_cn66xx_setup_pkt_ctl_regs(oct);
lio_cn6xxx_setup_global_output_regs(oct);
/* Default error timeout value should be 0x200000 to avoid host hang
* when reads invalid register
*/
octeon_write_csr64(oct, CN6XXX_SLI_WINDOW_CTL, 0x200000ULL);
return 0;
}
void lio_cn6xxx_setup_iq_regs(struct octeon_device *oct, u32 iq_no)
{
struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
octeon_write_csr64(oct, CN6XXX_SLI_IQ_PKT_INSTR_HDR64(iq_no), 0);
/* Write the start of the input queue's ring and its size */
octeon_write_csr64(oct, CN6XXX_SLI_IQ_BASE_ADDR64(iq_no),
iq->base_addr_dma);
octeon_write_csr(oct, CN6XXX_SLI_IQ_SIZE(iq_no), iq->max_count);
/* Remember the doorbell & instruction count register addr for this
* queue
*/
iq->doorbell_reg = oct->mmio[0].hw_addr + CN6XXX_SLI_IQ_DOORBELL(iq_no);
iq->inst_cnt_reg = oct->mmio[0].hw_addr
+ CN6XXX_SLI_IQ_INSTR_COUNT(iq_no);
dev_dbg(&oct->pci_dev->dev, "InstQ[%d]:dbell reg @ 0x%p instcnt_reg @ 0x%p\n",
iq_no, iq->doorbell_reg, iq->inst_cnt_reg);
/* Store the current instruction counter
* (used in flush_iq calculation)
*/
iq->reset_instr_cnt = readl(iq->inst_cnt_reg);
}
static void lio_cn66xx_setup_iq_regs(struct octeon_device *oct, u32 iq_no)
{
lio_cn6xxx_setup_iq_regs(oct, iq_no);
/* Backpressure for this queue - WMARK set to all F's. This effectively
* disables the backpressure mechanism.
*/
octeon_write_csr64(oct, CN66XX_SLI_IQ_BP64(iq_no),
(0xFFFFFFFFULL << 32));
}
void lio_cn6xxx_setup_oq_regs(struct octeon_device *oct, u32 oq_no)
{
u32 intr;
struct octeon_droq *droq = oct->droq[oq_no];
octeon_write_csr64(oct, CN6XXX_SLI_OQ_BASE_ADDR64(oq_no),
droq->desc_ring_dma);
octeon_write_csr(oct, CN6XXX_SLI_OQ_SIZE(oq_no), droq->max_count);
octeon_write_csr(oct, CN6XXX_SLI_OQ_BUFF_INFO_SIZE(oq_no),
droq->buffer_size);
/* Get the mapped address of the pkt_sent and pkts_credit regs */
droq->pkts_sent_reg =
oct->mmio[0].hw_addr + CN6XXX_SLI_OQ_PKTS_SENT(oq_no);
droq->pkts_credit_reg =
oct->mmio[0].hw_addr + CN6XXX_SLI_OQ_PKTS_CREDIT(oq_no);
/* Enable this output queue to generate Packet Timer Interrupt */
intr = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB);
intr |= (1 << oq_no);
octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB, intr);
/* Enable this output queue to generate Packet Timer Interrupt */
intr = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB);
intr |= (1 << oq_no);
octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB, intr);
}
int lio_cn6xxx_enable_io_queues(struct octeon_device *oct)
{
u32 mask;
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_INSTR_SIZE);
mask |= oct->io_qmask.iq64B;
octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_SIZE, mask);
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB);
mask |= oct->io_qmask.iq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB, mask);
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_OUT_ENB);
mask |= oct->io_qmask.oq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_ENB, mask);
return 0;
}
void lio_cn6xxx_disable_io_queues(struct octeon_device *oct)
{
int i;
u32 mask, loop = HZ;
u32 d32;
/* Reset the Enable bits for Input Queues. */
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB);
mask ^= oct->io_qmask.iq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB, mask);
/* Wait until hardware indicates that the queues are out of reset. */
mask = (u32)oct->io_qmask.iq;
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_IQ);
while (((d32 & mask) != mask) && loop--) {
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_IQ);
schedule_timeout_uninterruptible(1);
}
/* Reset the doorbell register for each Input queue. */
for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
if (!(oct->io_qmask.iq & BIT_ULL(i)))
continue;
octeon_write_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i), 0xFFFFFFFF);
d32 = octeon_read_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i));
}
/* Reset the Enable bits for Output Queues. */
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_OUT_ENB);
mask ^= oct->io_qmask.oq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_ENB, mask);
/* Wait until hardware indicates that the queues are out of reset. */
loop = HZ;
mask = (u32)oct->io_qmask.oq;
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_OQ);
while (((d32 & mask) != mask) && loop--) {
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_OQ);
schedule_timeout_uninterruptible(1);
}
;
/* Reset the doorbell register for each Output queue. */
for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) {
if (!(oct->io_qmask.oq & BIT_ULL(i)))
continue;
octeon_write_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i), 0xFFFFFFFF);
d32 = octeon_read_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i));
d32 = octeon_read_csr(oct, CN6XXX_SLI_OQ_PKTS_SENT(i));
octeon_write_csr(oct, CN6XXX_SLI_OQ_PKTS_SENT(i), d32);
}
d32 = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT);
if (d32)
octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT, d32);
d32 = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT);
if (d32)
octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT, d32);
}
void
lio_cn6xxx_bar1_idx_setup(struct octeon_device *oct,
u64 core_addr,
u32 idx,
int valid)
{
u64 bar1;
if (valid == 0) {
bar1 = lio_pci_readq(oct, CN6XXX_BAR1_REG(idx, oct->pcie_port));
lio_pci_writeq(oct, (bar1 & 0xFFFFFFFEULL),
CN6XXX_BAR1_REG(idx, oct->pcie_port));
bar1 = lio_pci_readq(oct, CN6XXX_BAR1_REG(idx, oct->pcie_port));
return;
}
/* Bits 17:4 of the PCI_BAR1_INDEXx stores bits 35:22 of
* the Core Addr
*/
lio_pci_writeq(oct, (((core_addr >> 22) << 4) | PCI_BAR1_MASK),
CN6XXX_BAR1_REG(idx, oct->pcie_port));
bar1 = lio_pci_readq(oct, CN6XXX_BAR1_REG(idx, oct->pcie_port));
}
void lio_cn6xxx_bar1_idx_write(struct octeon_device *oct,
u32 idx,
u32 mask)
{
lio_pci_writeq(oct, mask, CN6XXX_BAR1_REG(idx, oct->pcie_port));
}
u32 lio_cn6xxx_bar1_idx_read(struct octeon_device *oct, u32 idx)
{
return (u32)lio_pci_readq(oct, CN6XXX_BAR1_REG(idx, oct->pcie_port));
}
u32
lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq)
{
u32 new_idx = readl(iq->inst_cnt_reg);
/* The new instr cnt reg is a 32-bit counter that can roll over. We have
* noted the counter's initial value at init time into
* reset_instr_cnt
*/
if (iq->reset_instr_cnt < new_idx)
new_idx -= iq->reset_instr_cnt;
else
new_idx += (0xffffffff - iq->reset_instr_cnt) + 1;
/* Modulo of the new index with the IQ size will give us
* the new index.
*/
new_idx %= iq->max_count;
return new_idx;
}
void lio_cn6xxx_enable_interrupt(struct octeon_device *oct,
u8 unused __attribute__((unused)))
{
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
u64 mask = cn6xxx->intr_mask64 | CN6XXX_INTR_DMA0_FORCE;
/* Enable Interrupt */
writeq(mask, cn6xxx->intr_enb_reg64);
}
void lio_cn6xxx_disable_interrupt(struct octeon_device *oct,
u8 unused __attribute__((unused)))
{
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
/* Disable Interrupts */
writeq(0, cn6xxx->intr_enb_reg64);
}
static void lio_cn6xxx_get_pcie_qlmport(struct octeon_device *oct)
{
/* CN63xx Pass2 and newer parts implements the SLI_MAC_NUMBER register
* to determine the PCIE port #
*/
oct->pcie_port = octeon_read_csr(oct, CN6XXX_SLI_MAC_NUMBER) & 0xff;
dev_dbg(&oct->pci_dev->dev, "Using PCIE Port %d\n", oct->pcie_port);
}
static void
lio_cn6xxx_process_pcie_error_intr(struct octeon_device *oct, u64 intr64)
{
dev_err(&oct->pci_dev->dev, "Error Intr: 0x%016llx\n",
CVM_CAST64(intr64));
}
static int lio_cn6xxx_process_droq_intr_regs(struct octeon_device *oct)
{
struct octeon_droq *droq;
int oq_no;
u32 pkt_count, droq_time_mask, droq_mask, droq_int_enb;
u32 droq_cnt_enb, droq_cnt_mask;
droq_cnt_enb = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB);
droq_cnt_mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT);
droq_mask = droq_cnt_mask & droq_cnt_enb;
droq_time_mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT);
droq_int_enb = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB);
droq_mask |= (droq_time_mask & droq_int_enb);
droq_mask &= oct->io_qmask.oq;
oct->droq_intr = 0;
for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); oq_no++) {
if (!(droq_mask & BIT_ULL(oq_no)))
continue;
droq = oct->droq[oq_no];
pkt_count = octeon_droq_check_hw_for_pkts(droq);
if (pkt_count) {
oct->droq_intr |= BIT_ULL(oq_no);
if (droq->ops.poll_mode) {
u32 value;
u32 reg;
struct octeon_cn6xxx *cn6xxx =
(struct octeon_cn6xxx *)oct->chip;
/* disable interrupts for this droq */
spin_lock
(&cn6xxx->lock_for_droq_int_enb_reg);
reg = CN6XXX_SLI_PKT_TIME_INT_ENB;
value = octeon_read_csr(oct, reg);
value &= ~(1 << oq_no);
octeon_write_csr(oct, reg, value);
reg = CN6XXX_SLI_PKT_CNT_INT_ENB;
value = octeon_read_csr(oct, reg);
value &= ~(1 << oq_no);
octeon_write_csr(oct, reg, value);
spin_unlock(&cn6xxx->lock_for_droq_int_enb_reg);
}
}
}
droq_time_mask &= oct->io_qmask.oq;
droq_cnt_mask &= oct->io_qmask.oq;
/* Reset the PKT_CNT/TIME_INT registers. */
if (droq_time_mask)
octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT, droq_time_mask);
if (droq_cnt_mask) /* reset PKT_CNT register:66xx */
octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT, droq_cnt_mask);
return 0;
}
irqreturn_t lio_cn6xxx_process_interrupt_regs(void *dev)
{
struct octeon_device *oct = (struct octeon_device *)dev;
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
u64 intr64;
intr64 = readq(cn6xxx->intr_sum_reg64);
/* If our device has interrupted, then proceed.
* Also check for all f's if interrupt was triggered on an error
* and the PCI read fails.
*/
if (!intr64 || (intr64 == 0xFFFFFFFFFFFFFFFFULL))
return IRQ_NONE;
oct->int_status = 0;
if (intr64 & CN6XXX_INTR_ERR)
lio_cn6xxx_process_pcie_error_intr(oct, intr64);
if (intr64 & CN6XXX_INTR_PKT_DATA) {
lio_cn6xxx_process_droq_intr_regs(oct);
oct->int_status |= OCT_DEV_INTR_PKT_DATA;
}
if (intr64 & CN6XXX_INTR_DMA0_FORCE)
oct->int_status |= OCT_DEV_INTR_DMA0_FORCE;
if (intr64 & CN6XXX_INTR_DMA1_FORCE)
oct->int_status |= OCT_DEV_INTR_DMA1_FORCE;
/* Clear the current interrupts */
writeq(intr64, cn6xxx->intr_sum_reg64);
return IRQ_HANDLED;
}
void lio_cn6xxx_setup_reg_address(struct octeon_device *oct,
void *chip,
struct octeon_reg_list *reg_list)
{
u8 __iomem *bar0_pciaddr = oct->mmio[0].hw_addr;
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)chip;
reg_list->pci_win_wr_addr_hi =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_ADDR_HI);
reg_list->pci_win_wr_addr_lo =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_ADDR_LO);
reg_list->pci_win_wr_addr =
(u64 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_ADDR64);
reg_list->pci_win_rd_addr_hi =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_ADDR_HI);
reg_list->pci_win_rd_addr_lo =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_ADDR_LO);
reg_list->pci_win_rd_addr =
(u64 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_ADDR64);
reg_list->pci_win_wr_data_hi =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_DATA_HI);
reg_list->pci_win_wr_data_lo =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_DATA_LO);
reg_list->pci_win_wr_data =
(u64 __iomem *)(bar0_pciaddr + CN6XXX_WIN_WR_DATA64);
reg_list->pci_win_rd_data_hi =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_DATA_HI);
reg_list->pci_win_rd_data_lo =
(u32 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_DATA_LO);
reg_list->pci_win_rd_data =
(u64 __iomem *)(bar0_pciaddr + CN6XXX_WIN_RD_DATA64);
lio_cn6xxx_get_pcie_qlmport(oct);
cn6xxx->intr_sum_reg64 = bar0_pciaddr + CN6XXX_SLI_INT_SUM64;
cn6xxx->intr_mask64 = CN6XXX_INTR_MASK;
cn6xxx->intr_enb_reg64 =
bar0_pciaddr + CN6XXX_SLI_INT_ENB64(oct->pcie_port);
}
int lio_setup_cn66xx_octeon_device(struct octeon_device *oct)
{
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
if (octeon_map_pci_barx(oct, 0, 0))
return 1;
if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) {
dev_err(&oct->pci_dev->dev, "%s CN66XX BAR1 map failed\n",
__func__);
octeon_unmap_pci_barx(oct, 0);
return 1;
}
spin_lock_init(&cn6xxx->lock_for_droq_int_enb_reg);
oct->fn_list.setup_iq_regs = lio_cn66xx_setup_iq_regs;
oct->fn_list.setup_oq_regs = lio_cn6xxx_setup_oq_regs;
oct->fn_list.soft_reset = lio_cn6xxx_soft_reset;
oct->fn_list.setup_device_regs = lio_cn6xxx_setup_device_regs;
oct->fn_list.update_iq_read_idx = lio_cn6xxx_update_read_index;
oct->fn_list.bar1_idx_setup = lio_cn6xxx_bar1_idx_setup;
oct->fn_list.bar1_idx_write = lio_cn6xxx_bar1_idx_write;
oct->fn_list.bar1_idx_read = lio_cn6xxx_bar1_idx_read;
oct->fn_list.process_interrupt_regs = lio_cn6xxx_process_interrupt_regs;
oct->fn_list.enable_interrupt = lio_cn6xxx_enable_interrupt;
oct->fn_list.disable_interrupt = lio_cn6xxx_disable_interrupt;
oct->fn_list.enable_io_queues = lio_cn6xxx_enable_io_queues;
oct->fn_list.disable_io_queues = lio_cn6xxx_disable_io_queues;
lio_cn6xxx_setup_reg_address(oct, oct->chip, &oct->reg_list);
cn6xxx->conf = (struct octeon_config *)
oct_get_config_info(oct, LIO_210SV);
if (!cn6xxx->conf) {
dev_err(&oct->pci_dev->dev, "%s No Config found for CN66XX\n",
__func__);
octeon_unmap_pci_barx(oct, 0);
octeon_unmap_pci_barx(oct, 1);
return 1;
}
oct->coproc_clock_rate = 1000000ULL * lio_cn6xxx_coprocessor_clock(oct);
return 0;
}
EXPORT_SYMBOL_GPL(lio_setup_cn66xx_octeon_device);
int lio_validate_cn6xxx_config_info(struct octeon_device *oct,
struct octeon_config *conf6xxx)
{
if (CFG_GET_IQ_MAX_Q(conf6xxx) > CN6XXX_MAX_INPUT_QUEUES) {
dev_err(&oct->pci_dev->dev, "%s: Num IQ (%d) exceeds Max (%d)\n",
__func__, CFG_GET_IQ_MAX_Q(conf6xxx),
CN6XXX_MAX_INPUT_QUEUES);
return 1;
}
if (CFG_GET_OQ_MAX_Q(conf6xxx) > CN6XXX_MAX_OUTPUT_QUEUES) {
dev_err(&oct->pci_dev->dev, "%s: Num OQ (%d) exceeds Max (%d)\n",
__func__, CFG_GET_OQ_MAX_Q(conf6xxx),
CN6XXX_MAX_OUTPUT_QUEUES);
return 1;
}
if (CFG_GET_IQ_INSTR_TYPE(conf6xxx) != OCTEON_32BYTE_INSTR &&
CFG_GET_IQ_INSTR_TYPE(conf6xxx) != OCTEON_64BYTE_INSTR) {
dev_err(&oct->pci_dev->dev, "%s: Invalid instr type for IQ\n",
__func__);
return 1;
}
if (!CFG_GET_OQ_REFILL_THRESHOLD(conf6xxx)) {
dev_err(&oct->pci_dev->dev, "%s: Invalid parameter for OQ\n",
__func__);
return 1;
}
if (!(CFG_GET_OQ_INTR_TIME(conf6xxx))) {
dev_err(&oct->pci_dev->dev, "%s: No Time Interrupt for OQ\n",
__func__);
return 1;
}
return 0;
}
@@ -0,0 +1,97 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn66xx_device.h
* \brief Host Driver: Routines that perform CN66XX specific operations.
*/
#ifndef __CN66XX_DEVICE_H__
#define __CN66XX_DEVICE_H__
/* Register address and configuration for a CN6XXX devices.
* If device specific changes need to be made then add a struct to include
* device specific fields as shown in the commented section
*/
struct octeon_cn6xxx {
/** PCI interrupt summary register */
u8 __iomem *intr_sum_reg64;
/** PCI interrupt enable register */
u8 __iomem *intr_enb_reg64;
/** The PCI interrupt mask used by interrupt handler */
u64 intr_mask64;
struct octeon_config *conf;
/* Example additional fields - not used currently
* struct {
* }cn6xyz;
*/
/* For the purpose of atomic access to interrupt enable reg */
spinlock_t lock_for_droq_int_enb_reg;
};
enum octeon_pcie_mps {
PCIE_MPS_DEFAULT = -1, /* Use the default setup by BIOS */
PCIE_MPS_128B = 0,
PCIE_MPS_256B = 1
};
enum octeon_pcie_mrrs {
PCIE_MRRS_DEFAULT = -1, /* Use the default setup by BIOS */
PCIE_MRRS_128B = 0,
PCIE_MRRS_256B = 1,
PCIE_MRRS_512B = 2,
PCIE_MRRS_1024B = 3,
PCIE_MRRS_2048B = 4,
PCIE_MRRS_4096B = 5
};
/* Common functions for 66xx and 68xx */
int lio_cn6xxx_soft_reset(struct octeon_device *oct);
void lio_cn6xxx_enable_error_reporting(struct octeon_device *oct);
void lio_cn6xxx_setup_pcie_mps(struct octeon_device *oct,
enum octeon_pcie_mps mps);
void lio_cn6xxx_setup_pcie_mrrs(struct octeon_device *oct,
enum octeon_pcie_mrrs mrrs);
void lio_cn6xxx_setup_global_input_regs(struct octeon_device *oct);
void lio_cn6xxx_setup_global_output_regs(struct octeon_device *oct);
void lio_cn6xxx_setup_iq_regs(struct octeon_device *oct, u32 iq_no);
void lio_cn6xxx_setup_oq_regs(struct octeon_device *oct, u32 oq_no);
int lio_cn6xxx_enable_io_queues(struct octeon_device *oct);
void lio_cn6xxx_disable_io_queues(struct octeon_device *oct);
irqreturn_t lio_cn6xxx_process_interrupt_regs(void *dev);
void lio_cn6xxx_bar1_idx_setup(struct octeon_device *oct, u64 core_addr,
u32 idx, int valid);
void lio_cn6xxx_bar1_idx_write(struct octeon_device *oct, u32 idx, u32 mask);
u32 lio_cn6xxx_bar1_idx_read(struct octeon_device *oct, u32 idx);
u32
lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq);
void lio_cn6xxx_enable_interrupt(struct octeon_device *oct, u8 unused);
void lio_cn6xxx_disable_interrupt(struct octeon_device *oct, u8 unused);
void lio_cn6xxx_setup_reg_address(struct octeon_device *oct, void *chip,
struct octeon_reg_list *reg_list);
u32 lio_cn6xxx_coprocessor_clock(struct octeon_device *oct);
u32 lio_cn6xxx_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us);
int lio_setup_cn66xx_octeon_device(struct octeon_device *oct);
int lio_validate_cn6xxx_config_info(struct octeon_device *oct,
struct octeon_config *conf6xxx);
#endif
@@ -0,0 +1,530 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn66xx_regs.h
* \brief Host Driver: Register Address and Register Mask values for
* Octeon CN66XX devices.
*/
#ifndef __CN66XX_REGS_H__
#define __CN66XX_REGS_H__
#define CN6XXX_XPANSION_BAR 0x30
#define CN6XXX_MSI_CAP 0x50
#define CN6XXX_MSI_ADDR_LO 0x54
#define CN6XXX_MSI_ADDR_HI 0x58
#define CN6XXX_MSI_DATA 0x5C
#define CN6XXX_PCIE_CAP 0x70
#define CN6XXX_PCIE_DEVCAP 0x74
#define CN6XXX_PCIE_DEVCTL 0x78
#define CN6XXX_PCIE_LINKCAP 0x7C
#define CN6XXX_PCIE_LINKCTL 0x80
#define CN6XXX_PCIE_SLOTCAP 0x84
#define CN6XXX_PCIE_SLOTCTL 0x88
#define CN6XXX_PCIE_ENH_CAP 0x100
#define CN6XXX_PCIE_UNCORR_ERR_STATUS 0x104
#define CN6XXX_PCIE_UNCORR_ERR_MASK 0x108
#define CN6XXX_PCIE_UNCORR_ERR 0x10C
#define CN6XXX_PCIE_CORR_ERR_STATUS 0x110
#define CN6XXX_PCIE_CORR_ERR_MASK 0x114
#define CN6XXX_PCIE_ADV_ERR_CAP 0x118
#define CN6XXX_PCIE_ACK_REPLAY_TIMER 0x700
#define CN6XXX_PCIE_OTHER_MSG 0x704
#define CN6XXX_PCIE_PORT_FORCE_LINK 0x708
#define CN6XXX_PCIE_ACK_FREQ 0x70C
#define CN6XXX_PCIE_PORT_LINK_CTL 0x710
#define CN6XXX_PCIE_LANE_SKEW 0x714
#define CN6XXX_PCIE_SYM_NUM 0x718
#define CN6XXX_PCIE_FLTMSK 0x720
/* ############## BAR0 Registers ################ */
#define CN6XXX_SLI_CTL_PORT0 0x0050
#define CN6XXX_SLI_CTL_PORT1 0x0060
#define CN6XXX_SLI_WINDOW_CTL 0x02E0
#define CN6XXX_SLI_DBG_DATA 0x0310
#define CN6XXX_SLI_SCRATCH1 0x03C0
#define CN6XXX_SLI_SCRATCH2 0x03D0
#define CN6XXX_SLI_CTL_STATUS 0x0570
#define CN6XXX_WIN_WR_ADDR_LO 0x0000
#define CN6XXX_WIN_WR_ADDR_HI 0x0004
#define CN6XXX_WIN_WR_ADDR64 CN6XXX_WIN_WR_ADDR_LO
#define CN6XXX_WIN_RD_ADDR_LO 0x0010
#define CN6XXX_WIN_RD_ADDR_HI 0x0014
#define CN6XXX_WIN_RD_ADDR64 CN6XXX_WIN_RD_ADDR_LO
#define CN6XXX_WIN_WR_DATA_LO 0x0020
#define CN6XXX_WIN_WR_DATA_HI 0x0024
#define CN6XXX_WIN_WR_DATA64 CN6XXX_WIN_WR_DATA_LO
#define CN6XXX_WIN_RD_DATA_LO 0x0040
#define CN6XXX_WIN_RD_DATA_HI 0x0044
#define CN6XXX_WIN_RD_DATA64 CN6XXX_WIN_RD_DATA_LO
#define CN6XXX_WIN_WR_MASK_LO 0x0030
#define CN6XXX_WIN_WR_MASK_HI 0x0034
#define CN6XXX_WIN_WR_MASK_REG CN6XXX_WIN_WR_MASK_LO
/* 1 register (32-bit) to enable Input queues */
#define CN6XXX_SLI_PKT_INSTR_ENB 0x1000
/* 1 register (32-bit) to enable Output queues */
#define CN6XXX_SLI_PKT_OUT_ENB 0x1010
/* 1 register (32-bit) to determine whether Output queues are in reset. */
#define CN6XXX_SLI_PORT_IN_RST_OQ 0x11F0
/* 1 register (32-bit) to determine whether Input queues are in reset. */
#define CN6XXX_SLI_PORT_IN_RST_IQ 0x11F4
/*###################### REQUEST QUEUE #########################*/
/* 1 register (32-bit) - instr. size of each input queue. */
#define CN6XXX_SLI_PKT_INSTR_SIZE 0x1020
/* 32 registers for Input Queue Instr Count - SLI_PKT_IN_DONE0_CNTS */
#define CN6XXX_SLI_IQ_INSTR_COUNT_START 0x2000
/* 32 registers for Input Queue Start Addr - SLI_PKT0_INSTR_BADDR */
#define CN6XXX_SLI_IQ_BASE_ADDR_START64 0x2800
/* 32 registers for Input Doorbell - SLI_PKT0_INSTR_BAOFF_DBELL */
#define CN6XXX_SLI_IQ_DOORBELL_START 0x2C00
/* 32 registers for Input Queue size - SLI_PKT0_INSTR_FIFO_RSIZE */
#define CN6XXX_SLI_IQ_SIZE_START 0x3000
/* 32 registers for Instruction Header Options - SLI_PKT0_INSTR_HEADER */
#define CN6XXX_SLI_IQ_PKT_INSTR_HDR_START64 0x3400
/* 1 register (64-bit) - Back Pressure for each input queue - SLI_PKT0_IN_BP */
#define CN66XX_SLI_INPUT_BP_START64 0x3800
/* Each Input Queue register is at a 16-byte Offset in BAR0 */
#define CN6XXX_IQ_OFFSET 0x10
/* 1 register (32-bit) - ES, RO, NS, Arbitration for Input Queue Data &
* gather list fetches. SLI_PKT_INPUT_CONTROL.
*/
#define CN6XXX_SLI_PKT_INPUT_CONTROL 0x1170
/* 1 register (64-bit) - Number of instructions to read at one time
* - 2 bits for each input ring. SLI_PKT_INSTR_RD_SIZE.
*/
#define CN6XXX_SLI_PKT_INSTR_RD_SIZE 0x11A0
/* 1 register (64-bit) - Assign Input ring to MAC port
* - 2 bits for each input ring. SLI_PKT_IN_PCIE_PORT.
*/
#define CN6XXX_SLI_IN_PCIE_PORT 0x11B0
/*------- Request Queue Macros ---------*/
#define CN6XXX_SLI_IQ_BASE_ADDR64(iq) \
(CN6XXX_SLI_IQ_BASE_ADDR_START64 + ((iq) * CN6XXX_IQ_OFFSET))
#define CN6XXX_SLI_IQ_SIZE(iq) \
(CN6XXX_SLI_IQ_SIZE_START + ((iq) * CN6XXX_IQ_OFFSET))
#define CN6XXX_SLI_IQ_PKT_INSTR_HDR64(iq) \
(CN6XXX_SLI_IQ_PKT_INSTR_HDR_START64 + ((iq) * CN6XXX_IQ_OFFSET))
#define CN6XXX_SLI_IQ_DOORBELL(iq) \
(CN6XXX_SLI_IQ_DOORBELL_START + ((iq) * CN6XXX_IQ_OFFSET))
#define CN6XXX_SLI_IQ_INSTR_COUNT(iq) \
(CN6XXX_SLI_IQ_INSTR_COUNT_START + ((iq) * CN6XXX_IQ_OFFSET))
#define CN66XX_SLI_IQ_BP64(iq) \
(CN66XX_SLI_INPUT_BP_START64 + ((iq) * CN6XXX_IQ_OFFSET))
/*------------------ Masks ----------------*/
#define CN6XXX_INPUT_CTL_ROUND_ROBIN_ARB BIT(22)
#define CN6XXX_INPUT_CTL_DATA_NS BIT(8)
#define CN6XXX_INPUT_CTL_DATA_ES_64B_SWAP BIT(6)
#define CN6XXX_INPUT_CTL_DATA_RO BIT(5)
#define CN6XXX_INPUT_CTL_USE_CSR BIT(4)
#define CN6XXX_INPUT_CTL_GATHER_NS BIT(3)
#define CN6XXX_INPUT_CTL_GATHER_ES_64B_SWAP BIT(2)
#define CN6XXX_INPUT_CTL_GATHER_RO BIT(1)
#ifdef __BIG_ENDIAN_BITFIELD
#define CN6XXX_INPUT_CTL_MASK \
(CN6XXX_INPUT_CTL_DATA_ES_64B_SWAP \
| CN6XXX_INPUT_CTL_USE_CSR \
| CN6XXX_INPUT_CTL_GATHER_ES_64B_SWAP)
#else
#define CN6XXX_INPUT_CTL_MASK \
(CN6XXX_INPUT_CTL_DATA_ES_64B_SWAP \
| CN6XXX_INPUT_CTL_USE_CSR)
#endif
/*############################ OUTPUT QUEUE #########################*/
/* 32 registers for Output queue buffer and info size - SLI_PKT0_OUT_SIZE */
#define CN6XXX_SLI_OQ0_BUFF_INFO_SIZE 0x0C00
/* 32 registers for Output Queue Start Addr - SLI_PKT0_SLIST_BADDR */
#define CN6XXX_SLI_OQ_BASE_ADDR_START64 0x1400
/* 32 registers for Output Queue Packet Credits - SLI_PKT0_SLIST_BAOFF_DBELL */
#define CN6XXX_SLI_OQ_PKT_CREDITS_START 0x1800
/* 32 registers for Output Queue size - SLI_PKT0_SLIST_FIFO_RSIZE */
#define CN6XXX_SLI_OQ_SIZE_START 0x1C00
/* 32 registers for Output Queue Packet Count - SLI_PKT0_CNTS */
#define CN6XXX_SLI_OQ_PKT_SENT_START 0x2400
/* Each Output Queue register is at a 16-byte Offset in BAR0 */
#define CN6XXX_OQ_OFFSET 0x10
/* 1 register (32-bit) - 1 bit for each output queue
* - Relaxed Ordering setting for reading Output Queues descriptors
* - SLI_PKT_SLIST_ROR
*/
#define CN6XXX_SLI_PKT_SLIST_ROR 0x1030
/* 1 register (32-bit) - 1 bit for each output queue
* - No Snoop mode for reading Output Queues descriptors
* - SLI_PKT_SLIST_NS
*/
#define CN6XXX_SLI_PKT_SLIST_NS 0x1040
/* 1 register (64-bit) - 2 bits for each output queue
* - Endian-Swap mode for reading Output Queue descriptors
* - SLI_PKT_SLIST_ES
*/
#define CN6XXX_SLI_PKT_SLIST_ES64 0x1050
/* 1 register (32-bit) - 1 bit for each output queue
* - InfoPtr mode for Output Queues.
* - SLI_PKT_IPTR
*/
#define CN6XXX_SLI_PKT_IPTR 0x1070
/* 1 register (32-bit) - 1 bit for each output queue
* - DPTR format selector for Output queues.
* - SLI_PKT_DPADDR
*/
#define CN6XXX_SLI_PKT_DPADDR 0x1080
/* 1 register (32-bit) - 1 bit for each output queue
* - Relaxed Ordering setting for reading Output Queues data
* - SLI_PKT_DATA_OUT_ROR
*/
#define CN6XXX_SLI_PKT_DATA_OUT_ROR 0x1090
/* 1 register (32-bit) - 1 bit for each output queue
* - No Snoop mode for reading Output Queues data
* - SLI_PKT_DATA_OUT_NS
*/
#define CN6XXX_SLI_PKT_DATA_OUT_NS 0x10A0
/* 1 register (64-bit) - 2 bits for each output queue
* - Endian-Swap mode for reading Output Queue data
* - SLI_PKT_DATA_OUT_ES
*/
#define CN6XXX_SLI_PKT_DATA_OUT_ES64 0x10B0
/* 1 register (32-bit) - 1 bit for each output queue
* - Controls whether SLI_PKTn_CNTS is incremented for bytes or for packets.
* - SLI_PKT_OUT_BMODE
*/
#define CN6XXX_SLI_PKT_OUT_BMODE 0x10D0
/* 1 register (64-bit) - 2 bits for each output queue
* - Assign PCIE port for Output queues
* - SLI_PKT_PCIE_PORT.
*/
#define CN6XXX_SLI_PKT_PCIE_PORT64 0x10E0
/* 1 (64-bit) register for Output Queue Packet Count Interrupt Threshold
* & Time Threshold. The same setting applies to all 32 queues.
* The register is defined as a 64-bit registers, but we use the
* 32-bit offsets to define distinct addresses.
*/
#define CN6XXX_SLI_OQ_INT_LEVEL_PKTS 0x1120
#define CN6XXX_SLI_OQ_INT_LEVEL_TIME 0x1124
/* 1 (64-bit register) for Output Queue backpressure across all rings. */
#define CN6XXX_SLI_OQ_WMARK 0x1180
/* 1 register to control output queue global backpressure & ring enable. */
#define CN6XXX_SLI_PKT_CTL 0x1220
/*------- Output Queue Macros ---------*/
#define CN6XXX_SLI_OQ_BASE_ADDR64(oq) \
(CN6XXX_SLI_OQ_BASE_ADDR_START64 + ((oq) * CN6XXX_OQ_OFFSET))
#define CN6XXX_SLI_OQ_SIZE(oq) \
(CN6XXX_SLI_OQ_SIZE_START + ((oq) * CN6XXX_OQ_OFFSET))
#define CN6XXX_SLI_OQ_BUFF_INFO_SIZE(oq) \
(CN6XXX_SLI_OQ0_BUFF_INFO_SIZE + ((oq) * CN6XXX_OQ_OFFSET))
#define CN6XXX_SLI_OQ_PKTS_SENT(oq) \
(CN6XXX_SLI_OQ_PKT_SENT_START + ((oq) * CN6XXX_OQ_OFFSET))
#define CN6XXX_SLI_OQ_PKTS_CREDIT(oq) \
(CN6XXX_SLI_OQ_PKT_CREDITS_START + ((oq) * CN6XXX_OQ_OFFSET))
/*######################### DMA Counters #########################*/
/* 2 registers (64-bit) - DMA Count - 1 for each DMA counter 0/1. */
#define CN6XXX_DMA_CNT_START 0x0400
/* 2 registers (64-bit) - DMA Timer 0/1, contains DMA timer values
* SLI_DMA_0_TIM
*/
#define CN6XXX_DMA_TIM_START 0x0420
/* 2 registers (64-bit) - DMA count & Time Interrupt threshold -
* SLI_DMA_0_INT_LEVEL
*/
#define CN6XXX_DMA_INT_LEVEL_START 0x03E0
/* Each DMA register is at a 16-byte Offset in BAR0 */
#define CN6XXX_DMA_OFFSET 0x10
/*---------- DMA Counter Macros ---------*/
#define CN6XXX_DMA_CNT(dq) \
(CN6XXX_DMA_CNT_START + ((dq) * CN6XXX_DMA_OFFSET))
#define CN6XXX_DMA_INT_LEVEL(dq) \
(CN6XXX_DMA_INT_LEVEL_START + ((dq) * CN6XXX_DMA_OFFSET))
#define CN6XXX_DMA_PKT_INT_LEVEL(dq) \
(CN6XXX_DMA_INT_LEVEL_START + ((dq) * CN6XXX_DMA_OFFSET))
#define CN6XXX_DMA_TIME_INT_LEVEL(dq) \
(CN6XXX_DMA_INT_LEVEL_START + 4 + ((dq) * CN6XXX_DMA_OFFSET))
#define CN6XXX_DMA_TIM(dq) \
(CN6XXX_DMA_TIM_START + ((dq) * CN6XXX_DMA_OFFSET))
/*######################## INTERRUPTS #########################*/
/* 1 register (64-bit) for Interrupt Summary */
#define CN6XXX_SLI_INT_SUM64 0x0330
/* 1 register (64-bit) for Interrupt Enable */
#define CN6XXX_SLI_INT_ENB64_PORT0 0x0340
#define CN6XXX_SLI_INT_ENB64_PORT1 0x0350
/* 1 register (32-bit) to enable Output Queue Packet/Byte Count Interrupt */
#define CN6XXX_SLI_PKT_CNT_INT_ENB 0x1150
/* 1 register (32-bit) to enable Output Queue Packet Timer Interrupt */
#define CN6XXX_SLI_PKT_TIME_INT_ENB 0x1160
/* 1 register (32-bit) to indicate which Output Queue reached pkt threshold */
#define CN6XXX_SLI_PKT_CNT_INT 0x1130
/* 1 register (32-bit) to indicate which Output Queue reached time threshold */
#define CN6XXX_SLI_PKT_TIME_INT 0x1140
/*------------------ Interrupt Masks ----------------*/
#define CN6XXX_INTR_RML_TIMEOUT_ERR BIT(1)
#define CN6XXX_INTR_BAR0_RW_TIMEOUT_ERR BIT(2)
#define CN6XXX_INTR_IO2BIG_ERR BIT(3)
#define CN6XXX_INTR_PKT_COUNT BIT(4)
#define CN6XXX_INTR_PKT_TIME BIT(5)
#define CN6XXX_INTR_M0UPB0_ERR BIT(8)
#define CN6XXX_INTR_M0UPWI_ERR BIT(9)
#define CN6XXX_INTR_M0UNB0_ERR BIT(10)
#define CN6XXX_INTR_M0UNWI_ERR BIT(11)
#define CN6XXX_INTR_M1UPB0_ERR BIT(12)
#define CN6XXX_INTR_M1UPWI_ERR BIT(13)
#define CN6XXX_INTR_M1UNB0_ERR BIT(14)
#define CN6XXX_INTR_M1UNWI_ERR BIT(15)
#define CN6XXX_INTR_MIO_INT0 BIT(16)
#define CN6XXX_INTR_MIO_INT1 BIT(17)
#define CN6XXX_INTR_MAC_INT0 BIT(18)
#define CN6XXX_INTR_MAC_INT1 BIT(19)
#define CN6XXX_INTR_DMA0_FORCE BIT_ULL(32)
#define CN6XXX_INTR_DMA1_FORCE BIT_ULL(33)
#define CN6XXX_INTR_DMA0_COUNT BIT_ULL(34)
#define CN6XXX_INTR_DMA1_COUNT BIT_ULL(35)
#define CN6XXX_INTR_DMA0_TIME BIT_ULL(36)
#define CN6XXX_INTR_DMA1_TIME BIT_ULL(37)
#define CN6XXX_INTR_INSTR_DB_OF_ERR BIT_ULL(48)
#define CN6XXX_INTR_SLIST_DB_OF_ERR BIT_ULL(49)
#define CN6XXX_INTR_POUT_ERR BIT_ULL(50)
#define CN6XXX_INTR_PIN_BP_ERR BIT_ULL(51)
#define CN6XXX_INTR_PGL_ERR BIT_ULL(52)
#define CN6XXX_INTR_PDI_ERR BIT_ULL(53)
#define CN6XXX_INTR_POP_ERR BIT_ULL(54)
#define CN6XXX_INTR_PINS_ERR BIT_ULL(55)
#define CN6XXX_INTR_SPRT0_ERR BIT_ULL(56)
#define CN6XXX_INTR_SPRT1_ERR BIT_ULL(57)
#define CN6XXX_INTR_ILL_PAD_ERR BIT_ULL(60)
#define CN6XXX_INTR_DMA0_DATA (CN6XXX_INTR_DMA0_TIME)
#define CN6XXX_INTR_DMA1_DATA (CN6XXX_INTR_DMA1_TIME)
#define CN6XXX_INTR_DMA_DATA \
(CN6XXX_INTR_DMA0_DATA | CN6XXX_INTR_DMA1_DATA)
#define CN6XXX_INTR_PKT_DATA (CN6XXX_INTR_PKT_TIME | \
CN6XXX_INTR_PKT_COUNT)
/* Sum of interrupts for all PCI-Express Data Interrupts */
#define CN6XXX_INTR_PCIE_DATA \
(CN6XXX_INTR_DMA_DATA | CN6XXX_INTR_PKT_DATA)
#define CN6XXX_INTR_MIO \
(CN6XXX_INTR_MIO_INT0 | CN6XXX_INTR_MIO_INT1)
#define CN6XXX_INTR_MAC \
(CN6XXX_INTR_MAC_INT0 | CN6XXX_INTR_MAC_INT1)
/* Sum of interrupts for error events */
#define CN6XXX_INTR_ERR \
(CN6XXX_INTR_BAR0_RW_TIMEOUT_ERR \
| CN6XXX_INTR_IO2BIG_ERR \
| CN6XXX_INTR_M0UPB0_ERR \
| CN6XXX_INTR_M0UPWI_ERR \
| CN6XXX_INTR_M0UNB0_ERR \
| CN6XXX_INTR_M0UNWI_ERR \
| CN6XXX_INTR_M1UPB0_ERR \
| CN6XXX_INTR_M1UPWI_ERR \
| CN6XXX_INTR_M1UNB0_ERR \
| CN6XXX_INTR_M1UNWI_ERR \
| CN6XXX_INTR_INSTR_DB_OF_ERR \
| CN6XXX_INTR_SLIST_DB_OF_ERR \
| CN6XXX_INTR_POUT_ERR \
| CN6XXX_INTR_PIN_BP_ERR \
| CN6XXX_INTR_PGL_ERR \
| CN6XXX_INTR_PDI_ERR \
| CN6XXX_INTR_POP_ERR \
| CN6XXX_INTR_PINS_ERR \
| CN6XXX_INTR_SPRT0_ERR \
| CN6XXX_INTR_SPRT1_ERR \
| CN6XXX_INTR_ILL_PAD_ERR)
/* Programmed Mask for Interrupt Sum */
#define CN6XXX_INTR_MASK \
(CN6XXX_INTR_PCIE_DATA \
| CN6XXX_INTR_DMA0_FORCE \
| CN6XXX_INTR_DMA1_FORCE \
| CN6XXX_INTR_MIO \
| CN6XXX_INTR_MAC \
| CN6XXX_INTR_ERR)
#define CN6XXX_SLI_S2M_PORT0_CTL 0x3D80
#define CN6XXX_SLI_S2M_PORT1_CTL 0x3D90
#define CN6XXX_SLI_S2M_PORTX_CTL(port) \
(CN6XXX_SLI_S2M_PORT0_CTL + ((port) * 0x10))
#define CN6XXX_SLI_INT_ENB64(port) \
(CN6XXX_SLI_INT_ENB64_PORT0 + ((port) * 0x10))
#define CN6XXX_SLI_MAC_NUMBER 0x3E00
/* CN6XXX BAR1 Index registers. */
#define CN6XXX_PEM_BAR1_INDEX000 0x00011800C00000A8ULL
#define CN6XXX_PEM_OFFSET 0x0000000001000000ULL
#define CN6XXX_BAR1_INDEX_START CN6XXX_PEM_BAR1_INDEX000
#define CN6XXX_PCI_BAR1_OFFSET 0x8
#define CN6XXX_BAR1_REG(idx, port) \
(CN6XXX_BAR1_INDEX_START + ((port) * CN6XXX_PEM_OFFSET) + \
(CN6XXX_PCI_BAR1_OFFSET * (idx)))
/*############################ DPI #########################*/
#define CN6XXX_DPI_CTL 0x0001df0000000040ULL
#define CN6XXX_DPI_DMA_CONTROL 0x0001df0000000048ULL
#define CN6XXX_DPI_REQ_GBL_ENB 0x0001df0000000050ULL
#define CN6XXX_DPI_REQ_ERR_RSP 0x0001df0000000058ULL
#define CN6XXX_DPI_REQ_ERR_RST 0x0001df0000000060ULL
#define CN6XXX_DPI_DMA_ENG0_ENB 0x0001df0000000080ULL
#define CN6XXX_DPI_DMA_ENG_ENB(q_no) \
(CN6XXX_DPI_DMA_ENG0_ENB + ((q_no) * 8))
#define CN6XXX_DPI_DMA_ENG0_BUF 0x0001df0000000880ULL
#define CN6XXX_DPI_DMA_ENG_BUF(q_no) \
(CN6XXX_DPI_DMA_ENG0_BUF + ((q_no) * 8))
#define CN6XXX_DPI_SLI_PRT0_CFG 0x0001df0000000900ULL
#define CN6XXX_DPI_SLI_PRT1_CFG 0x0001df0000000908ULL
#define CN6XXX_DPI_SLI_PRTX_CFG(port) \
(CN6XXX_DPI_SLI_PRT0_CFG + ((port) * 0x10))
#define CN6XXX_DPI_DMA_COMMIT_MODE BIT_ULL(58)
#define CN6XXX_DPI_DMA_PKT_HP BIT_ULL(57)
#define CN6XXX_DPI_DMA_PKT_EN BIT_ULL(56)
#define CN6XXX_DPI_DMA_O_ES BIT_ULL(15)
#define CN6XXX_DPI_DMA_O_MODE BIT_ULL(14)
#define CN6XXX_DPI_DMA_CTL_MASK \
(CN6XXX_DPI_DMA_COMMIT_MODE | \
CN6XXX_DPI_DMA_PKT_HP | \
CN6XXX_DPI_DMA_PKT_EN | \
CN6XXX_DPI_DMA_O_ES | \
CN6XXX_DPI_DMA_O_MODE)
/*############################ CIU #########################*/
#define CN6XXX_CIU_SOFT_BIST 0x0001070000000738ULL
#define CN6XXX_CIU_SOFT_RST 0x0001070000000740ULL
/*############################ MIO #########################*/
#define CN6XXX_MIO_PTP_CLOCK_CFG 0x0001070000000f00ULL
#define CN6XXX_MIO_PTP_CLOCK_LO 0x0001070000000f08ULL
#define CN6XXX_MIO_PTP_CLOCK_HI 0x0001070000000f10ULL
#define CN6XXX_MIO_PTP_CLOCK_COMP 0x0001070000000f18ULL
#define CN6XXX_MIO_PTP_TIMESTAMP 0x0001070000000f20ULL
#define CN6XXX_MIO_PTP_EVT_CNT 0x0001070000000f28ULL
#define CN6XXX_MIO_PTP_CKOUT_THRESH_LO 0x0001070000000f30ULL
#define CN6XXX_MIO_PTP_CKOUT_THRESH_HI 0x0001070000000f38ULL
#define CN6XXX_MIO_PTP_CKOUT_HI_INCR 0x0001070000000f40ULL
#define CN6XXX_MIO_PTP_CKOUT_LO_INCR 0x0001070000000f48ULL
#define CN6XXX_MIO_PTP_PPS_THRESH_LO 0x0001070000000f50ULL
#define CN6XXX_MIO_PTP_PPS_THRESH_HI 0x0001070000000f58ULL
#define CN6XXX_MIO_PTP_PPS_HI_INCR 0x0001070000000f60ULL
#define CN6XXX_MIO_PTP_PPS_LO_INCR 0x0001070000000f68ULL
#define CN6XXX_MIO_QLM4_CFG 0x00011800000015B0ULL
#define CN6XXX_MIO_RST_BOOT 0x0001180000001600ULL
#define CN6XXX_MIO_QLM_CFG_MASK 0x7
/*############################ LMC #########################*/
#define CN6XXX_LMC0_RESET_CTL 0x0001180088000180ULL
#define CN6XXX_LMC0_RESET_CTL_DDR3RST_MASK 0x0000000000000001ULL
#endif
@@ -0,0 +1,184 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
#include "cn66xx_regs.h"
#include "cn66xx_device.h"
#include "cn68xx_device.h"
#include "cn68xx_regs.h"
static void lio_cn68xx_set_dpi_regs(struct octeon_device *oct)
{
u32 i;
u32 fifo_sizes[6] = { 3, 3, 1, 1, 1, 8 };
lio_pci_writeq(oct, CN6XXX_DPI_DMA_CTL_MASK, CN6XXX_DPI_DMA_CONTROL);
dev_dbg(&oct->pci_dev->dev, "DPI_DMA_CONTROL: 0x%016llx\n",
lio_pci_readq(oct, CN6XXX_DPI_DMA_CONTROL));
for (i = 0; i < 6; i++) {
/* Prevent service of instruction queue for all DMA engines
* Engine 5 will remain 0. Engines 0 - 4 will be setup by
* core.
*/
lio_pci_writeq(oct, 0, CN6XXX_DPI_DMA_ENG_ENB(i));
lio_pci_writeq(oct, fifo_sizes[i], CN6XXX_DPI_DMA_ENG_BUF(i));
dev_dbg(&oct->pci_dev->dev, "DPI_ENG_BUF%d: 0x%016llx\n", i,
lio_pci_readq(oct, CN6XXX_DPI_DMA_ENG_BUF(i)));
}
/* DPI_SLI_PRT_CFG has MPS and MRRS settings that will be set
* separately.
*/
lio_pci_writeq(oct, 1, CN6XXX_DPI_CTL);
dev_dbg(&oct->pci_dev->dev, "DPI_CTL: 0x%016llx\n",
lio_pci_readq(oct, CN6XXX_DPI_CTL));
}
static int lio_cn68xx_soft_reset(struct octeon_device *oct)
{
lio_cn6xxx_soft_reset(oct);
lio_cn68xx_set_dpi_regs(oct);
return 0;
}
static void lio_cn68xx_setup_pkt_ctl_regs(struct octeon_device *oct)
{
struct octeon_cn6xxx *cn68xx = (struct octeon_cn6xxx *)oct->chip;
u64 pktctl, tx_pipe, max_oqs;
pktctl = octeon_read_csr64(oct, CN6XXX_SLI_PKT_CTL);
/* 68XX specific */
max_oqs = CFG_GET_OQ_MAX_Q(CHIP_CONF(oct, cn6xxx));
tx_pipe = octeon_read_csr64(oct, CN68XX_SLI_TX_PIPE);
tx_pipe &= 0xffffffffff00ffffULL; /* clear out NUMP field */
tx_pipe |= max_oqs << 16; /* put max_oqs in NUMP field */
octeon_write_csr64(oct, CN68XX_SLI_TX_PIPE, tx_pipe);
if (CFG_GET_IS_SLI_BP_ON(cn68xx->conf))
pktctl |= 0xF;
else
/* Disable per-port backpressure. */
pktctl &= ~0xF;
octeon_write_csr64(oct, CN6XXX_SLI_PKT_CTL, pktctl);
}
static int lio_cn68xx_setup_device_regs(struct octeon_device *oct)
{
lio_cn6xxx_setup_pcie_mps(oct, PCIE_MPS_DEFAULT);
lio_cn6xxx_setup_pcie_mrrs(oct, PCIE_MRRS_256B);
lio_cn6xxx_enable_error_reporting(oct);
lio_cn6xxx_setup_global_input_regs(oct);
lio_cn68xx_setup_pkt_ctl_regs(oct);
lio_cn6xxx_setup_global_output_regs(oct);
/* Default error timeout value should be 0x200000 to avoid host hang
* when reads invalid register
*/
octeon_write_csr64(oct, CN6XXX_SLI_WINDOW_CTL, 0x200000ULL);
return 0;
}
static inline void lio_cn68xx_vendor_message_fix(struct octeon_device *oct)
{
u32 val = 0;
/* Set M_VEND1_DRP and M_VEND0_DRP bits */
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_FLTMSK, &val);
val |= 0x3;
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_FLTMSK, val);
}
static int lio_is_210nv(struct octeon_device *oct)
{
u64 mio_qlm4_cfg = lio_pci_readq(oct, CN6XXX_MIO_QLM4_CFG);
return ((mio_qlm4_cfg & CN6XXX_MIO_QLM_CFG_MASK) == 0);
}
int lio_setup_cn68xx_octeon_device(struct octeon_device *oct)
{
struct octeon_cn6xxx *cn68xx = (struct octeon_cn6xxx *)oct->chip;
u16 card_type = LIO_410NV;
if (octeon_map_pci_barx(oct, 0, 0))
return 1;
if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) {
dev_err(&oct->pci_dev->dev, "%s CN68XX BAR1 map failed\n",
__func__);
octeon_unmap_pci_barx(oct, 0);
return 1;
}
spin_lock_init(&cn68xx->lock_for_droq_int_enb_reg);
oct->fn_list.setup_iq_regs = lio_cn6xxx_setup_iq_regs;
oct->fn_list.setup_oq_regs = lio_cn6xxx_setup_oq_regs;
oct->fn_list.process_interrupt_regs = lio_cn6xxx_process_interrupt_regs;
oct->fn_list.soft_reset = lio_cn68xx_soft_reset;
oct->fn_list.setup_device_regs = lio_cn68xx_setup_device_regs;
oct->fn_list.update_iq_read_idx = lio_cn6xxx_update_read_index;
oct->fn_list.bar1_idx_setup = lio_cn6xxx_bar1_idx_setup;
oct->fn_list.bar1_idx_write = lio_cn6xxx_bar1_idx_write;
oct->fn_list.bar1_idx_read = lio_cn6xxx_bar1_idx_read;
oct->fn_list.enable_interrupt = lio_cn6xxx_enable_interrupt;
oct->fn_list.disable_interrupt = lio_cn6xxx_disable_interrupt;
oct->fn_list.enable_io_queues = lio_cn6xxx_enable_io_queues;
oct->fn_list.disable_io_queues = lio_cn6xxx_disable_io_queues;
lio_cn6xxx_setup_reg_address(oct, oct->chip, &oct->reg_list);
/* Determine variant of card */
if (lio_is_210nv(oct))
card_type = LIO_210NV;
cn68xx->conf = (struct octeon_config *)
oct_get_config_info(oct, card_type);
if (!cn68xx->conf) {
dev_err(&oct->pci_dev->dev, "%s No Config found for CN68XX %s\n",
__func__,
(card_type == LIO_410NV) ? LIO_410NV_NAME :
LIO_210NV_NAME);
octeon_unmap_pci_barx(oct, 0);
octeon_unmap_pci_barx(oct, 1);
return 1;
}
oct->coproc_clock_rate = 1000000ULL * lio_cn6xxx_coprocessor_clock(oct);
lio_cn68xx_vendor_message_fix(oct);
return 0;
}
EXPORT_SYMBOL_GPL(lio_setup_cn68xx_octeon_device);
@@ -0,0 +1,27 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn68xx_device.h
* \brief Host Driver: Routines that perform CN68XX specific operations.
*/
#ifndef __CN68XX_DEVICE_H__
#define __CN68XX_DEVICE_H__
int lio_setup_cn68xx_octeon_device(struct octeon_device *oct);
#endif
@@ -0,0 +1,45 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file cn68xx_regs.h
* \brief Host Driver: Register Address and Register Mask values for
* Octeon CN68XX devices. The register map for CN66XX is the same
* for most registers. This file has the other registers that are
* 68XX-specific.
*/
#ifndef __CN68XX_REGS_H__
#define __CN68XX_REGS_H__
/*###################### REQUEST QUEUE #########################*/
#define CN68XX_SLI_IQ_PORT0_PKIND 0x0800
#define CN68XX_SLI_IQ_PORT_PKIND(iq) \
(CN68XX_SLI_IQ_PORT0_PKIND + ((iq) * CN6XXX_IQ_OFFSET))
/*############################ OUTPUT QUEUE #########################*/
/* Starting pipe number and number of pipes used by the SLI packet output. */
#define CN68XX_SLI_TX_PIPE 0x1230
/*######################## INTERRUPTS #########################*/
/*------------------ Interrupt Masks ----------------*/
#define CN68XX_INTR_PIPE_ERR BIT_ULL(61)
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,672 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2017 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/if_vlan.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_nic.h"
#include "octeon_main.h"
#include "octeon_network.h"
#include "lio_vf_rep.h"
static int lio_vf_rep_open(struct net_device *ndev);
static int lio_vf_rep_stop(struct net_device *ndev);
static netdev_tx_t lio_vf_rep_pkt_xmit(struct sk_buff *skb,
struct net_device *ndev);
static void lio_vf_rep_tx_timeout(struct net_device *netdev, unsigned int txqueue);
static int lio_vf_rep_phys_port_name(struct net_device *dev,
char *buf, size_t len);
static void lio_vf_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats64);
static int lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu);
static int lio_vf_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid);
static const struct net_device_ops lio_vf_rep_ndev_ops = {
.ndo_open = lio_vf_rep_open,
.ndo_stop = lio_vf_rep_stop,
.ndo_start_xmit = lio_vf_rep_pkt_xmit,
.ndo_tx_timeout = lio_vf_rep_tx_timeout,
.ndo_get_phys_port_name = lio_vf_rep_phys_port_name,
.ndo_get_stats64 = lio_vf_rep_get_stats64,
.ndo_change_mtu = lio_vf_rep_change_mtu,
.ndo_get_port_parent_id = lio_vf_get_port_parent_id,
};
static int
lio_vf_rep_send_soft_command(struct octeon_device *oct,
void *req, int req_size,
void *resp, int resp_size)
{
int tot_resp_size = sizeof(struct lio_vf_rep_resp) + resp_size;
struct octeon_soft_command *sc = NULL;
struct lio_vf_rep_resp *rep_resp;
void *sc_req;
int err;
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, req_size,
tot_resp_size, 0);
if (!sc)
return -ENOMEM;
init_completion(&sc->complete);
sc->sc_status = OCTEON_REQUEST_PENDING;
sc_req = (struct lio_vf_rep_req *)sc->virtdptr;
memcpy(sc_req, req, req_size);
rep_resp = (struct lio_vf_rep_resp *)sc->virtrptr;
memset(rep_resp, 0, tot_resp_size);
WRITE_ONCE(rep_resp->status, 1);
sc->iq_no = 0;
octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
OPCODE_NIC_VF_REP_CMD, 0, 0, 0);
err = octeon_send_soft_command(oct, sc);
if (err == IQ_SEND_FAILED)
goto free_buff;
err = wait_for_sc_completion_timeout(oct, sc, 0);
if (err)
return err;
err = READ_ONCE(rep_resp->status) ? -EBUSY : 0;
if (err)
dev_err(&oct->pci_dev->dev, "VF rep send config failed\n");
else if (resp)
memcpy(resp, (rep_resp + 1), resp_size);
WRITE_ONCE(sc->caller_is_done, true);
return err;
free_buff:
octeon_free_soft_command(oct, sc);
return err;
}
static int
lio_vf_rep_open(struct net_device *ndev)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
struct lio_vf_rep_req rep_cfg;
struct octeon_device *oct;
int ret;
oct = vf_rep->oct;
memset(&rep_cfg, 0, sizeof(rep_cfg));
rep_cfg.req_type = LIO_VF_REP_REQ_STATE;
rep_cfg.ifidx = vf_rep->ifidx;
rep_cfg.rep_state.state = LIO_VF_REP_STATE_UP;
ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
sizeof(rep_cfg), NULL, 0);
if (ret) {
dev_err(&oct->pci_dev->dev,
"VF_REP open failed with err %d\n", ret);
return -EIO;
}
atomic_set(&vf_rep->ifstate, (atomic_read(&vf_rep->ifstate) |
LIO_IFSTATE_RUNNING));
netif_carrier_on(ndev);
netif_start_queue(ndev);
return 0;
}
static int
lio_vf_rep_stop(struct net_device *ndev)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
struct lio_vf_rep_req rep_cfg;
struct octeon_device *oct;
int ret;
oct = vf_rep->oct;
memset(&rep_cfg, 0, sizeof(rep_cfg));
rep_cfg.req_type = LIO_VF_REP_REQ_STATE;
rep_cfg.ifidx = vf_rep->ifidx;
rep_cfg.rep_state.state = LIO_VF_REP_STATE_DOWN;
ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
sizeof(rep_cfg), NULL, 0);
if (ret) {
dev_err(&oct->pci_dev->dev,
"VF_REP dev stop failed with err %d\n", ret);
return -EIO;
}
atomic_set(&vf_rep->ifstate, (atomic_read(&vf_rep->ifstate) &
~LIO_IFSTATE_RUNNING));
netif_tx_disable(ndev);
netif_carrier_off(ndev);
return 0;
}
static void
lio_vf_rep_tx_timeout(struct net_device *ndev, unsigned int txqueue)
{
netif_trans_update(ndev);
netif_wake_queue(ndev);
}
static void
lio_vf_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats64)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
/* Swap tx and rx stats as VF rep is a switch port */
stats64->tx_packets = vf_rep->stats.rx_packets;
stats64->tx_bytes = vf_rep->stats.rx_bytes;
stats64->tx_dropped = vf_rep->stats.rx_dropped;
stats64->rx_packets = vf_rep->stats.tx_packets;
stats64->rx_bytes = vf_rep->stats.tx_bytes;
stats64->rx_dropped = vf_rep->stats.tx_dropped;
}
static int
lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
struct lio_vf_rep_req rep_cfg;
struct octeon_device *oct;
int ret;
oct = vf_rep->oct;
memset(&rep_cfg, 0, sizeof(rep_cfg));
rep_cfg.req_type = LIO_VF_REP_REQ_MTU;
rep_cfg.ifidx = vf_rep->ifidx;
rep_cfg.rep_mtu.mtu = cpu_to_be32(new_mtu);
ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
sizeof(rep_cfg), NULL, 0);
if (ret) {
dev_err(&oct->pci_dev->dev,
"Change MTU failed with err %d\n", ret);
return -EIO;
}
WRITE_ONCE(ndev->mtu, new_mtu);
return 0;
}
static int
lio_vf_rep_phys_port_name(struct net_device *dev,
char *buf, size_t len)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
struct octeon_device *oct = vf_rep->oct;
int ret;
ret = snprintf(buf, len, "pf%dvf%d", oct->pf_num,
vf_rep->ifidx - oct->pf_num * 64 - 1);
if (ret >= len)
return -EOPNOTSUPP;
return 0;
}
static struct net_device *
lio_vf_rep_get_ndev(struct octeon_device *oct, int ifidx)
{
int vf_id, max_vfs = CN23XX_MAX_VFS_PER_PF + 1;
int vfid_mask = max_vfs - 1;
if (ifidx <= oct->pf_num * max_vfs ||
ifidx >= oct->pf_num * max_vfs + max_vfs)
return NULL;
/* ifidx 1-63 for PF0 VFs
* ifidx 65-127 for PF1 VFs
*/
vf_id = (ifidx & vfid_mask) - 1;
return oct->vf_rep_list.ndev[vf_id];
}
static void
lio_vf_rep_copy_packet(struct octeon_device *oct,
struct sk_buff *skb,
int len)
{
if (likely(len > MIN_SKB_SIZE)) {
struct octeon_skb_page_info *pg_info;
unsigned char *va;
pg_info = ((struct octeon_skb_page_info *)(skb->cb));
if (pg_info->page) {
va = page_address(pg_info->page) +
pg_info->page_offset;
memcpy(skb->data, va, MIN_SKB_SIZE);
skb_put(skb, MIN_SKB_SIZE);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
pg_info->page,
pg_info->page_offset + MIN_SKB_SIZE,
len - MIN_SKB_SIZE,
LIO_RXBUFFER_SZ);
}
} else {
struct octeon_skb_page_info *pg_info =
((struct octeon_skb_page_info *)(skb->cb));
skb_copy_to_linear_data(skb, page_address(pg_info->page) +
pg_info->page_offset, len);
skb_put(skb, len);
put_page(pg_info->page);
}
}
static int
lio_vf_rep_pkt_recv(struct octeon_recv_info *recv_info, void *buf)
{
struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt;
struct lio_vf_rep_desc *vf_rep;
struct net_device *vf_ndev;
struct octeon_device *oct;
union octeon_rh *rh;
struct sk_buff *skb;
int i, ifidx;
oct = lio_get_device(recv_pkt->octeon_id);
if (!oct)
goto free_buffers;
skb = recv_pkt->buffer_ptr[0];
rh = &recv_pkt->rh;
ifidx = rh->r.ossp;
vf_ndev = lio_vf_rep_get_ndev(oct, ifidx);
if (!vf_ndev)
goto free_buffers;
vf_rep = netdev_priv(vf_ndev);
if (!(atomic_read(&vf_rep->ifstate) & LIO_IFSTATE_RUNNING) ||
recv_pkt->buffer_count > 1)
goto free_buffers;
skb->dev = vf_ndev;
/* Multiple buffers are not used for vf_rep packets.
* So just buffer_size[0] is valid.
*/
lio_vf_rep_copy_packet(oct, skb, recv_pkt->buffer_size[0]);
skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT);
skb->protocol = eth_type_trans(skb, skb->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
octeon_free_recv_info(recv_info);
return 0;
free_buffers:
for (i = 0; i < recv_pkt->buffer_count; i++)
recv_buffer_free(recv_pkt->buffer_ptr[i]);
octeon_free_recv_info(recv_info);
return 0;
}
static void
lio_vf_rep_packet_sent_callback(struct octeon_device *oct,
u32 status, void *buf)
{
struct octeon_soft_command *sc = (struct octeon_soft_command *)buf;
struct sk_buff *skb = sc->ctxptr;
struct net_device *ndev = skb->dev;
u32 iq_no;
dma_unmap_single(&oct->pci_dev->dev, sc->dmadptr,
sc->datasize, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
iq_no = sc->iq_no;
octeon_free_soft_command(oct, sc);
if (octnet_iq_is_full(oct, iq_no))
return;
if (netif_queue_stopped(ndev))
netif_wake_queue(ndev);
}
static netdev_tx_t
lio_vf_rep_pkt_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
struct net_device *parent_ndev = vf_rep->parent_ndev;
struct octeon_device *oct = vf_rep->oct;
struct octeon_instr_pki_ih3 *pki_ih3;
struct octeon_soft_command *sc;
struct lio *parent_lio;
int status;
parent_lio = GET_LIO(parent_ndev);
if (!(atomic_read(&vf_rep->ifstate) & LIO_IFSTATE_RUNNING) ||
skb->len <= 0)
goto xmit_failed;
if (octnet_iq_is_full(vf_rep->oct, parent_lio->txq)) {
dev_err(&oct->pci_dev->dev, "VF rep: Device IQ full\n");
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
}
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, 0, 16, 0);
if (!sc) {
dev_err(&oct->pci_dev->dev, "VF rep: Soft command alloc failed\n");
goto xmit_failed;
}
/* Multiple buffers are not used for vf_rep packets. */
if (skb_shinfo(skb)->nr_frags != 0) {
dev_err(&oct->pci_dev->dev, "VF rep: nr_frags != 0. Dropping packet\n");
octeon_free_soft_command(oct, sc);
goto xmit_failed;
}
sc->dmadptr = dma_map_single(&oct->pci_dev->dev,
skb->data, skb->len, DMA_TO_DEVICE);
if (dma_mapping_error(&oct->pci_dev->dev, sc->dmadptr)) {
dev_err(&oct->pci_dev->dev, "VF rep: DMA mapping failed\n");
octeon_free_soft_command(oct, sc);
goto xmit_failed;
}
sc->virtdptr = skb->data;
sc->datasize = skb->len;
sc->ctxptr = skb;
sc->iq_no = parent_lio->txq;
octeon_prepare_soft_command(oct, sc, OPCODE_NIC, OPCODE_NIC_VF_REP_PKT,
vf_rep->ifidx, 0, 0);
pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3;
pki_ih3->tagtype = ORDERED_TAG;
sc->callback = lio_vf_rep_packet_sent_callback;
sc->callback_arg = sc;
status = octeon_send_soft_command(oct, sc);
if (status == IQ_SEND_FAILED) {
dma_unmap_single(&oct->pci_dev->dev, sc->dmadptr,
sc->datasize, DMA_TO_DEVICE);
octeon_free_soft_command(oct, sc);
goto xmit_failed;
}
if (status == IQ_SEND_STOP)
netif_stop_queue(ndev);
netif_trans_update(ndev);
return NETDEV_TX_OK;
xmit_failed:
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
static int lio_vf_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
struct net_device *parent_ndev = vf_rep->parent_ndev;
struct lio *lio = GET_LIO(parent_ndev);
ppid->id_len = ETH_ALEN;
ether_addr_copy(ppid->id, (void *)&lio->linfo.hw_addr + 2);
return 0;
}
static void
lio_vf_rep_fetch_stats(struct work_struct *work)
{
struct cavium_wk *wk = (struct cavium_wk *)work;
struct lio_vf_rep_desc *vf_rep = wk->ctxptr;
struct lio_vf_rep_stats stats;
struct lio_vf_rep_req rep_cfg;
struct octeon_device *oct;
int ret;
oct = vf_rep->oct;
memset(&rep_cfg, 0, sizeof(rep_cfg));
rep_cfg.req_type = LIO_VF_REP_REQ_STATS;
rep_cfg.ifidx = vf_rep->ifidx;
ret = lio_vf_rep_send_soft_command(oct, &rep_cfg, sizeof(rep_cfg),
&stats, sizeof(stats));
if (!ret) {
octeon_swap_8B_data((u64 *)&stats, (sizeof(stats) >> 3));
memcpy(&vf_rep->stats, &stats, sizeof(stats));
}
schedule_delayed_work(&vf_rep->stats_wk.work,
msecs_to_jiffies(LIO_VF_REP_STATS_POLL_TIME_MS));
}
int
lio_vf_rep_create(struct octeon_device *oct)
{
struct lio_vf_rep_desc *vf_rep;
struct net_device *ndev;
int i, num_vfs;
if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return 0;
if (!oct->sriov_info.sriov_enabled)
return 0;
num_vfs = oct->sriov_info.num_vfs_alloced;
oct->vf_rep_list.num_vfs = 0;
for (i = 0; i < num_vfs; i++) {
ndev = alloc_etherdev(sizeof(struct lio_vf_rep_desc));
if (!ndev) {
dev_err(&oct->pci_dev->dev,
"VF rep device %d creation failed\n", i);
goto cleanup;
}
ndev->min_mtu = LIO_MIN_MTU_SIZE;
ndev->max_mtu = LIO_MAX_MTU_SIZE;
ndev->netdev_ops = &lio_vf_rep_ndev_ops;
vf_rep = netdev_priv(ndev);
memset(vf_rep, 0, sizeof(*vf_rep));
vf_rep->ndev = ndev;
vf_rep->oct = oct;
vf_rep->parent_ndev = oct->props[0].netdev;
vf_rep->ifidx = (oct->pf_num * 64) + i + 1;
eth_hw_addr_random(ndev);
if (register_netdev(ndev)) {
dev_err(&oct->pci_dev->dev, "VF rep nerdev registration failed\n");
free_netdev(ndev);
goto cleanup;
}
netif_carrier_off(ndev);
INIT_DELAYED_WORK(&vf_rep->stats_wk.work,
lio_vf_rep_fetch_stats);
vf_rep->stats_wk.ctxptr = (void *)vf_rep;
schedule_delayed_work(&vf_rep->stats_wk.work,
msecs_to_jiffies
(LIO_VF_REP_STATS_POLL_TIME_MS));
oct->vf_rep_list.num_vfs++;
oct->vf_rep_list.ndev[i] = ndev;
}
if (octeon_register_dispatch_fn(oct, OPCODE_NIC,
OPCODE_NIC_VF_REP_PKT,
lio_vf_rep_pkt_recv, oct)) {
dev_err(&oct->pci_dev->dev, "VF rep Dispatch func registration failed\n");
goto cleanup;
}
return 0;
cleanup:
for (i = 0; i < oct->vf_rep_list.num_vfs; i++) {
ndev = oct->vf_rep_list.ndev[i];
oct->vf_rep_list.ndev[i] = NULL;
if (ndev) {
vf_rep = netdev_priv(ndev);
cancel_delayed_work_sync
(&vf_rep->stats_wk.work);
unregister_netdev(ndev);
free_netdev(ndev);
}
}
oct->vf_rep_list.num_vfs = 0;
return -1;
}
void
lio_vf_rep_destroy(struct octeon_device *oct)
{
struct lio_vf_rep_desc *vf_rep;
struct net_device *ndev;
int i;
if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
return;
if (!oct->sriov_info.sriov_enabled)
return;
for (i = 0; i < oct->vf_rep_list.num_vfs; i++) {
ndev = oct->vf_rep_list.ndev[i];
oct->vf_rep_list.ndev[i] = NULL;
if (ndev) {
vf_rep = netdev_priv(ndev);
cancel_delayed_work_sync
(&vf_rep->stats_wk.work);
netif_tx_disable(ndev);
netif_carrier_off(ndev);
unregister_netdev(ndev);
free_netdev(ndev);
}
}
oct->vf_rep_list.num_vfs = 0;
}
static int
lio_vf_rep_netdev_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
struct lio_vf_rep_desc *vf_rep;
struct lio_vf_rep_req rep_cfg;
struct octeon_device *oct;
int ret;
switch (event) {
case NETDEV_REGISTER:
case NETDEV_CHANGENAME:
break;
default:
return NOTIFY_DONE;
}
if (ndev->netdev_ops != &lio_vf_rep_ndev_ops)
return NOTIFY_DONE;
vf_rep = netdev_priv(ndev);
oct = vf_rep->oct;
if (strlen(ndev->name) > LIO_IF_NAME_SIZE) {
dev_err(&oct->pci_dev->dev,
"Device name change sync failed as the size is > %d\n",
LIO_IF_NAME_SIZE);
return NOTIFY_DONE;
}
memset(&rep_cfg, 0, sizeof(rep_cfg));
rep_cfg.req_type = LIO_VF_REP_REQ_DEVNAME;
rep_cfg.ifidx = vf_rep->ifidx;
strscpy(rep_cfg.rep_name.name, ndev->name,
sizeof(rep_cfg.rep_name.name));
ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
sizeof(rep_cfg), NULL, 0);
if (ret)
dev_err(&oct->pci_dev->dev,
"vf_rep netdev name change failed with err %d\n", ret);
return NOTIFY_DONE;
}
static struct notifier_block lio_vf_rep_netdev_notifier = {
.notifier_call = lio_vf_rep_netdev_event,
};
int
lio_vf_rep_modinit(void)
{
if (register_netdevice_notifier(&lio_vf_rep_netdev_notifier)) {
pr_err("netdev notifier registration failed\n");
return -EFAULT;
}
return 0;
}
void
lio_vf_rep_modexit(void)
{
if (unregister_netdevice_notifier(&lio_vf_rep_netdev_notifier))
pr_err("netdev notifier unregister failed\n");
}
@@ -0,0 +1,49 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2017 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium, Inc. for more information
**********************************************************************/
/*! \file octeon_vf_main.h
* \brief Host Driver: This file defines vf_rep related macros and structures
*/
#ifndef __LIO_VF_REP_H__
#define __LIO_VF_REP_H__
#define LIO_VF_REP_REQ_TMO_MS 5000
#define LIO_VF_REP_STATS_POLL_TIME_MS 200
struct lio_vf_rep_desc {
struct net_device *parent_ndev;
struct net_device *ndev;
struct octeon_device *oct;
struct lio_vf_rep_stats stats;
struct cavium_wk stats_wk;
atomic_t ifstate;
int ifidx;
};
struct lio_vf_rep_sc_ctx {
struct completion complete;
};
int lio_vf_rep_create(struct octeon_device *oct);
void lio_vf_rep_destroy(struct octeon_device *oct);
int lio_vf_rep_modinit(void);
void lio_vf_rep_modexit(void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#ifndef _LIQUIDIO_IMAGE_H_
#define _LIQUIDIO_IMAGE_H_
#define LIO_MAX_FW_TYPE_LEN (8)
#define LIO_MAX_FW_FILENAME_LEN (256)
#define LIO_FW_DIR "liquidio/"
#define LIO_FW_BASE_NAME "lio_"
#define LIO_FW_NAME_SUFFIX ".bin"
#define LIO_FW_NAME_TYPE_NIC "nic"
#define LIO_FW_NAME_TYPE_AUTO "auto"
#define LIO_FW_NAME_TYPE_NONE "none"
#define LIO_MAX_FIRMWARE_VERSION_LEN 16
#define LIO_MAX_BOOTCMD_LEN 1024
#define LIO_MAX_IMAGES 16
#define LIO_NIC_MAGIC 0x434E4943 /* "CNIC" */
struct octeon_firmware_desc {
__be64 addr;
__be32 len;
__be32 crc32; /* crc32 of image */
};
/* Following the header is a list of 64-bit aligned binary images,
* as described by the desc field.
* Numeric fields are in network byte order.
*/
struct octeon_firmware_file_header {
__be32 magic;
char version[LIO_MAX_FIRMWARE_VERSION_LEN];
char bootcmd[LIO_MAX_BOOTCMD_LEN];
__be32 num_images;
struct octeon_firmware_desc desc[LIO_MAX_IMAGES];
__be32 pad;
__be32 crc32; /* header checksum */
};
#endif /* _LIQUIDIO_IMAGE_H_ */
@@ -0,0 +1,472 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file octeon_config.h
* \brief Host Driver: Configuration data structures for the host driver.
*/
#ifndef __OCTEON_CONFIG_H__
#define __OCTEON_CONFIG_H__
/*--------------------------CONFIG VALUES------------------------*/
/* The following macros affect the way the driver data structures
* are generated for Octeon devices.
* They can be modified.
*/
/* Maximum octeon devices defined as MAX_OCTEON_NICIF to support
* multiple(<= MAX_OCTEON_NICIF) Miniports
*/
#define MAX_OCTEON_NICIF 128
#define MAX_OCTEON_DEVICES MAX_OCTEON_NICIF
#define MAX_OCTEON_LINKS MAX_OCTEON_NICIF
#define MAX_OCTEON_MULTICAST_ADDR 32
#define MAX_OCTEON_FILL_COUNT 8
/* CN6xxx IQ configuration macros */
#define CN6XXX_MAX_INPUT_QUEUES 32
#define CN6XXX_MAX_IQ_DESCRIPTORS 2048
#define CN6XXX_DB_MIN 1
#define CN6XXX_DB_MAX 8
#define CN6XXX_DB_TIMEOUT 1
/* CN6xxx OQ configuration macros */
#define CN6XXX_MAX_OUTPUT_QUEUES 32
#define CN6XXX_MAX_OQ_DESCRIPTORS 2048
#define CN6XXX_OQ_BUF_SIZE 1664
#define CN6XXX_OQ_PKTSPER_INTR ((CN6XXX_MAX_OQ_DESCRIPTORS < 512) ? \
(CN6XXX_MAX_OQ_DESCRIPTORS / 4) : 128)
#define CN6XXX_OQ_REFIL_THRESHOLD ((CN6XXX_MAX_OQ_DESCRIPTORS < 512) ? \
(CN6XXX_MAX_OQ_DESCRIPTORS / 4) : 128)
#define CN6XXX_OQ_INTR_PKT 64
#define CN6XXX_OQ_INTR_TIME 100
#define DEFAULT_NUM_NIC_PORTS_66XX 2
#define DEFAULT_NUM_NIC_PORTS_68XX 4
#define DEFAULT_NUM_NIC_PORTS_68XX_210NV 2
/* CN23xx IQ configuration macros */
#define CN23XX_MAX_VFS_PER_PF_PASS_1_0 8
#define CN23XX_MAX_VFS_PER_PF_PASS_1_1 31
#define CN23XX_MAX_VFS_PER_PF 63
#define CN23XX_MAX_RINGS_PER_VF 8
#define CN23XX_MAX_RINGS_PER_PF_PASS_1_0 12
#define CN23XX_MAX_RINGS_PER_PF_PASS_1_1 32
#define CN23XX_MAX_RINGS_PER_PF 64
#define CN23XX_MAX_RINGS_PER_VF 8
#define CN23XX_MAX_INPUT_QUEUES CN23XX_MAX_RINGS_PER_PF
#define CN23XX_MAX_IQ_DESCRIPTORS 2048
#define CN23XX_DEFAULT_IQ_DESCRIPTORS 512
#define CN23XX_MIN_IQ_DESCRIPTORS 128
#define CN23XX_DB_MIN 1
#define CN23XX_DB_MAX 8
#define CN23XX_DB_TIMEOUT 1
#define CN23XX_MAX_OUTPUT_QUEUES CN23XX_MAX_RINGS_PER_PF
#define CN23XX_MAX_OQ_DESCRIPTORS 2048
#define CN23XX_DEFAULT_OQ_DESCRIPTORS 512
#define CN23XX_MIN_OQ_DESCRIPTORS 128
#define CN23XX_OQ_BUF_SIZE 1664
#define CN23XX_OQ_PKTSPER_INTR 128
/*#define CAVIUM_ONLY_CN23XX_RX_PERF*/
#define CN23XX_OQ_REFIL_THRESHOLD 16
#define CN23XX_OQ_INTR_PKT 64
#define CN23XX_OQ_INTR_TIME 100
#define DEFAULT_NUM_NIC_PORTS_23XX 1
#define CN23XX_CFG_IO_QUEUES CN23XX_MAX_RINGS_PER_PF
/* PEMs count */
#define CN23XX_MAX_MACS 4
#define CN23XX_DEF_IQ_INTR_THRESHOLD 32
#define CN23XX_DEF_IQ_INTR_BYTE_THRESHOLD (64 * 1024)
/* common OCTEON configuration macros */
#define CN6XXX_CFG_IO_QUEUES 32
#define OCTEON_32BYTE_INSTR 32
#define OCTEON_64BYTE_INSTR 64
#define OCTEON_MAX_BASE_IOQ 4
#define OCTEON_DMA_INTR_PKT 64
#define OCTEON_DMA_INTR_TIME 1000
#define MAX_TXQS_PER_INTF 8
#define MAX_RXQS_PER_INTF 8
#define DEF_TXQS_PER_INTF 4
#define DEF_RXQS_PER_INTF 4
#define INVALID_IOQ_NO 0xff
#define DEFAULT_POW_GRP 0
/* Macros to get octeon config params */
#define CFG_GET_IQ_CFG(cfg) ((cfg)->iq)
#define CFG_GET_IQ_MAX_Q(cfg) ((cfg)->iq.max_iqs)
#define CFG_GET_IQ_PENDING_LIST_SIZE(cfg) ((cfg)->iq.pending_list_size)
#define CFG_GET_IQ_INSTR_TYPE(cfg) ((cfg)->iq.instr_type)
#define CFG_GET_IQ_DB_MIN(cfg) ((cfg)->iq.db_min)
#define CFG_GET_IQ_DB_TIMEOUT(cfg) ((cfg)->iq.db_timeout)
#define CFG_GET_IQ_INTR_PKT(cfg) ((cfg)->iq.iq_intr_pkt)
#define CFG_SET_IQ_INTR_PKT(cfg, val) (cfg)->iq.iq_intr_pkt = val
#define CFG_GET_OQ_MAX_Q(cfg) ((cfg)->oq.max_oqs)
#define CFG_GET_OQ_PKTS_PER_INTR(cfg) ((cfg)->oq.pkts_per_intr)
#define CFG_GET_OQ_REFILL_THRESHOLD(cfg) ((cfg)->oq.refill_threshold)
#define CFG_GET_OQ_INTR_PKT(cfg) ((cfg)->oq.oq_intr_pkt)
#define CFG_GET_OQ_INTR_TIME(cfg) ((cfg)->oq.oq_intr_time)
#define CFG_SET_OQ_INTR_PKT(cfg, val) (cfg)->oq.oq_intr_pkt = val
#define CFG_SET_OQ_INTR_TIME(cfg, val) (cfg)->oq.oq_intr_time = val
#define CFG_GET_DMA_INTR_PKT(cfg) ((cfg)->dma.dma_intr_pkt)
#define CFG_GET_DMA_INTR_TIME(cfg) ((cfg)->dma.dma_intr_time)
#define CFG_GET_NUM_NIC_PORTS(cfg) ((cfg)->num_nic_ports)
#define CFG_GET_NUM_DEF_TX_DESCS(cfg) ((cfg)->num_def_tx_descs)
#define CFG_GET_NUM_DEF_RX_DESCS(cfg) ((cfg)->num_def_rx_descs)
#define CFG_GET_DEF_RX_BUF_SIZE(cfg) ((cfg)->def_rx_buf_size)
#define CFG_GET_MAX_TXQS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].max_txqs)
#define CFG_GET_NUM_TXQS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].num_txqs)
#define CFG_GET_MAX_RXQS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].max_rxqs)
#define CFG_GET_NUM_RXQS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].num_rxqs)
#define CFG_GET_NUM_RX_DESCS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].num_rx_descs)
#define CFG_GET_NUM_TX_DESCS_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].num_tx_descs)
#define CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].rx_buf_size)
#define CFG_GET_BASE_QUE_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].base_queue)
#define CFG_GET_GMXID_NIC_IF(cfg, idx) \
((cfg)->nic_if_cfg[idx].gmx_port_id)
#define CFG_GET_CTRL_Q_GRP(cfg) ((cfg)->misc.ctrlq_grp)
#define CFG_GET_HOST_LINK_QUERY_INTERVAL(cfg) \
((cfg)->misc.host_link_query_interval)
#define CFG_GET_OCT_LINK_QUERY_INTERVAL(cfg) \
((cfg)->misc.oct_link_query_interval)
#define CFG_GET_IS_SLI_BP_ON(cfg) ((cfg)->misc.enable_sli_oq_bp)
#define CFG_SET_NUM_RX_DESCS_NIC_IF(cfg, idx, value) \
((cfg)->nic_if_cfg[idx].num_rx_descs = value)
#define CFG_SET_NUM_TX_DESCS_NIC_IF(cfg, idx, value) \
((cfg)->nic_if_cfg[idx].num_tx_descs = value)
/* Max IOQs per OCTEON Link */
#define MAX_IOQS_PER_NICIF 64
enum lio_card_type {
LIO_210SV = 0, /* Two port, 66xx */
LIO_210NV, /* Two port, 68xx */
LIO_410NV, /* Four port, 68xx */
LIO_23XX /* 23xx */
};
#define LIO_210SV_NAME "210sv"
#define LIO_210NV_NAME "210nv"
#define LIO_410NV_NAME "410nv"
#define LIO_23XX_NAME "23xx"
/** Structure to define the configuration attributes for each Input queue.
* Applicable to all Octeon processors
**/
struct octeon_iq_config {
#ifdef __BIG_ENDIAN_BITFIELD
u64 reserved:16;
/** Tx interrupt packets. Applicable to 23xx only */
u64 iq_intr_pkt:16;
/** Minimum ticks to wait before checking for pending instructions. */
u64 db_timeout:16;
/** Minimum number of commands pending to be posted to Octeon
* before driver hits the Input queue doorbell.
*/
u64 db_min:8;
/** Command size - 32 or 64 bytes */
u64 instr_type:32;
/** Pending list size (usually set to the sum of the size of all Input
* queues)
*/
u64 pending_list_size:32;
/* Max number of IQs available */
u64 max_iqs:8;
#else
/* Max number of IQs available */
u64 max_iqs:8;
/** Pending list size (usually set to the sum of the size of all Input
* queues)
*/
u64 pending_list_size:32;
/** Command size - 32 or 64 bytes */
u64 instr_type:32;
/** Minimum number of commands pending to be posted to Octeon
* before driver hits the Input queue doorbell.
*/
u64 db_min:8;
/** Minimum ticks to wait before checking for pending instructions. */
u64 db_timeout:16;
/** Tx interrupt packets. Applicable to 23xx only */
u64 iq_intr_pkt:16;
u64 reserved:16;
#endif
};
/** Structure to define the configuration attributes for each Output queue.
* Applicable to all Octeon processors
**/
struct octeon_oq_config {
#ifdef __BIG_ENDIAN_BITFIELD
u64 reserved:16;
u64 pkts_per_intr:16;
/** Interrupt Coalescing (Time Interval). Octeon will interrupt the
* host if atleast one packet was sent in the time interval specified
* by this field. The driver uses time interval interrupt coalescing
* by default. The time is specified in microseconds.
*/
u64 oq_intr_time:16;
/** Interrupt Coalescing (Packet Count). Octeon will interrupt the host
* only if it sent as many packets as specified by this field.
* The driver
* usually does not use packet count interrupt coalescing.
*/
u64 oq_intr_pkt:16;
/** The number of buffers that were consumed during packet processing by
* the driver on this Output queue before the driver attempts to
* replenish
* the descriptor ring with new buffers.
*/
u64 refill_threshold:16;
/* Max number of OQs available */
u64 max_oqs:8;
#else
/* Max number of OQs available */
u64 max_oqs:8;
/** The number of buffers that were consumed during packet processing by
* the driver on this Output queue before the driver attempts to
* replenish
* the descriptor ring with new buffers.
*/
u64 refill_threshold:16;
/** Interrupt Coalescing (Packet Count). Octeon will interrupt the host
* only if it sent as many packets as specified by this field.
* The driver
* usually does not use packet count interrupt coalescing.
*/
u64 oq_intr_pkt:16;
/** Interrupt Coalescing (Time Interval). Octeon will interrupt the
* host if atleast one packet was sent in the time interval specified
* by this field. The driver uses time interval interrupt coalescing
* by default. The time is specified in microseconds.
*/
u64 oq_intr_time:16;
u64 pkts_per_intr:16;
u64 reserved:16;
#endif
};
/** This structure conatins the NIC link configuration attributes,
* common for all the OCTEON Modles.
*/
struct octeon_nic_if_config {
#ifdef __BIG_ENDIAN_BITFIELD
u64 reserved:56;
u64 base_queue:16;
u64 gmx_port_id:8;
/* SKB size, We need not change buf size even for Jumbo frames.
* Octeon can send jumbo frames in 4 consecutive descriptors,
*/
u64 rx_buf_size:16;
/* Num of desc for tx rings */
u64 num_tx_descs:16;
/* Num of desc for rx rings */
u64 num_rx_descs:16;
/* Actual configured value. Range could be: 1...max_rxqs */
u64 num_rxqs:16;
/* Max Rxqs: Half for each of the two ports :max_oq/2 */
u64 max_rxqs:16;
/* Actual configured value. Range could be: 1...max_txqs */
u64 num_txqs:16;
/* Max Txqs: Half for each of the two ports :max_iq/2 */
u64 max_txqs:16;
#else
/* Max Txqs: Half for each of the two ports :max_iq/2 */
u64 max_txqs:16;
/* Actual configured value. Range could be: 1...max_txqs */
u64 num_txqs:16;
/* Max Rxqs: Half for each of the two ports :max_oq/2 */
u64 max_rxqs:16;
/* Actual configured value. Range could be: 1...max_rxqs */
u64 num_rxqs:16;
/* Num of desc for rx rings */
u64 num_rx_descs:16;
/* Num of desc for tx rings */
u64 num_tx_descs:16;
/* SKB size, We need not change buf size even for Jumbo frames.
* Octeon can send jumbo frames in 4 consecutive descriptors,
*/
u64 rx_buf_size:16;
u64 gmx_port_id:8;
u64 base_queue:16;
u64 reserved:56;
#endif
};
/** Structure to define the configuration attributes for meta data.
* Applicable to all Octeon processors.
*/
struct octeon_misc_config {
#ifdef __BIG_ENDIAN_BITFIELD
/** Host link status polling period */
u64 host_link_query_interval:32;
/** Oct link status polling period */
u64 oct_link_query_interval:32;
u64 enable_sli_oq_bp:1;
/** Control IQ Group */
u64 ctrlq_grp:4;
#else
/** Control IQ Group */
u64 ctrlq_grp:4;
/** BP for SLI OQ */
u64 enable_sli_oq_bp:1;
/** Host link status polling period */
u64 oct_link_query_interval:32;
/** Oct link status polling period */
u64 host_link_query_interval:32;
#endif
};
/** Structure to define the configuration for all OCTEON processors. */
struct octeon_config {
u16 card_type;
char *card_name;
/** Input Queue attributes. */
struct octeon_iq_config iq;
/** Output Queue attributes. */
struct octeon_oq_config oq;
/** NIC Port Configuration */
struct octeon_nic_if_config nic_if_cfg[MAX_OCTEON_NICIF];
/** Miscellaneous attributes */
struct octeon_misc_config misc;
int num_nic_ports;
int num_def_tx_descs;
/* Num of desc for rx rings */
int num_def_rx_descs;
int def_rx_buf_size;
};
/* The following config values are fixed and should not be modified. */
#define BAR1_INDEX_DYNAMIC_MAP 2
#define BAR1_INDEX_STATIC_MAP 15
#define OCTEON_BAR1_ENTRY_SIZE (4 * 1024 * 1024)
#define MAX_BAR1_IOREMAP_SIZE (16 * OCTEON_BAR1_ENTRY_SIZE)
/* Response lists - 1 ordered, 1 unordered-blocking, 1 unordered-nonblocking
* 1 process done list, 1 zombie lists(timeouted sc list)
* NoResponse Lists are now maintained with each IQ. (Dec' 2007).
*/
#define MAX_RESPONSE_LISTS 6
/* Opcode hash bits. The opcode is hashed on the lower 6-bits to lookup the
* dispatch table.
*/
#define OPCODE_MASK_BITS 6
/* Mask for the 6-bit lookup hash */
#define OCTEON_OPCODE_MASK 0x3f
/* Size of the dispatch table. The 6-bit hash can index into 2^6 entries */
#define DISPATCH_LIST_SIZE BIT(OPCODE_MASK_BITS)
/* Maximum number of Octeon Instruction (command) queues */
#define MAX_OCTEON_INSTR_QUEUES(oct) \
(OCTEON_CN23XX_PF(oct) ? CN23XX_MAX_INPUT_QUEUES : \
CN6XXX_MAX_INPUT_QUEUES)
/* Maximum number of Octeon Instruction (command) queues */
#define MAX_OCTEON_OUTPUT_QUEUES(oct) \
(OCTEON_CN23XX_PF(oct) ? CN23XX_MAX_OUTPUT_QUEUES : \
CN6XXX_MAX_OUTPUT_QUEUES)
#define MAX_POSSIBLE_OCTEON_INSTR_QUEUES CN23XX_MAX_INPUT_QUEUES
#define MAX_POSSIBLE_OCTEON_OUTPUT_QUEUES CN23XX_MAX_OUTPUT_QUEUES
#define MAX_POSSIBLE_VFS 64
#endif /* __OCTEON_CONFIG_H__ */
@@ -0,0 +1,919 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*
* @file octeon_console.c
*/
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/crc32.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "liquidio_image.h"
#include "octeon_mem_ops.h"
static void octeon_remote_lock(void);
static void octeon_remote_unlock(void);
static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct,
const char *name,
u32 flags);
static int octeon_console_read(struct octeon_device *oct, u32 console_num,
char *buffer, u32 buf_size);
#define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008
#define BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR 0x0006c004
#define BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR 0x0006c000
#define BOOTLOADER_PCI_READ_DESC_ADDR 0x0006c100
#define BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN 248
#define OCTEON_PCI_IO_BUF_OWNER_OCTEON 0x00000001
#define OCTEON_PCI_IO_BUF_OWNER_HOST 0x00000002
/** Can change without breaking ABI */
#define CVMX_BOOTMEM_NUM_NAMED_BLOCKS 64
/** minimum alignment of bootmem alloced blocks */
#define CVMX_BOOTMEM_ALIGNMENT_SIZE (16ull)
/** CVMX bootmem descriptor major version */
#define CVMX_BOOTMEM_DESC_MAJ_VER 3
/* CVMX bootmem descriptor minor version */
#define CVMX_BOOTMEM_DESC_MIN_VER 0
/* Current versions */
#define OCTEON_PCI_CONSOLE_MAJOR_VERSION 1
#define OCTEON_PCI_CONSOLE_MINOR_VERSION 0
#define OCTEON_PCI_CONSOLE_BLOCK_NAME "__pci_console"
#define OCTEON_CONSOLE_POLL_INTERVAL_MS 100 /* 10 times per second */
/* First three members of cvmx_bootmem_desc are left in original
* positions for backwards compatibility.
* Assumes big endian target
*/
struct cvmx_bootmem_desc {
/** spinlock to control access to list */
u32 lock;
/** flags for indicating various conditions */
u32 flags;
u64 head_addr;
/** incremented changed when incompatible changes made */
u32 major_version;
/** incremented changed when compatible changes made,
* reset to zero when major incremented
*/
u32 minor_version;
u64 app_data_addr;
u64 app_data_size;
/** number of elements in named blocks array */
u32 nb_num_blocks;
/** length of name array in bootmem blocks */
u32 named_block_name_len;
/** address of named memory block descriptors */
u64 named_block_array_addr;
};
/* Structure that defines a single console.
*
* Note: when read_index == write_index, the buffer is empty.
* The actual usable size of each console is console_buf_size -1;
*/
struct octeon_pci_console {
u64 input_base_addr;
u32 input_read_index;
u32 input_write_index;
u64 output_base_addr;
u32 output_read_index;
u32 output_write_index;
u32 lock;
u32 buf_size;
};
/* This is the main container structure that contains all the information
* about all PCI consoles. The address of this structure is passed to various
* routines that operation on PCI consoles.
*/
struct octeon_pci_console_desc {
u32 major_version;
u32 minor_version;
u32 lock;
u32 flags;
u32 num_consoles;
u32 pad;
/* must be 64 bit aligned here... */
/* Array of addresses of octeon_pci_console structures */
u64 console_addr_array[];
/* Implicit storage for console_addr_array */
};
/*
* This function is the implementation of the get macros defined
* for individual structure members. The argument are generated
* by the macros inorder to read only the needed memory.
*
* @param oct Pointer to current octeon device
* @param base 64bit physical address of the complete structure
* @param offset Offset from the beginning of the structure to the member being
* accessed.
* @param size Size of the structure member.
*
* @return Value of the structure member promoted into a u64.
*/
static inline u64 __cvmx_bootmem_desc_get(struct octeon_device *oct,
u64 base,
u32 offset,
u32 size)
{
base = (1ull << 63) | (base + offset);
switch (size) {
case 4:
return octeon_read_device_mem32(oct, base);
case 8:
return octeon_read_device_mem64(oct, base);
default:
return 0;
}
}
/*
* This function retrieves the string name of a named block. It is
* more complicated than a simple memcpy() since the named block
* descriptor may not be directly accessible.
*
* @param addr Physical address of the named block descriptor
* @param str String to receive the named block string name
* @param len Length of the string buffer, which must match the length
* stored in the bootmem descriptor.
*/
static void CVMX_BOOTMEM_NAMED_GET_NAME(struct octeon_device *oct,
u64 addr,
char *str,
u32 len)
{
addr += offsetof(struct cvmx_bootmem_named_block_desc, name);
octeon_pci_read_core_mem(oct, addr, (u8 *)str, len);
str[len] = 0;
}
/* See header file for descriptions of functions */
/*
* Check the version information on the bootmem descriptor
*
* @param exact_match
* Exact major version to check against. A zero means
* check that the version supports named blocks.
*
* @return Zero if the version is correct. Negative if the version is
* incorrect. Failures also cause a message to be displayed.
*/
static int __cvmx_bootmem_check_version(struct octeon_device *oct,
u32 exact_match)
{
u32 major_version;
u32 minor_version;
if (!oct->bootmem_desc_addr)
oct->bootmem_desc_addr =
octeon_read_device_mem64(oct,
BOOTLOADER_PCI_READ_DESC_ADDR);
major_version = (u32)__cvmx_bootmem_desc_get(
oct, oct->bootmem_desc_addr,
offsetof(struct cvmx_bootmem_desc, major_version),
sizeof_field(struct cvmx_bootmem_desc, major_version));
minor_version = (u32)__cvmx_bootmem_desc_get(
oct, oct->bootmem_desc_addr,
offsetof(struct cvmx_bootmem_desc, minor_version),
sizeof_field(struct cvmx_bootmem_desc, minor_version));
dev_dbg(&oct->pci_dev->dev, "%s: major_version=%d\n", __func__,
major_version);
if ((major_version > 3) ||
(exact_match && major_version != exact_match)) {
dev_err(&oct->pci_dev->dev, "bootmem ver mismatch %d.%d addr:0x%llx\n",
major_version, minor_version,
(long long)oct->bootmem_desc_addr);
return -1;
} else {
return 0;
}
}
static const struct cvmx_bootmem_named_block_desc
*__cvmx_bootmem_find_named_block_flags(struct octeon_device *oct,
const char *name, u32 flags)
{
struct cvmx_bootmem_named_block_desc *desc =
&oct->bootmem_named_block_desc;
u64 named_addr = cvmx_bootmem_phy_named_block_find(oct, name, flags);
if (named_addr) {
desc->base_addr = __cvmx_bootmem_desc_get(
oct, named_addr,
offsetof(struct cvmx_bootmem_named_block_desc,
base_addr),
sizeof_field(
struct cvmx_bootmem_named_block_desc,
base_addr));
desc->size = __cvmx_bootmem_desc_get(oct, named_addr,
offsetof(struct cvmx_bootmem_named_block_desc,
size),
sizeof_field(
struct cvmx_bootmem_named_block_desc,
size));
strscpy(desc->name, name, sizeof(desc->name));
return &oct->bootmem_named_block_desc;
} else {
return NULL;
}
}
static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct,
const char *name,
u32 flags)
{
u64 result = 0;
if (!__cvmx_bootmem_check_version(oct, 3)) {
u32 i;
u64 named_block_array_addr = __cvmx_bootmem_desc_get(
oct, oct->bootmem_desc_addr,
offsetof(struct cvmx_bootmem_desc,
named_block_array_addr),
sizeof_field(struct cvmx_bootmem_desc,
named_block_array_addr));
u32 num_blocks = (u32)__cvmx_bootmem_desc_get(
oct, oct->bootmem_desc_addr,
offsetof(struct cvmx_bootmem_desc,
nb_num_blocks),
sizeof_field(struct cvmx_bootmem_desc,
nb_num_blocks));
u32 name_length = (u32)__cvmx_bootmem_desc_get(
oct, oct->bootmem_desc_addr,
offsetof(struct cvmx_bootmem_desc,
named_block_name_len),
sizeof_field(struct cvmx_bootmem_desc,
named_block_name_len));
u64 named_addr = named_block_array_addr;
for (i = 0; i < num_blocks; i++) {
u64 named_size = __cvmx_bootmem_desc_get(
oct, named_addr,
offsetof(
struct cvmx_bootmem_named_block_desc,
size),
sizeof_field(
struct cvmx_bootmem_named_block_desc,
size));
if (name && named_size) {
char *name_tmp =
kmalloc(name_length + 1, GFP_KERNEL);
if (!name_tmp)
break;
CVMX_BOOTMEM_NAMED_GET_NAME(oct, named_addr,
name_tmp,
name_length);
if (!strncmp(name, name_tmp, name_length)) {
result = named_addr;
kfree(name_tmp);
break;
}
kfree(name_tmp);
} else if (!name && !named_size) {
result = named_addr;
break;
}
named_addr +=
sizeof(struct cvmx_bootmem_named_block_desc);
}
}
return result;
}
/*
* Find a named block on the remote Octeon
*
* @param name Name of block to find
* @param base_addr Address the block is at (OUTPUT)
* @param size The size of the block (OUTPUT)
*
* @return Zero on success, One on failure.
*/
static int octeon_named_block_find(struct octeon_device *oct, const char *name,
u64 *base_addr, u64 *size)
{
const struct cvmx_bootmem_named_block_desc *named_block;
octeon_remote_lock();
named_block = __cvmx_bootmem_find_named_block_flags(oct, name, 0);
octeon_remote_unlock();
if (named_block) {
*base_addr = named_block->base_addr;
*size = named_block->size;
return 0;
}
return 1;
}
static void octeon_remote_lock(void)
{
/* fill this in if any sharing is needed */
}
static void octeon_remote_unlock(void)
{
/* fill this in if any sharing is needed */
}
int octeon_console_send_cmd(struct octeon_device *oct, char *cmd_str,
u32 wait_hundredths)
{
u32 len = (u32)strlen(cmd_str);
dev_dbg(&oct->pci_dev->dev, "sending \"%s\" to bootloader\n", cmd_str);
if (len > BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN - 1) {
dev_err(&oct->pci_dev->dev, "Command string too long, max length is: %d\n",
BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN - 1);
return -1;
}
if (octeon_wait_for_bootloader(oct, wait_hundredths) != 0) {
dev_err(&oct->pci_dev->dev, "Bootloader not ready for command.\n");
return -1;
}
/* Write command to bootloader */
octeon_remote_lock();
octeon_pci_write_core_mem(oct, BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR,
(u8 *)cmd_str, len);
octeon_write_device_mem32(oct, BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR,
len);
octeon_write_device_mem32(oct, BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR,
OCTEON_PCI_IO_BUF_OWNER_OCTEON);
/* Bootloader should accept command very quickly
* if it really was ready
*/
if (octeon_wait_for_bootloader(oct, 200) != 0) {
octeon_remote_unlock();
dev_err(&oct->pci_dev->dev, "Bootloader did not accept command.\n");
return -1;
}
octeon_remote_unlock();
return 0;
}
int octeon_wait_for_bootloader(struct octeon_device *oct,
u32 wait_time_hundredths)
{
dev_dbg(&oct->pci_dev->dev, "waiting %d0 ms for bootloader\n",
wait_time_hundredths);
if (octeon_mem_access_ok(oct))
return -1;
while (wait_time_hundredths > 0 &&
octeon_read_device_mem32(oct,
BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR)
!= OCTEON_PCI_IO_BUF_OWNER_HOST) {
if (--wait_time_hundredths <= 0)
return -1;
schedule_timeout_uninterruptible(HZ / 100);
}
return 0;
}
static void octeon_console_handle_result(struct octeon_device *oct,
size_t console_num)
{
struct octeon_console *console;
console = &oct->console[console_num];
console->waiting = 0;
}
static char console_buffer[OCTEON_CONSOLE_MAX_READ_BYTES];
static void output_console_line(struct octeon_device *oct,
struct octeon_console *console,
size_t console_num,
char *console_buffer,
s32 bytes_read)
{
char *line;
s32 i;
size_t len;
line = console_buffer;
for (i = 0; i < bytes_read; i++) {
/* Output a line at a time, prefixed */
if (console_buffer[i] == '\n') {
console_buffer[i] = '\0';
/* We need to output 'line', prefaced by 'leftover'.
* However, it is possible we're being called to
* output 'leftover' by itself (in the case of nothing
* having been read from the console).
*
* To avoid duplication, check for this condition.
*/
if (console->leftover[0] &&
(line != console->leftover)) {
if (console->print)
(*console->print)(oct, (u32)console_num,
console->leftover,
line);
console->leftover[0] = '\0';
} else {
if (console->print)
(*console->print)(oct, (u32)console_num,
line, NULL);
}
line = &console_buffer[i + 1];
}
}
/* Save off any leftovers */
if (line != &console_buffer[bytes_read]) {
console_buffer[bytes_read] = '\0';
len = strlen(console->leftover);
strscpy(&console->leftover[len], line,
sizeof(console->leftover) - len + 1);
}
}
static void check_console(struct work_struct *work)
{
s32 bytes_read, tries, total_read;
size_t len;
struct octeon_console *console;
struct cavium_wk *wk = (struct cavium_wk *)work;
struct octeon_device *oct = (struct octeon_device *)wk->ctxptr;
u32 console_num = (u32)wk->ctxul;
u32 delay;
console = &oct->console[console_num];
tries = 0;
total_read = 0;
do {
/* Take console output regardless of whether it will
* be logged
*/
bytes_read =
octeon_console_read(oct, console_num, console_buffer,
sizeof(console_buffer) - 1);
if (bytes_read > 0) {
total_read += bytes_read;
if (console->waiting)
octeon_console_handle_result(oct, console_num);
if (console->print) {
output_console_line(oct, console, console_num,
console_buffer, bytes_read);
}
} else if (bytes_read < 0) {
dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n",
console_num, bytes_read);
}
tries++;
} while ((bytes_read > 0) && (tries < 16));
/* If nothing is read after polling the console,
* output any leftovers if any
*/
if (console->print && (total_read == 0) &&
(console->leftover[0])) {
/* append '\n' as terminator for 'output_console_line' */
len = strlen(console->leftover);
console->leftover[len] = '\n';
output_console_line(oct, console, console_num,
console->leftover, (s32)(len + 1));
console->leftover[0] = '\0';
}
delay = OCTEON_CONSOLE_POLL_INTERVAL_MS;
schedule_delayed_work(&wk->work, msecs_to_jiffies(delay));
}
int octeon_init_consoles(struct octeon_device *oct)
{
int ret = 0;
u64 addr, size;
ret = octeon_mem_access_ok(oct);
if (ret) {
dev_err(&oct->pci_dev->dev, "Memory access not okay'\n");
return ret;
}
ret = octeon_named_block_find(oct, OCTEON_PCI_CONSOLE_BLOCK_NAME, &addr,
&size);
if (ret) {
dev_err(&oct->pci_dev->dev, "Could not find console '%s'\n",
OCTEON_PCI_CONSOLE_BLOCK_NAME);
return ret;
}
/* Dedicate one of Octeon's BAR1 index registers to create a static
* mapping to a region of Octeon DRAM that contains the PCI console
* named block.
*/
oct->console_nb_info.bar1_index = BAR1_INDEX_STATIC_MAP;
oct->fn_list.bar1_idx_setup(oct, addr, oct->console_nb_info.bar1_index,
true);
oct->console_nb_info.dram_region_base = addr
& ~(OCTEON_BAR1_ENTRY_SIZE - 1ULL);
/* num_consoles > 0, is an indication that the consoles
* are accessible
*/
oct->num_consoles = octeon_read_device_mem32(oct,
addr + offsetof(struct octeon_pci_console_desc,
num_consoles));
oct->console_desc_addr = addr;
dev_dbg(&oct->pci_dev->dev, "Initialized consoles. %d available\n",
oct->num_consoles);
return ret;
}
static void octeon_get_uboot_version(struct octeon_device *oct)
{
s32 bytes_read, tries, total_read;
struct octeon_console *console;
u32 console_num = 0;
char *uboot_ver;
char *buf;
char *p;
#define OCTEON_UBOOT_VER_BUF_SIZE 512
buf = kmalloc(OCTEON_UBOOT_VER_BUF_SIZE, GFP_KERNEL);
if (!buf)
return;
if (octeon_console_send_cmd(oct, "setenv stdout pci\n", 50)) {
kfree(buf);
return;
}
if (octeon_console_send_cmd(oct, "version\n", 1)) {
kfree(buf);
return;
}
console = &oct->console[console_num];
tries = 0;
total_read = 0;
do {
/* Take console output regardless of whether it will
* be logged
*/
bytes_read =
octeon_console_read(oct,
console_num, buf + total_read,
OCTEON_UBOOT_VER_BUF_SIZE - 1 -
total_read);
if (bytes_read > 0) {
buf[bytes_read] = '\0';
total_read += bytes_read;
if (console->waiting)
octeon_console_handle_result(oct, console_num);
} else if (bytes_read < 0) {
dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n",
console_num, bytes_read);
}
tries++;
} while ((bytes_read > 0) && (tries < 16));
/* If nothing is read after polling the console,
* output any leftovers if any
*/
if ((total_read == 0) && (console->leftover[0])) {
dev_dbg(&oct->pci_dev->dev, "%u: %s\n",
console_num, console->leftover);
console->leftover[0] = '\0';
}
buf[OCTEON_UBOOT_VER_BUF_SIZE - 1] = '\0';
uboot_ver = strstr(buf, "U-Boot");
if (uboot_ver) {
p = strstr(uboot_ver, "mips");
if (p) {
p--;
*p = '\0';
dev_info(&oct->pci_dev->dev, "%s\n", uboot_ver);
}
}
kfree(buf);
octeon_console_send_cmd(oct, "setenv stdout serial\n", 50);
}
int octeon_add_console(struct octeon_device *oct, u32 console_num,
char *dbg_enb)
{
int ret = 0;
u32 delay;
u64 coreaddr;
struct delayed_work *work;
struct octeon_console *console;
if (console_num >= oct->num_consoles) {
dev_err(&oct->pci_dev->dev,
"trying to read from console number %d when only 0 to %d exist\n",
console_num, oct->num_consoles);
} else {
console = &oct->console[console_num];
console->waiting = 0;
coreaddr = oct->console_desc_addr + console_num * 8 +
offsetof(struct octeon_pci_console_desc,
console_addr_array);
console->addr = octeon_read_device_mem64(oct, coreaddr);
coreaddr = console->addr + offsetof(struct octeon_pci_console,
buf_size);
console->buffer_size = octeon_read_device_mem32(oct, coreaddr);
coreaddr = console->addr + offsetof(struct octeon_pci_console,
input_base_addr);
console->input_base_addr =
octeon_read_device_mem64(oct, coreaddr);
coreaddr = console->addr + offsetof(struct octeon_pci_console,
output_base_addr);
console->output_base_addr =
octeon_read_device_mem64(oct, coreaddr);
console->leftover[0] = '\0';
work = &oct->console_poll_work[console_num].work;
octeon_get_uboot_version(oct);
INIT_DELAYED_WORK(work, check_console);
oct->console_poll_work[console_num].ctxptr = (void *)oct;
oct->console_poll_work[console_num].ctxul = console_num;
delay = OCTEON_CONSOLE_POLL_INTERVAL_MS;
schedule_delayed_work(work, msecs_to_jiffies(delay));
/* an empty string means use default debug console enablement */
if (dbg_enb && !dbg_enb[0])
dbg_enb = "setenv pci_console_active 1";
if (dbg_enb)
ret = octeon_console_send_cmd(oct, dbg_enb, 2000);
console->active = 1;
}
return ret;
}
/*
* Removes all consoles
*
* @param oct octeon device
*/
void octeon_remove_consoles(struct octeon_device *oct)
{
u32 i;
struct octeon_console *console;
for (i = 0; i < oct->num_consoles; i++) {
console = &oct->console[i];
if (!console->active)
continue;
cancel_delayed_work_sync(&oct->console_poll_work[i].
work);
console->addr = 0;
console->buffer_size = 0;
console->input_base_addr = 0;
console->output_base_addr = 0;
}
oct->num_consoles = 0;
}
static inline int octeon_console_free_bytes(u32 buffer_size,
u32 wr_idx,
u32 rd_idx)
{
if (rd_idx >= buffer_size || wr_idx >= buffer_size)
return -1;
return ((buffer_size - 1) - (wr_idx - rd_idx)) % buffer_size;
}
static inline int octeon_console_avail_bytes(u32 buffer_size,
u32 wr_idx,
u32 rd_idx)
{
if (rd_idx >= buffer_size || wr_idx >= buffer_size)
return -1;
return buffer_size - 1 -
octeon_console_free_bytes(buffer_size, wr_idx, rd_idx);
}
static int octeon_console_read(struct octeon_device *oct, u32 console_num,
char *buffer, u32 buf_size)
{
int bytes_to_read;
u32 rd_idx, wr_idx;
struct octeon_console *console;
if (console_num >= oct->num_consoles) {
dev_err(&oct->pci_dev->dev, "Attempted to read from disabled console %d\n",
console_num);
return 0;
}
console = &oct->console[console_num];
/* Check to see if any data is available.
* Maybe optimize this with 64-bit read.
*/
rd_idx = octeon_read_device_mem32(oct, console->addr +
offsetof(struct octeon_pci_console, output_read_index));
wr_idx = octeon_read_device_mem32(oct, console->addr +
offsetof(struct octeon_pci_console, output_write_index));
bytes_to_read = octeon_console_avail_bytes(console->buffer_size,
wr_idx, rd_idx);
if (bytes_to_read <= 0)
return bytes_to_read;
bytes_to_read = min_t(s32, bytes_to_read, buf_size);
/* Check to see if what we want to read is not contiguous, and limit
* ourselves to the contiguous block
*/
if (rd_idx + bytes_to_read >= console->buffer_size)
bytes_to_read = console->buffer_size - rd_idx;
octeon_pci_read_core_mem(oct, console->output_base_addr + rd_idx,
(u8 *)buffer, bytes_to_read);
octeon_write_device_mem32(oct, console->addr +
offsetof(struct octeon_pci_console,
output_read_index),
(rd_idx + bytes_to_read) %
console->buffer_size);
return bytes_to_read;
}
#define FBUF_SIZE (4 * 1024 * 1024)
#define MAX_BOOTTIME_SIZE 80
int octeon_download_firmware(struct octeon_device *oct, const u8 *data,
size_t size)
{
struct octeon_firmware_file_header *h;
char boottime[MAX_BOOTTIME_SIZE];
struct timespec64 ts;
u32 crc32_result;
u64 load_addr;
u32 image_len;
int ret = 0;
u32 i, rem;
if (size < sizeof(struct octeon_firmware_file_header)) {
dev_err(&oct->pci_dev->dev, "Firmware file too small (%d < %d).\n",
(u32)size,
(u32)sizeof(struct octeon_firmware_file_header));
return -EINVAL;
}
h = (struct octeon_firmware_file_header *)data;
if (be32_to_cpu(h->magic) != LIO_NIC_MAGIC) {
dev_err(&oct->pci_dev->dev, "Unrecognized firmware file.\n");
return -EINVAL;
}
crc32_result = crc32((unsigned int)~0, data,
sizeof(struct octeon_firmware_file_header) -
sizeof(u32)) ^ ~0U;
if (crc32_result != be32_to_cpu(h->crc32)) {
dev_err(&oct->pci_dev->dev, "Firmware CRC mismatch (0x%08x != 0x%08x).\n",
crc32_result, be32_to_cpu(h->crc32));
return -EINVAL;
}
if (memcmp(LIQUIDIO_BASE_VERSION, h->version,
strlen(LIQUIDIO_BASE_VERSION))) {
dev_err(&oct->pci_dev->dev, "Unmatched firmware version. Expected %s.x, got %s.\n",
LIQUIDIO_BASE_VERSION,
h->version);
return -EINVAL;
}
if (be32_to_cpu(h->num_images) > LIO_MAX_IMAGES) {
dev_err(&oct->pci_dev->dev, "Too many images in firmware file (%d).\n",
be32_to_cpu(h->num_images));
return -EINVAL;
}
dev_info(&oct->pci_dev->dev, "Firmware version: %s\n", h->version);
snprintf(oct->fw_info.liquidio_firmware_version, 32, "LIQUIDIO: %s",
h->version);
data += sizeof(struct octeon_firmware_file_header);
dev_info(&oct->pci_dev->dev, "%s: Loading %d images\n", __func__,
be32_to_cpu(h->num_images));
/* load all images */
for (i = 0; i < be32_to_cpu(h->num_images); i++) {
load_addr = be64_to_cpu(h->desc[i].addr);
image_len = be32_to_cpu(h->desc[i].len);
dev_info(&oct->pci_dev->dev, "Loading firmware %d at %llx\n",
image_len, load_addr);
/* Write in 4MB chunks*/
rem = image_len;
while (rem) {
if (rem < FBUF_SIZE)
size = rem;
else
size = FBUF_SIZE;
/* download the image */
octeon_pci_write_core_mem(oct, load_addr, data, (u32)size);
data += size;
rem -= (u32)size;
load_addr += size;
}
}
/* Pass date and time information to NIC at the time of loading
* firmware and periodically update the host time to NIC firmware.
* This is to make NIC firmware use the same time reference as Host,
* so that it is easy to correlate logs from firmware and host for
* debugging.
*
* Octeon always uses UTC time. so timezone information is not sent.
*/
ktime_get_real_ts64(&ts);
ret = snprintf(boottime, MAX_BOOTTIME_SIZE,
" time_sec=%lld time_nsec=%ld",
(s64)ts.tv_sec, ts.tv_nsec);
if ((sizeof(h->bootcmd) - strnlen(h->bootcmd, sizeof(h->bootcmd))) <
ret) {
dev_err(&oct->pci_dev->dev, "Boot command buffer too small\n");
return -EINVAL;
}
strncat(h->bootcmd, boottime,
sizeof(h->bootcmd) - strnlen(h->bootcmd, sizeof(h->bootcmd)));
dev_info(&oct->pci_dev->dev, "Writing boot command: %s\n",
h->bootcmd);
/* Invoke the bootcmd */
ret = octeon_console_send_cmd(oct, h->bootcmd, 50);
if (ret)
dev_info(&oct->pci_dev->dev, "Boot command send failed\n");
return ret;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,894 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file octeon_device.h
* \brief Host Driver: This file defines the octeon device structure.
*/
#ifndef _OCTEON_DEVICE_H_
#define _OCTEON_DEVICE_H_
#include <linux/interrupt.h>
#include <net/devlink.h>
/** PCI VendorId Device Id */
#define OCTEON_CN68XX_PCIID 0x91177d
#define OCTEON_CN66XX_PCIID 0x92177d
#define OCTEON_CN23XX_PCIID_PF 0x9702177d
/** Driver identifies chips by these Ids, created by clubbing together
* DeviceId+RevisionId; Where Revision Id is not used to distinguish
* between chips, a value of 0 is used for revision id.
*/
#define OCTEON_CN68XX 0x0091
#define OCTEON_CN66XX 0x0092
#define OCTEON_CN23XX_PF_VID 0x9702
#define OCTEON_CN23XX_VF_VID 0x9712
/**RevisionId for the chips */
#define OCTEON_CN23XX_REV_1_0 0x00
#define OCTEON_CN23XX_REV_1_1 0x01
#define OCTEON_CN23XX_REV_2_0 0x80
/**SubsystemId for the chips */
#define OCTEON_CN2350_10GB_SUBSYS_ID_1 0X3177d
#define OCTEON_CN2350_10GB_SUBSYS_ID_2 0X4177d
#define OCTEON_CN2360_10GB_SUBSYS_ID 0X5177d
#define OCTEON_CN2350_25GB_SUBSYS_ID 0X7177d
#define OCTEON_CN2360_25GB_SUBSYS_ID 0X6177d
/** Endian-swap modes supported by Octeon. */
enum octeon_pci_swap_mode {
OCTEON_PCI_PASSTHROUGH = 0,
OCTEON_PCI_64BIT_SWAP = 1,
OCTEON_PCI_32BIT_BYTE_SWAP = 2,
OCTEON_PCI_32BIT_LW_SWAP = 3
};
enum lio_fw_state {
FW_IS_PRELOADED = 0,
FW_NEEDS_TO_BE_LOADED = 1,
FW_IS_BEING_LOADED = 2,
FW_HAS_BEEN_LOADED = 3,
};
enum {
OCTEON_CONFIG_TYPE_DEFAULT = 0,
NUM_OCTEON_CONFS,
};
#define OCTEON_INPUT_INTR (1)
#define OCTEON_OUTPUT_INTR (2)
#define OCTEON_MBOX_INTR (4)
#define OCTEON_ALL_INTR 0xff
/*--------------- PCI BAR1 index registers -------------*/
/* BAR1 Mask */
#define PCI_BAR1_ENABLE_CA 1
#define PCI_BAR1_ENDIAN_MODE OCTEON_PCI_64BIT_SWAP
#define PCI_BAR1_ENTRY_VALID 1
#define PCI_BAR1_MASK ((PCI_BAR1_ENABLE_CA << 3) \
| (PCI_BAR1_ENDIAN_MODE << 1) \
| PCI_BAR1_ENTRY_VALID)
/** Octeon Device state.
* Each octeon device goes through each of these states
* as it is initialized.
*/
#define OCT_DEV_BEGIN_STATE 0x0
#define OCT_DEV_PCI_ENABLE_DONE 0x1
#define OCT_DEV_PCI_MAP_DONE 0x2
#define OCT_DEV_DISPATCH_INIT_DONE 0x3
#define OCT_DEV_INSTR_QUEUE_INIT_DONE 0x4
#define OCT_DEV_SC_BUFF_POOL_INIT_DONE 0x5
#define OCT_DEV_RESP_LIST_INIT_DONE 0x6
#define OCT_DEV_DROQ_INIT_DONE 0x7
#define OCT_DEV_MBOX_SETUP_DONE 0x8
#define OCT_DEV_MSIX_ALLOC_VECTOR_DONE 0x9
#define OCT_DEV_INTR_SET_DONE 0xa
#define OCT_DEV_IO_QUEUES_DONE 0xb
#define OCT_DEV_CONSOLE_INIT_DONE 0xc
#define OCT_DEV_HOST_OK 0xd
#define OCT_DEV_CORE_OK 0xe
#define OCT_DEV_RUNNING 0xf
#define OCT_DEV_IN_RESET 0x10
#define OCT_DEV_STATE_INVALID 0x11
#define OCT_DEV_STATES OCT_DEV_STATE_INVALID
/** Octeon Device interrupts
* These interrupt bits are set in int_status filed of
* octeon_device structure
*/
#define OCT_DEV_INTR_DMA0_FORCE 0x01
#define OCT_DEV_INTR_DMA1_FORCE 0x02
#define OCT_DEV_INTR_PKT_DATA 0x04
#define LIO_RESET_SECS (3)
/*---------------------------DISPATCH LIST-------------------------------*/
/** The dispatch list entry.
* The driver keeps a record of functions registered for each
* response header opcode in this structure. Since the opcode is
* hashed to index into the driver's list, more than one opcode
* can hash to the same entry, in which case the list field points
* to a linked list with the other entries.
*/
struct octeon_dispatch {
/** List head for this entry */
struct list_head list;
/** The opcode for which the dispatch function & arg should be used */
u16 opcode;
/** The function to be called for a packet received by the driver */
octeon_dispatch_fn_t dispatch_fn;
/* The application specified argument to be passed to the above
* function along with the received packet
*/
void *arg;
};
/** The dispatch list structure. */
struct octeon_dispatch_list {
/** access to dispatch list must be atomic */
spinlock_t lock;
/** Count of dispatch functions currently registered */
u32 count;
/** The list of dispatch functions */
struct octeon_dispatch *dlist;
};
/*----------------------- THE OCTEON DEVICE ---------------------------*/
#define OCT_MEM_REGIONS 3
/** PCI address space mapping information.
* Each of the 3 address spaces given by BAR0, BAR2 and BAR4 of
* Octeon gets mapped to different physical address spaces in
* the kernel.
*/
struct octeon_mmio {
/** PCI address to which the BAR is mapped. */
u64 start;
/** Length of this PCI address space. */
u32 len;
/** Length that has been mapped to phys. address space. */
u32 mapped_len;
/** The physical address to which the PCI address space is mapped. */
u8 __iomem *hw_addr;
/** Flag indicating the mapping was successful. */
u32 done;
};
#define MAX_OCTEON_MAPS 32
struct octeon_io_enable {
u64 iq;
u64 oq;
u64 iq64B;
};
struct octeon_reg_list {
u32 __iomem *pci_win_wr_addr_hi;
u32 __iomem *pci_win_wr_addr_lo;
u64 __iomem *pci_win_wr_addr;
u32 __iomem *pci_win_rd_addr_hi;
u32 __iomem *pci_win_rd_addr_lo;
u64 __iomem *pci_win_rd_addr;
u32 __iomem *pci_win_wr_data_hi;
u32 __iomem *pci_win_wr_data_lo;
u64 __iomem *pci_win_wr_data;
u32 __iomem *pci_win_rd_data_hi;
u32 __iomem *pci_win_rd_data_lo;
u64 __iomem *pci_win_rd_data;
};
#define OCTEON_CONSOLE_MAX_READ_BYTES 512
typedef int (*octeon_console_print_fn)(struct octeon_device *oct,
u32 num, char *pre, char *suf);
struct octeon_console {
u32 active;
u32 waiting;
u64 addr;
u32 buffer_size;
u64 input_base_addr;
u64 output_base_addr;
octeon_console_print_fn print;
char leftover[OCTEON_CONSOLE_MAX_READ_BYTES];
};
struct octeon_board_info {
char name[OCT_BOARD_NAME];
char serial_number[OCT_SERIAL_LEN];
u64 major;
u64 minor;
};
struct octeon_fn_list {
void (*setup_iq_regs)(struct octeon_device *, u32);
void (*setup_oq_regs)(struct octeon_device *, u32);
irqreturn_t (*process_interrupt_regs)(void *);
u64 (*msix_interrupt_handler)(void *);
int (*setup_mbox)(struct octeon_device *);
int (*free_mbox)(struct octeon_device *);
int (*soft_reset)(struct octeon_device *);
int (*setup_device_regs)(struct octeon_device *);
void (*bar1_idx_setup)(struct octeon_device *, u64, u32, int);
void (*bar1_idx_write)(struct octeon_device *, u32, u32);
u32 (*bar1_idx_read)(struct octeon_device *, u32);
u32 (*update_iq_read_idx)(struct octeon_instr_queue *);
void (*enable_oq_pkt_time_intr)(struct octeon_device *, u32);
void (*disable_oq_pkt_time_intr)(struct octeon_device *, u32);
void (*enable_interrupt)(struct octeon_device *, u8);
void (*disable_interrupt)(struct octeon_device *, u8);
int (*enable_io_queues)(struct octeon_device *);
void (*disable_io_queues)(struct octeon_device *);
};
/* Must be multiple of 8, changing breaks ABI */
#define CVMX_BOOTMEM_NAME_LEN 128
/* Structure for named memory blocks
* Number of descriptors
* available can be changed without affecting compatibility,
* but name length changes require a bump in the bootmem
* descriptor version
* Note: This structure must be naturally 64 bit aligned, as a single
* memory image will be used by both 32 and 64 bit programs.
*/
struct cvmx_bootmem_named_block_desc {
/** Base address of named block */
u64 base_addr;
/** Size actually allocated for named block */
u64 size;
/** name of named block */
char name[CVMX_BOOTMEM_NAME_LEN];
};
struct oct_fw_info {
u32 max_nic_ports; /** max nic ports for the device */
u32 num_gmx_ports; /** num gmx ports */
u64 app_cap_flags; /** firmware cap flags */
/** The core application is running in this mode.
* See octeon-drv-opcodes.h for values.
*/
u32 app_mode;
char liquidio_firmware_version[32];
/* Fields extracted from legacy string 'liquidio_firmware_version' */
struct {
u8 maj;
u8 min;
u8 rev;
} ver;
};
#define OCT_FW_VER(maj, min, rev) \
(((u32)(maj) << 16) | ((u32)(min) << 8) | ((u32)(rev)))
/* wrappers around work structs */
struct cavium_wk {
struct delayed_work work;
void *ctxptr;
u64 ctxul;
};
struct cavium_wq {
struct workqueue_struct *wq;
struct cavium_wk wk;
};
struct octdev_props {
/* Each interface in the Octeon device has a network
* device pointer (used for OS specific calls).
*/
int rx_on;
int fec;
int fec_boot;
int napi_enabled;
int gmxport;
struct net_device *netdev;
};
#define LIO_FLAG_MSIX_ENABLED 0x1
#define MSIX_PO_INT 0x1
#define MSIX_PI_INT 0x2
#define MSIX_MBOX_INT 0x4
struct octeon_pf_vf_hs_word {
#ifdef __LITTLE_ENDIAN_BITFIELD
/** PKIND value assigned for the DPI interface */
u64 pkind : 8;
/** OCTEON core clock multiplier */
u64 core_tics_per_us : 16;
/** OCTEON coprocessor clock multiplier */
u64 coproc_tics_per_us : 16;
/** app that currently running on OCTEON */
u64 app_mode : 8;
/** RESERVED */
u64 reserved : 16;
#else
/** RESERVED */
u64 reserved : 16;
/** app that currently running on OCTEON */
u64 app_mode : 8;
/** OCTEON coprocessor clock multiplier */
u64 coproc_tics_per_us : 16;
/** OCTEON core clock multiplier */
u64 core_tics_per_us : 16;
/** PKIND value assigned for the DPI interface */
u64 pkind : 8;
#endif
};
struct octeon_sriov_info {
/* Number of rings assigned to VF */
u32 rings_per_vf;
/** Max Number of VF devices that can be enabled. This variable can
* specified during load time or it will be derived after allocating
* PF queues. When max_vfs is derived then each VF will get one queue
**/
u32 max_vfs;
/** Number of VF devices enabled using sysfs. */
u32 num_vfs_alloced;
/* Actual rings left for PF device */
u32 num_pf_rings;
/* SRN of PF usable IO queues */
u32 pf_srn;
/* total pf rings */
u32 trs;
u32 sriov_enabled;
struct lio_trusted_vf trusted_vf;
u64 vf_macaddr[MAX_POSSIBLE_VFS];
u16 vf_vlantci[MAX_POSSIBLE_VFS];
int vf_linkstate[MAX_POSSIBLE_VFS];
bool vf_spoofchk[MAX_POSSIBLE_VFS];
u64 vf_drv_loaded_mask;
};
struct octeon_ioq_vector {
struct octeon_device *oct_dev;
int iq_index;
int droq_index;
int vector;
struct octeon_mbox *mbox;
struct cpumask affinity_mask;
u32 ioq_num;
};
struct lio_vf_rep_list {
int num_vfs;
struct net_device *ndev[CN23XX_MAX_VFS_PER_PF];
};
struct lio_devlink_priv {
struct octeon_device *oct;
};
/** The Octeon device.
* Each Octeon device has this structure to represent all its
* components.
*/
struct octeon_device {
/** Lock for PCI window configuration accesses */
spinlock_t pci_win_lock;
/** Lock for memory accesses */
spinlock_t mem_access_lock;
/** PCI device pointer */
struct pci_dev *pci_dev;
/** Chip specific information. */
void *chip;
/** Number of interfaces detected in this octeon device. */
u32 ifcount;
struct octdev_props props[MAX_OCTEON_LINKS];
/** Octeon Chip type. */
u16 chip_id;
u16 rev_id;
u32 subsystem_id;
u16 pf_num;
u16 vf_num;
/** This device's id - set by the driver. */
u32 octeon_id;
/** This device's PCIe port used for traffic. */
u16 pcie_port;
u16 flags;
#define LIO_FLAG_MSI_ENABLED (u32)(1 << 1)
/** The state of this device */
atomic_t status;
/** memory mapped io range */
struct octeon_mmio mmio[OCT_MEM_REGIONS];
struct octeon_reg_list reg_list;
struct octeon_fn_list fn_list;
struct octeon_board_info boardinfo;
u32 num_iqs;
/* The pool containing pre allocated buffers used for soft commands */
struct octeon_sc_buffer_pool sc_buf_pool;
/** The input instruction queues */
struct octeon_instr_queue *instr_queue
[MAX_POSSIBLE_OCTEON_INSTR_QUEUES];
/** The doubly-linked list of instruction response */
struct octeon_response_list response_list[MAX_RESPONSE_LISTS];
u32 num_oqs;
/** The DROQ output queues */
struct octeon_droq *droq[MAX_POSSIBLE_OCTEON_OUTPUT_QUEUES];
struct octeon_io_enable io_qmask;
/** List of dispatch functions */
struct octeon_dispatch_list dispatch;
u32 int_status;
u64 droq_intr;
/** Physical location of the cvmx_bootmem_desc_t in octeon memory */
u64 bootmem_desc_addr;
/** Placeholder memory for named blocks.
* Assumes single-threaded access
*/
struct cvmx_bootmem_named_block_desc bootmem_named_block_desc;
/** Address of consoles descriptor */
u64 console_desc_addr;
/** Number of consoles available. 0 means they are inaccessible */
u32 num_consoles;
/* Console caches */
struct octeon_console console[MAX_OCTEON_MAPS];
/* Console named block info */
struct {
u64 dram_region_base;
int bar1_index;
} console_nb_info;
/* Coprocessor clock rate. */
u64 coproc_clock_rate;
/** The core application is running in this mode. See liquidio_common.h
* for values.
*/
u32 app_mode;
struct oct_fw_info fw_info;
/** The name given to this device. */
char device_name[32];
/** Application Context */
void *app_ctx;
struct cavium_wq dma_comp_wq;
/** Lock for dma response list */
spinlock_t cmd_resp_wqlock;
u32 cmd_resp_state;
struct cavium_wq check_db_wq[MAX_POSSIBLE_OCTEON_INSTR_QUEUES];
struct cavium_wk nic_poll_work;
struct cavium_wk console_poll_work[MAX_OCTEON_MAPS];
void *priv;
int num_msix_irqs;
void *msix_entries;
/* when requesting IRQs, the names are stored here */
void *irq_name_storage;
struct octeon_sriov_info sriov_info;
struct octeon_pf_vf_hs_word pfvf_hsword;
int msix_on;
/** Mail Box details of each octeon queue. */
struct octeon_mbox *mbox[MAX_POSSIBLE_VFS];
/** IOq information of it's corresponding MSI-X interrupt. */
struct octeon_ioq_vector *ioq_vector;
int rx_pause;
int tx_pause;
struct oct_link_stats link_stats; /*stastics from firmware*/
/* private flags to control driver-specific features through ethtool */
u32 priv_flags;
void *watchdog_task;
u32 rx_coalesce_usecs;
u32 rx_max_coalesced_frames;
u32 tx_max_coalesced_frames;
bool cores_crashed;
struct {
int bus;
int dev;
int func;
} loc;
atomic_t *adapter_refcount; /* reference count of adapter */
atomic_t *adapter_fw_state; /* per-adapter, lio_fw_state */
bool ptp_enable;
struct lio_vf_rep_list vf_rep_list;
struct devlink *devlink;
enum devlink_eswitch_mode eswitch_mode;
/* for 25G NIC speed change */
u8 speed_boot;
u8 speed_setting;
u8 no_speed_setting;
u32 vfstats_poll;
#define LIO_VFSTATS_POLL 10
};
#define OCT_DRV_ONLINE 1
#define OCT_DRV_OFFLINE 2
#define OCTEON_CN6XXX(oct) ({ \
typeof(oct) _oct = (oct); \
((_oct->chip_id == OCTEON_CN66XX) || \
(_oct->chip_id == OCTEON_CN68XX)); })
#define OCTEON_CN23XX_PF(oct) ((oct)->chip_id == OCTEON_CN23XX_PF_VID)
#define OCTEON_CN23XX_VF(oct) ((oct)->chip_id == OCTEON_CN23XX_VF_VID)
#define CHIP_CONF(oct, TYPE) \
(((struct octeon_ ## TYPE *)((oct)->chip))->conf)
#define MAX_IO_PENDING_PKT_COUNT 100
/*------------------ Function Prototypes ----------------------*/
/** Initialize device list memory */
void octeon_init_device_list(int conf_type);
/** Free memory for Input and Output queue structures for a octeon device */
void octeon_free_device_mem(struct octeon_device *oct);
/* Look up a free entry in the octeon_device table and allocate resources
* for the octeon_device structure for an octeon device. Called at init
* time.
*/
struct octeon_device *octeon_allocate_device(u32 pci_id,
u32 priv_size);
/** Register a device's bus location at initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @param bus - PCIe bus #
* @param dev - PCIe device #
* @param func - PCIe function #
* @param is_pf - TRUE for PF, FALSE for VF
* @return reference count of device's adapter
*/
int octeon_register_device(struct octeon_device *oct,
int bus, int dev, int func, int is_pf);
/** Deregister a device at de-initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @return reference count of device's adapter
*/
int octeon_deregister_device(struct octeon_device *oct);
/** Initialize the driver's dispatch list which is a mix of a hash table
* and a linked list. This is done at driver load time.
* @param octeon_dev - pointer to the octeon device structure.
* @return 0 on success, else -ve error value
*/
int octeon_init_dispatch_list(struct octeon_device *octeon_dev);
/** Delete the driver's dispatch list and all registered entries.
* This is done at driver unload time.
* @param octeon_dev - pointer to the octeon device structure.
*/
void octeon_delete_dispatch_list(struct octeon_device *octeon_dev);
/** Initialize the core device fields with the info returned by the FW.
* @param recv_info - Receive info structure
* @param buf - Receive buffer
*/
int octeon_core_drv_init(struct octeon_recv_info *recv_info, void *buf);
/** Gets the dispatch function registered to receive packets with a
* given opcode/subcode.
* @param octeon_dev - the octeon device pointer.
* @param opcode - the opcode for which the dispatch function
* is to checked.
* @param subcode - the subcode for which the dispatch function
* is to checked.
*
* @return Success: octeon_dispatch_fn_t (dispatch function pointer)
* @return Failure: NULL
*
* Looks up the dispatch list to get the dispatch function for a
* given opcode.
*/
octeon_dispatch_fn_t
octeon_get_dispatch(struct octeon_device *octeon_dev, u16 opcode,
u16 subcode);
/** Get the octeon device pointer.
* @param octeon_id - The id for which the octeon device pointer is required.
* @return Success: Octeon device pointer.
* @return Failure: NULL.
*/
struct octeon_device *lio_get_device(u32 octeon_id);
/** Read windowed register.
* @param oct - pointer to the Octeon device.
* @param addr - Address of the register to read.
*
* This routine is called to read from the indirectly accessed
* Octeon registers that are visible through a PCI BAR0 mapped window
* register.
* @return - 64 bit value read from the register.
*/
u64 lio_pci_readq(struct octeon_device *oct, u64 addr);
/** Write windowed register.
* @param oct - pointer to the Octeon device.
* @param val - Value to write
* @param addr - Address of the register to write
*
* This routine is called to write to the indirectly accessed
* Octeon registers that are visible through a PCI BAR0 mapped window
* register.
* @return Nothing.
*/
void lio_pci_writeq(struct octeon_device *oct, u64 val, u64 addr);
/* Routines for reading and writing CSRs */
#define octeon_write_csr(oct_dev, reg_off, value) \
writel(value, (oct_dev)->mmio[0].hw_addr + (reg_off))
#define octeon_write_csr64(oct_dev, reg_off, val64) \
writeq(val64, (oct_dev)->mmio[0].hw_addr + (reg_off))
#define octeon_read_csr(oct_dev, reg_off) \
readl((oct_dev)->mmio[0].hw_addr + (reg_off))
#define octeon_read_csr64(oct_dev, reg_off) \
readq((oct_dev)->mmio[0].hw_addr + (reg_off))
/**
* Checks if memory access is okay
*
* @param oct which octeon to send to
* @return Zero on success, negative on failure.
*/
int octeon_mem_access_ok(struct octeon_device *oct);
/**
* Waits for DDR initialization.
*
* @param oct which octeon to send to
* @param timeout_in_ms pointer to how long to wait until DDR is initialized
* in ms.
* If contents are 0, it waits until contents are non-zero
* before starting to check.
* @return Zero on success, negative on failure.
*/
int octeon_wait_for_ddr_init(struct octeon_device *oct,
u32 *timeout_in_ms);
/**
* Wait for u-boot to boot and be waiting for a command.
*
* @param wait_time_hundredths
* Maximum time to wait
*
* @return Zero on success, negative on failure.
*/
int octeon_wait_for_bootloader(struct octeon_device *oct,
u32 wait_time_hundredths);
/**
* Initialize console access
*
* @param oct which octeon initialize
* @return Zero on success, negative on failure.
*/
int octeon_init_consoles(struct octeon_device *oct);
/**
* Adds access to a console to the device.
*
* @param oct: which octeon to add to
* @param console_num: which console
* @param dbg_enb: ptr to debug enablement string, one of:
* * NULL for no debug output (i.e. disabled)
* * empty string enables debug output (via default method)
* * specific string to enable debug console output
*
* @return Zero on success, negative on failure.
*/
int octeon_add_console(struct octeon_device *oct, u32 console_num,
char *dbg_enb);
/** Removes all attached consoles. */
void octeon_remove_consoles(struct octeon_device *oct);
/**
* Send a string to u-boot on console 0 as a command.
*
* @param oct which octeon to send to
* @param cmd_str String to send
* @param wait_hundredths Time to wait for u-boot to accept the command.
*
* @return Zero on success, negative on failure.
*/
int octeon_console_send_cmd(struct octeon_device *oct, char *cmd_str,
u32 wait_hundredths);
/** Parses, validates, and downloads firmware, then boots associated cores.
* @param oct which octeon to download firmware to
* @param data - The complete firmware file image
* @param size - The size of the data
*
* @return 0 if success.
* -EINVAL if file is incompatible or badly formatted.
* -ENODEV if no handler was found for the application type or an
* invalid octeon id was passed.
*/
int octeon_download_firmware(struct octeon_device *oct, const u8 *data,
size_t size);
char *lio_get_state_string(atomic_t *state_ptr);
/** Sets up instruction queues for the device
* @param oct which octeon to setup
*
* @return 0 if success. 1 if fails
*/
int octeon_setup_instr_queues(struct octeon_device *oct);
/** Sets up output queues for the device
* @param oct which octeon to setup
*
* @return 0 if success. 1 if fails
*/
int octeon_setup_output_queues(struct octeon_device *oct);
int octeon_get_tx_qsize(struct octeon_device *oct, u32 q_no);
int octeon_get_rx_qsize(struct octeon_device *oct, u32 q_no);
/** Turns off the input and output queues for the device
* @param oct which octeon to disable
*/
int octeon_set_io_queues_off(struct octeon_device *oct);
/** Turns on or off the given output queue for the device
* @param oct which octeon to change
* @param q_no which queue
* @param enable 1 to enable, 0 to disable
*/
void octeon_set_droq_pkt_op(struct octeon_device *oct, u32 q_no, u32 enable);
/** Retrieve the config for the device
* @param oct which octeon
* @param card_type type of card
*
* @returns pointer to configuration
*/
void *oct_get_config_info(struct octeon_device *oct, u16 card_type);
/** Gets the octeon device configuration
* @return - pointer to the octeon configuration struture
*/
struct octeon_config *octeon_get_conf(struct octeon_device *oct);
void octeon_free_ioq_vector(struct octeon_device *oct);
int octeon_allocate_ioq_vector(struct octeon_device *oct, u32 num_ioqs);
void lio_enable_irq(struct octeon_droq *droq, struct octeon_instr_queue *iq);
/* LiquidIO driver pivate flags */
enum {
OCT_PRIV_FLAG_TX_BYTES = 0, /* Tx interrupts by pending byte count */
};
#define OCT_PRIV_FLAG_DEFAULT 0x0
static inline u32 lio_get_priv_flag(struct octeon_device *octdev, u32 flag)
{
return !!(octdev->priv_flags & (0x1 << flag));
}
static inline void lio_set_priv_flag(struct octeon_device *octdev,
u32 flag, u32 val)
{
if (val)
octdev->priv_flags |= (0x1 << flag);
else
octdev->priv_flags &= ~(0x1 << flag);
}
#endif
@@ -0,0 +1,968 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
#include "octeon_network.h"
#include "cn66xx_regs.h"
#include "cn66xx_device.h"
#include "cn23xx_pf_device.h"
#include "cn23xx_vf_device.h"
struct __dispatch {
struct list_head list;
struct octeon_recv_info *rinfo;
octeon_dispatch_fn_t disp_fn;
};
/** Get the argument that the user set when registering dispatch
* function for a given opcode/subcode.
* @param octeon_dev - the octeon device pointer.
* @param opcode - the opcode for which the dispatch argument
* is to be checked.
* @param subcode - the subcode for which the dispatch argument
* is to be checked.
* @return Success: void * (argument to the dispatch function)
* @return Failure: NULL
*
*/
void *octeon_get_dispatch_arg(struct octeon_device *octeon_dev,
u16 opcode, u16 subcode)
{
int idx;
struct list_head *dispatch;
void *fn_arg = NULL;
u16 combined_opcode = OPCODE_SUBCODE(opcode, subcode);
idx = combined_opcode & OCTEON_OPCODE_MASK;
spin_lock_bh(&octeon_dev->dispatch.lock);
if (octeon_dev->dispatch.count == 0) {
spin_unlock_bh(&octeon_dev->dispatch.lock);
return NULL;
}
if (octeon_dev->dispatch.dlist[idx].opcode == combined_opcode) {
fn_arg = octeon_dev->dispatch.dlist[idx].arg;
} else {
list_for_each(dispatch,
&octeon_dev->dispatch.dlist[idx].list) {
if (((struct octeon_dispatch *)dispatch)->opcode ==
combined_opcode) {
fn_arg = ((struct octeon_dispatch *)
dispatch)->arg;
break;
}
}
}
spin_unlock_bh(&octeon_dev->dispatch.lock);
return fn_arg;
}
/** Check for packets on Droq. This function should be called with lock held.
* @param droq - Droq on which count is checked.
* @return Returns packet count.
*/
u32 octeon_droq_check_hw_for_pkts(struct octeon_droq *droq)
{
u32 pkt_count = 0;
u32 last_count;
pkt_count = readl(droq->pkts_sent_reg);
last_count = pkt_count - droq->pkt_count;
droq->pkt_count = pkt_count;
/* we shall write to cnts at napi irq enable or end of droq tasklet */
if (last_count)
atomic_add(last_count, &droq->pkts_pending);
return last_count;
}
EXPORT_SYMBOL_GPL(octeon_droq_check_hw_for_pkts);
static void octeon_droq_compute_max_packet_bufs(struct octeon_droq *droq)
{
u32 count = 0;
/* max_empty_descs is the max. no. of descs that can have no buffers.
* If the empty desc count goes beyond this value, we cannot safely
* read in a 64K packet sent by Octeon
* (64K is max pkt size from Octeon)
*/
droq->max_empty_descs = 0;
do {
droq->max_empty_descs++;
count += droq->buffer_size;
} while (count < (64 * 1024));
droq->max_empty_descs = droq->max_count - droq->max_empty_descs;
}
static void octeon_droq_reset_indices(struct octeon_droq *droq)
{
droq->read_idx = 0;
droq->write_idx = 0;
droq->refill_idx = 0;
droq->refill_count = 0;
atomic_set(&droq->pkts_pending, 0);
}
static void
octeon_droq_destroy_ring_buffers(struct octeon_device *oct,
struct octeon_droq *droq)
{
u32 i;
struct octeon_skb_page_info *pg_info;
for (i = 0; i < droq->max_count; i++) {
pg_info = &droq->recv_buf_list[i].pg_info;
if (!pg_info)
continue;
if (pg_info->dma)
lio_unmap_ring(oct->pci_dev,
(u64)pg_info->dma);
pg_info->dma = 0;
if (pg_info->page)
recv_buffer_destroy(droq->recv_buf_list[i].buffer,
pg_info);
droq->recv_buf_list[i].buffer = NULL;
}
octeon_droq_reset_indices(droq);
}
static int
octeon_droq_setup_ring_buffers(struct octeon_device *oct,
struct octeon_droq *droq)
{
u32 i;
void *buf;
struct octeon_droq_desc *desc_ring = droq->desc_ring;
for (i = 0; i < droq->max_count; i++) {
buf = recv_buffer_alloc(oct, &droq->recv_buf_list[i].pg_info);
if (!buf) {
dev_err(&oct->pci_dev->dev, "%s buffer alloc failed\n",
__func__);
droq->stats.rx_alloc_failure++;
return -ENOMEM;
}
droq->recv_buf_list[i].buffer = buf;
droq->recv_buf_list[i].data = get_rbd(buf);
desc_ring[i].info_ptr = 0;
desc_ring[i].buffer_ptr =
lio_map_ring(droq->recv_buf_list[i].buffer);
}
octeon_droq_reset_indices(droq);
octeon_droq_compute_max_packet_bufs(droq);
return 0;
}
int octeon_delete_droq(struct octeon_device *oct, u32 q_no)
{
struct octeon_droq *droq = oct->droq[q_no];
dev_dbg(&oct->pci_dev->dev, "%s[%d]\n", __func__, q_no);
octeon_droq_destroy_ring_buffers(oct, droq);
vfree(droq->recv_buf_list);
if (droq->desc_ring)
lio_dma_free(oct, (droq->max_count * OCT_DROQ_DESC_SIZE),
droq->desc_ring, droq->desc_ring_dma);
memset(droq, 0, OCT_DROQ_SIZE);
oct->io_qmask.oq &= ~(1ULL << q_no);
vfree(oct->droq[q_no]);
oct->droq[q_no] = NULL;
oct->num_oqs--;
return 0;
}
EXPORT_SYMBOL_GPL(octeon_delete_droq);
int octeon_init_droq(struct octeon_device *oct,
u32 q_no,
u32 num_descs,
u32 desc_size,
void *app_ctx)
{
struct octeon_droq *droq;
u32 desc_ring_size = 0, c_num_descs = 0, c_buf_size = 0;
u32 c_pkts_per_intr = 0, c_refill_threshold = 0;
int numa_node = dev_to_node(&oct->pci_dev->dev);
dev_dbg(&oct->pci_dev->dev, "%s[%d]\n", __func__, q_no);
droq = oct->droq[q_no];
memset(droq, 0, OCT_DROQ_SIZE);
droq->oct_dev = oct;
droq->q_no = q_no;
if (app_ctx)
droq->app_ctx = app_ctx;
else
droq->app_ctx = (void *)(size_t)q_no;
c_num_descs = num_descs;
c_buf_size = desc_size;
if (OCTEON_CN6XXX(oct)) {
struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx);
c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf6x);
c_refill_threshold =
(u32)CFG_GET_OQ_REFILL_THRESHOLD(conf6x);
} else if (OCTEON_CN23XX_PF(oct)) {
struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf);
c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23);
c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23);
} else if (OCTEON_CN23XX_VF(oct)) {
struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_vf);
c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23);
c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23);
} else {
return 1;
}
droq->max_count = c_num_descs;
droq->buffer_size = c_buf_size;
desc_ring_size = droq->max_count * OCT_DROQ_DESC_SIZE;
droq->desc_ring = lio_dma_alloc(oct, desc_ring_size,
(dma_addr_t *)&droq->desc_ring_dma);
if (!droq->desc_ring) {
dev_err(&oct->pci_dev->dev,
"Output queue %d ring alloc failed\n", q_no);
return 1;
}
dev_dbg(&oct->pci_dev->dev, "droq[%d]: desc_ring: virt: 0x%p, dma: %lx\n",
q_no, droq->desc_ring, droq->desc_ring_dma);
dev_dbg(&oct->pci_dev->dev, "droq[%d]: num_desc: %d\n", q_no,
droq->max_count);
droq->recv_buf_list = vzalloc_node(array_size(droq->max_count, OCT_DROQ_RECVBUF_SIZE),
numa_node);
if (!droq->recv_buf_list)
droq->recv_buf_list = vzalloc(array_size(droq->max_count, OCT_DROQ_RECVBUF_SIZE));
if (!droq->recv_buf_list) {
dev_err(&oct->pci_dev->dev, "Output queue recv buf list alloc failed\n");
goto init_droq_fail;
}
if (octeon_droq_setup_ring_buffers(oct, droq))
goto init_droq_fail;
droq->pkts_per_intr = c_pkts_per_intr;
droq->refill_threshold = c_refill_threshold;
dev_dbg(&oct->pci_dev->dev, "DROQ INIT: max_empty_descs: %d\n",
droq->max_empty_descs);
INIT_LIST_HEAD(&droq->dispatch_list);
/* For 56xx Pass1, this function won't be called, so no checks. */
oct->fn_list.setup_oq_regs(oct, q_no);
oct->io_qmask.oq |= BIT_ULL(q_no);
return 0;
init_droq_fail:
octeon_delete_droq(oct, q_no);
return 1;
}
/* octeon_create_recv_info
* Parameters:
* octeon_dev - pointer to the octeon device structure
* droq - droq in which the packet arrived.
* buf_cnt - no. of buffers used by the packet.
* idx - index in the descriptor for the first buffer in the packet.
* Description:
* Allocates a recv_info_t and copies the buffer addresses for packet data
* into the recv_pkt space which starts at an 8B offset from recv_info_t.
* Flags the descriptors for refill later. If available descriptors go
* below the threshold to receive a 64K pkt, new buffers are first allocated
* before the recv_pkt_t is created.
* This routine will be called in interrupt context.
* Returns:
* Success: Pointer to recv_info_t
* Failure: NULL.
*/
static inline struct octeon_recv_info *octeon_create_recv_info(
struct octeon_device *octeon_dev,
struct octeon_droq *droq,
u32 buf_cnt,
u32 idx)
{
struct octeon_droq_info *info;
struct octeon_recv_pkt *recv_pkt;
struct octeon_recv_info *recv_info;
u32 i, bytes_left;
struct octeon_skb_page_info *pg_info;
info = (struct octeon_droq_info *)droq->recv_buf_list[idx].data;
recv_info = octeon_alloc_recv_info(sizeof(struct __dispatch));
if (!recv_info)
return NULL;
recv_pkt = recv_info->recv_pkt;
recv_pkt->rh = info->rh;
recv_pkt->length = (u32)info->length;
recv_pkt->buffer_count = (u16)buf_cnt;
recv_pkt->octeon_id = (u16)octeon_dev->octeon_id;
i = 0;
bytes_left = (u32)info->length;
while (buf_cnt) {
{
pg_info = &droq->recv_buf_list[idx].pg_info;
lio_unmap_ring(octeon_dev->pci_dev,
(u64)pg_info->dma);
pg_info->page = NULL;
pg_info->dma = 0;
}
recv_pkt->buffer_size[i] =
(bytes_left >=
droq->buffer_size) ? droq->buffer_size : bytes_left;
recv_pkt->buffer_ptr[i] = droq->recv_buf_list[idx].buffer;
droq->recv_buf_list[idx].buffer = NULL;
idx = incr_index(idx, 1, droq->max_count);
bytes_left -= droq->buffer_size;
i++;
buf_cnt--;
}
return recv_info;
}
/* If we were not able to refill all buffers, try to move around
* the buffers that were not dispatched.
*/
static inline u32
octeon_droq_refill_pullup_descs(struct octeon_droq *droq,
struct octeon_droq_desc *desc_ring)
{
u32 desc_refilled = 0;
u32 refill_index = droq->refill_idx;
while (refill_index != droq->read_idx) {
if (droq->recv_buf_list[refill_index].buffer) {
droq->recv_buf_list[droq->refill_idx].buffer =
droq->recv_buf_list[refill_index].buffer;
droq->recv_buf_list[droq->refill_idx].data =
droq->recv_buf_list[refill_index].data;
desc_ring[droq->refill_idx].buffer_ptr =
desc_ring[refill_index].buffer_ptr;
droq->recv_buf_list[refill_index].buffer = NULL;
desc_ring[refill_index].buffer_ptr = 0;
do {
droq->refill_idx = incr_index(droq->refill_idx,
1,
droq->max_count);
desc_refilled++;
droq->refill_count--;
} while (droq->recv_buf_list[droq->refill_idx].buffer);
}
refill_index = incr_index(refill_index, 1, droq->max_count);
} /* while */
return desc_refilled;
}
/* octeon_droq_refill
* Parameters:
* droq - droq in which descriptors require new buffers.
* Description:
* Called during normal DROQ processing in interrupt mode or by the poll
* thread to refill the descriptors from which buffers were dispatched
* to upper layers. Attempts to allocate new buffers. If that fails, moves
* up buffers (that were not dispatched) to form a contiguous ring.
* Returns:
* No of descriptors refilled.
*/
static u32
octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq)
{
struct octeon_droq_desc *desc_ring;
void *buf = NULL;
u8 *data;
u32 desc_refilled = 0;
struct octeon_skb_page_info *pg_info;
desc_ring = droq->desc_ring;
while (droq->refill_count && (desc_refilled < droq->max_count)) {
/* If a valid buffer exists (happens if there is no dispatch),
* reuse the buffer, else allocate.
*/
if (!droq->recv_buf_list[droq->refill_idx].buffer) {
pg_info =
&droq->recv_buf_list[droq->refill_idx].pg_info;
/* Either recycle the existing pages or go for
* new page alloc
*/
if (pg_info->page)
buf = recv_buffer_reuse(octeon_dev, pg_info);
else
buf = recv_buffer_alloc(octeon_dev, pg_info);
/* If a buffer could not be allocated, no point in
* continuing
*/
if (!buf) {
droq->stats.rx_alloc_failure++;
break;
}
droq->recv_buf_list[droq->refill_idx].buffer =
buf;
data = get_rbd(buf);
} else {
data = get_rbd(droq->recv_buf_list
[droq->refill_idx].buffer);
}
droq->recv_buf_list[droq->refill_idx].data = data;
desc_ring[droq->refill_idx].buffer_ptr =
lio_map_ring(droq->recv_buf_list[
droq->refill_idx].buffer);
droq->refill_idx = incr_index(droq->refill_idx, 1,
droq->max_count);
desc_refilled++;
droq->refill_count--;
}
if (droq->refill_count)
desc_refilled +=
octeon_droq_refill_pullup_descs(droq, desc_ring);
/* if droq->refill_count
* The refill count would not change in pass two. We only moved buffers
* to close the gap in the ring, but we would still have the same no. of
* buffers to refill.
*/
return desc_refilled;
}
/** check if we can allocate packets to get out of oom.
* @param droq - Droq being checked.
* @return 1 if fails to refill minimum
*/
int octeon_retry_droq_refill(struct octeon_droq *droq)
{
struct octeon_device *oct = droq->oct_dev;
int desc_refilled, reschedule = 1;
u32 pkts_credit;
pkts_credit = readl(droq->pkts_credit_reg);
desc_refilled = octeon_droq_refill(oct, droq);
if (desc_refilled) {
/* Flush the droq descriptor data to memory to be sure
* that when we update the credits the data in memory
* is accurate.
*/
wmb();
writel(desc_refilled, droq->pkts_credit_reg);
if (pkts_credit + desc_refilled >= CN23XX_SLI_DEF_BP)
reschedule = 0;
}
return reschedule;
}
static inline u32
octeon_droq_get_bufcount(u32 buf_size, u32 total_len)
{
return DIV_ROUND_UP(total_len, buf_size);
}
static int
octeon_droq_dispatch_pkt(struct octeon_device *oct,
struct octeon_droq *droq,
union octeon_rh *rh,
struct octeon_droq_info *info)
{
u32 cnt;
octeon_dispatch_fn_t disp_fn;
struct octeon_recv_info *rinfo;
cnt = octeon_droq_get_bufcount(droq->buffer_size, (u32)info->length);
disp_fn = octeon_get_dispatch(oct, (u16)rh->r.opcode,
(u16)rh->r.subcode);
if (disp_fn) {
rinfo = octeon_create_recv_info(oct, droq, cnt, droq->read_idx);
if (rinfo) {
struct __dispatch *rdisp = rinfo->rsvd;
rdisp->rinfo = rinfo;
rdisp->disp_fn = disp_fn;
rinfo->recv_pkt->rh = *rh;
list_add_tail(&rdisp->list,
&droq->dispatch_list);
} else {
droq->stats.dropped_nomem++;
}
} else {
dev_err(&oct->pci_dev->dev, "DROQ: No dispatch function (opcode %u/%u)\n",
(unsigned int)rh->r.opcode,
(unsigned int)rh->r.subcode);
droq->stats.dropped_nodispatch++;
}
return cnt;
}
static inline void octeon_droq_drop_packets(struct octeon_device *oct,
struct octeon_droq *droq,
u32 cnt)
{
u32 i = 0, buf_cnt;
struct octeon_droq_info *info;
for (i = 0; i < cnt; i++) {
info = (struct octeon_droq_info *)
droq->recv_buf_list[droq->read_idx].data;
octeon_swap_8B_data((u64 *)info, 2);
if (info->length) {
info->length += OCTNET_FRM_LENGTH_SIZE;
droq->stats.bytes_received += info->length;
buf_cnt = octeon_droq_get_bufcount(droq->buffer_size,
(u32)info->length);
} else {
dev_err(&oct->pci_dev->dev, "DROQ: In drop: pkt with len 0\n");
buf_cnt = 1;
}
droq->read_idx = incr_index(droq->read_idx, buf_cnt,
droq->max_count);
droq->refill_count += buf_cnt;
}
}
static u32
octeon_droq_fast_process_packets(struct octeon_device *oct,
struct octeon_droq *droq,
u32 pkts_to_process)
{
u32 pkt, total_len = 0, pkt_count, retval;
struct octeon_droq_info *info;
union octeon_rh *rh;
pkt_count = pkts_to_process;
for (pkt = 0; pkt < pkt_count; pkt++) {
u32 pkt_len = 0;
struct sk_buff *nicbuf = NULL;
struct octeon_skb_page_info *pg_info;
void *buf;
info = (struct octeon_droq_info *)
droq->recv_buf_list[droq->read_idx].data;
octeon_swap_8B_data((u64 *)info, 2);
if (!info->length) {
dev_err(&oct->pci_dev->dev,
"DROQ[%d] idx: %d len:0, pkt_cnt: %d\n",
droq->q_no, droq->read_idx, pkt_count);
print_hex_dump_bytes("", DUMP_PREFIX_ADDRESS,
(u8 *)info,
OCT_DROQ_INFO_SIZE);
break;
}
/* Len of resp hdr in included in the received data len. */
rh = &info->rh;
info->length += OCTNET_FRM_LENGTH_SIZE;
rh->r_dh.len += (ROUNDUP8(OCT_DROQ_INFO_SIZE) / sizeof(u64));
total_len += (u32)info->length;
if (opcode_slow_path(rh)) {
u32 buf_cnt;
buf_cnt = octeon_droq_dispatch_pkt(oct, droq, rh, info);
droq->read_idx = incr_index(droq->read_idx,
buf_cnt, droq->max_count);
droq->refill_count += buf_cnt;
} else {
if (info->length <= droq->buffer_size) {
pkt_len = (u32)info->length;
nicbuf = droq->recv_buf_list[
droq->read_idx].buffer;
pg_info = &droq->recv_buf_list[
droq->read_idx].pg_info;
if (recv_buffer_recycle(oct, pg_info))
pg_info->page = NULL;
droq->recv_buf_list[droq->read_idx].buffer =
NULL;
droq->read_idx = incr_index(droq->read_idx, 1,
droq->max_count);
droq->refill_count++;
} else {
nicbuf = octeon_fast_packet_alloc((u32)
info->length);
pkt_len = 0;
/* nicbuf allocation can fail. We'll handle it
* inside the loop.
*/
while (pkt_len < info->length) {
int cpy_len, idx = droq->read_idx;
cpy_len = ((pkt_len + droq->buffer_size)
> info->length) ?
((u32)info->length - pkt_len) :
droq->buffer_size;
if (nicbuf) {
octeon_fast_packet_next(droq,
nicbuf,
cpy_len,
idx);
buf = droq->recv_buf_list[
idx].buffer;
recv_buffer_fast_free(buf);
droq->recv_buf_list[idx].buffer
= NULL;
} else {
droq->stats.rx_alloc_failure++;
}
pkt_len += cpy_len;
droq->read_idx =
incr_index(droq->read_idx, 1,
droq->max_count);
droq->refill_count++;
}
}
if (nicbuf) {
if (droq->ops.fptr) {
droq->ops.fptr(oct->octeon_id,
nicbuf, pkt_len,
rh, &droq->napi,
droq->ops.farg);
} else {
recv_buffer_free(nicbuf);
}
}
}
if (droq->refill_count >= droq->refill_threshold) {
int desc_refilled = octeon_droq_refill(oct, droq);
if (desc_refilled) {
/* Flush the droq descriptor data to memory to
* be sure that when we update the credits the
* data in memory is accurate.
*/
wmb();
writel(desc_refilled, droq->pkts_credit_reg);
}
}
} /* for (each packet)... */
/* Increment refill_count by the number of buffers processed. */
droq->stats.pkts_received += pkt;
droq->stats.bytes_received += total_len;
retval = pkt;
if ((droq->ops.drop_on_max) && (pkts_to_process - pkt)) {
octeon_droq_drop_packets(oct, droq, (pkts_to_process - pkt));
droq->stats.dropped_toomany += (pkts_to_process - pkt);
retval = pkts_to_process;
}
atomic_sub(retval, &droq->pkts_pending);
if (droq->refill_count >= droq->refill_threshold &&
readl(droq->pkts_credit_reg) < CN23XX_SLI_DEF_BP) {
octeon_droq_check_hw_for_pkts(droq);
/* Make sure there are no pkts_pending */
if (!atomic_read(&droq->pkts_pending))
octeon_schedule_rxq_oom_work(oct, droq);
}
return retval;
}
int
octeon_droq_process_packets(struct octeon_device *oct,
struct octeon_droq *droq,
u32 budget)
{
u32 pkt_count = 0;
struct list_head *tmp, *tmp2;
octeon_droq_check_hw_for_pkts(droq);
pkt_count = atomic_read(&droq->pkts_pending);
if (!pkt_count)
return 0;
if (pkt_count > budget)
pkt_count = budget;
octeon_droq_fast_process_packets(oct, droq, pkt_count);
list_for_each_safe(tmp, tmp2, &droq->dispatch_list) {
struct __dispatch *rdisp = (struct __dispatch *)tmp;
list_del(tmp);
rdisp->disp_fn(rdisp->rinfo,
octeon_get_dispatch_arg
(oct,
(u16)rdisp->rinfo->recv_pkt->rh.r.opcode,
(u16)rdisp->rinfo->recv_pkt->rh.r.subcode));
}
/* If there are packets pending. schedule tasklet again */
if (atomic_read(&droq->pkts_pending))
return 1;
return 0;
}
EXPORT_SYMBOL_GPL(octeon_droq_process_packets);
/*
* Utility function to poll for packets. check_hw_for_packets must be
* called before calling this routine.
*/
int
octeon_droq_process_poll_pkts(struct octeon_device *oct,
struct octeon_droq *droq, u32 budget)
{
struct list_head *tmp, *tmp2;
u32 pkts_available = 0, pkts_processed = 0;
u32 total_pkts_processed = 0;
if (budget > droq->max_count)
budget = droq->max_count;
while (total_pkts_processed < budget) {
octeon_droq_check_hw_for_pkts(droq);
pkts_available = min((budget - total_pkts_processed),
(u32)(atomic_read(&droq->pkts_pending)));
if (pkts_available == 0)
break;
pkts_processed =
octeon_droq_fast_process_packets(oct, droq,
pkts_available);
total_pkts_processed += pkts_processed;
}
list_for_each_safe(tmp, tmp2, &droq->dispatch_list) {
struct __dispatch *rdisp = (struct __dispatch *)tmp;
list_del(tmp);
rdisp->disp_fn(rdisp->rinfo,
octeon_get_dispatch_arg
(oct,
(u16)rdisp->rinfo->recv_pkt->rh.r.opcode,
(u16)rdisp->rinfo->recv_pkt->rh.r.subcode));
}
return total_pkts_processed;
}
/* Enable Pkt Interrupt */
int
octeon_enable_irq(struct octeon_device *oct, u32 q_no)
{
switch (oct->chip_id) {
case OCTEON_CN66XX:
case OCTEON_CN68XX: {
struct octeon_cn6xxx *cn6xxx =
(struct octeon_cn6xxx *)oct->chip;
unsigned long flags;
u32 value;
spin_lock_irqsave
(&cn6xxx->lock_for_droq_int_enb_reg, flags);
value = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB);
value |= (1 << q_no);
octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB, value);
value = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB);
value |= (1 << q_no);
octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB, value);
/* don't bother flushing the enables */
spin_unlock_irqrestore
(&cn6xxx->lock_for_droq_int_enb_reg, flags);
}
break;
case OCTEON_CN23XX_PF_VID:
lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
break;
case OCTEON_CN23XX_VF_VID:
lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
break;
default:
dev_err(&oct->pci_dev->dev, "%s Unknown Chip\n", __func__);
return 1;
}
return 0;
}
int octeon_register_droq_ops(struct octeon_device *oct, u32 q_no,
struct octeon_droq_ops *ops)
{
struct octeon_config *oct_cfg = NULL;
struct octeon_droq *droq;
oct_cfg = octeon_get_conf(oct);
if (!oct_cfg)
return -EINVAL;
if (!(ops)) {
dev_err(&oct->pci_dev->dev, "%s: droq_ops pointer is NULL\n",
__func__);
return -EINVAL;
}
if (q_no >= CFG_GET_OQ_MAX_Q(oct_cfg)) {
dev_err(&oct->pci_dev->dev, "%s: droq id (%d) exceeds MAX (%d)\n",
__func__, q_no, (oct->num_oqs - 1));
return -EINVAL;
}
droq = oct->droq[q_no];
memcpy(&droq->ops, ops, sizeof(struct octeon_droq_ops));
return 0;
}
int octeon_unregister_droq_ops(struct octeon_device *oct, u32 q_no)
{
struct octeon_config *oct_cfg = NULL;
struct octeon_droq *droq;
oct_cfg = octeon_get_conf(oct);
if (!oct_cfg)
return -EINVAL;
if (q_no >= CFG_GET_OQ_MAX_Q(oct_cfg)) {
dev_err(&oct->pci_dev->dev, "%s: droq id (%d) exceeds MAX (%d)\n",
__func__, q_no, oct->num_oqs - 1);
return -EINVAL;
}
droq = oct->droq[q_no];
if (!droq) {
dev_info(&oct->pci_dev->dev,
"Droq id (%d) not available.\n", q_no);
return 0;
}
droq->ops.fptr = NULL;
droq->ops.farg = NULL;
droq->ops.drop_on_max = 0;
return 0;
}
EXPORT_SYMBOL_GPL(octeon_unregister_droq_ops);
int octeon_create_droq(struct octeon_device *oct,
u32 q_no, u32 num_descs,
u32 desc_size, void *app_ctx)
{
struct octeon_droq *droq;
int numa_node = dev_to_node(&oct->pci_dev->dev);
if (oct->droq[q_no]) {
dev_dbg(&oct->pci_dev->dev, "Droq already in use. Cannot create droq %d again\n",
q_no);
return 1;
}
/* Allocate the DS for the new droq. */
droq = vmalloc_node(sizeof(*droq), numa_node);
if (!droq)
droq = vmalloc(sizeof(*droq));
if (!droq)
return -1;
memset(droq, 0, sizeof(struct octeon_droq));
/*Disable the pkt o/p for this Q */
octeon_set_droq_pkt_op(oct, q_no, 0);
oct->droq[q_no] = droq;
/* Initialize the Droq */
if (octeon_init_droq(oct, q_no, num_descs, desc_size, app_ctx)) {
vfree(oct->droq[q_no]);
oct->droq[q_no] = NULL;
return -1;
}
oct->num_oqs++;
dev_dbg(&oct->pci_dev->dev, "%s: Total number of OQ: %d\n", __func__,
oct->num_oqs);
/* Global Droq register settings */
/* As of now not required, as setting are done for all 32 Droqs at
* the same time.
*/
return 0;
}
@@ -0,0 +1,414 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file octeon_droq.h
* \brief Implementation of Octeon Output queues. "Output" is with
* respect to the Octeon device on the NIC. From this driver's point of
* view they are ingress queues.
*/
#ifndef __OCTEON_DROQ_H__
#define __OCTEON_DROQ_H__
/* Default number of packets that will be processed in one iteration. */
#define MAX_PACKET_BUDGET 0xFFFFFFFF
/** Octeon descriptor format.
* The descriptor ring is made of descriptors which have 2 64-bit values:
* -# Physical (bus) address of the data buffer.
* -# Physical (bus) address of a octeon_droq_info structure.
* The Octeon device DMA's incoming packets and its information at the address
* given by these descriptor fields.
*/
struct octeon_droq_desc {
/** The buffer pointer */
u64 buffer_ptr;
/** The Info pointer */
u64 info_ptr;
};
#define OCT_DROQ_DESC_SIZE (sizeof(struct octeon_droq_desc))
/** Information about packet DMA'ed by Octeon.
* The format of the information available at Info Pointer after Octeon
* has posted a packet. Not all descriptors have valid information. Only
* the Info field of the first descriptor for a packet has information
* about the packet.
*/
struct octeon_droq_info {
/** The Length of the packet. */
u64 length;
/** The Output Receive Header. */
union octeon_rh rh;
};
#define OCT_DROQ_INFO_SIZE (sizeof(struct octeon_droq_info))
struct octeon_skb_page_info {
/* DMA address for the page */
dma_addr_t dma;
/* Page for the rx dma **/
struct page *page;
/** which offset into page */
unsigned int page_offset;
};
/** Pointer to data buffer.
* Driver keeps a pointer to the data buffer that it made available to
* the Octeon device. Since the descriptor ring keeps physical (bus)
* addresses, this field is required for the driver to keep track of
* the virtual address pointers.
*/
struct octeon_recv_buffer {
/** Packet buffer, including metadata. */
void *buffer;
/** Data in the packet buffer. */
u8 *data;
/** pg_info **/
struct octeon_skb_page_info pg_info;
};
#define OCT_DROQ_RECVBUF_SIZE (sizeof(struct octeon_recv_buffer))
/** Output Queue statistics. Each output queue has four stats fields. */
struct oct_droq_stats {
/** Number of packets received in this queue. */
u64 pkts_received;
/** Bytes received by this queue. */
u64 bytes_received;
/** Packets dropped due to no dispatch function. */
u64 dropped_nodispatch;
/** Packets dropped due to no memory available. */
u64 dropped_nomem;
/** Packets dropped due to large number of pkts to process. */
u64 dropped_toomany;
/** Number of packets sent to stack from this queue. */
u64 rx_pkts_received;
/** Number of Bytes sent to stack from this queue. */
u64 rx_bytes_received;
/** Num of Packets dropped due to receive path failures. */
u64 rx_dropped;
u64 rx_vxlan;
/** Num of failures of recv_buffer_alloc() */
u64 rx_alloc_failure;
};
/* The maximum number of buffers that can be dispatched from the
* output/dma queue. Set to 64 assuming 1K buffers in DROQ and the fact that
* max packet size from DROQ is 64K.
*/
#define MAX_RECV_BUFS 64
/** Receive Packet format used when dispatching output queue packets
* with non-raw opcodes.
* The received packet will be sent to the upper layers using this
* structure which is passed as a parameter to the dispatch function
*/
struct octeon_recv_pkt {
/** Number of buffers in this received packet */
u16 buffer_count;
/** Id of the device that is sending the packet up */
u16 octeon_id;
/** Length of data in the packet buffer */
u32 length;
/** The receive header */
union octeon_rh rh;
/** Pointer to the OS-specific packet buffer */
void *buffer_ptr[MAX_RECV_BUFS];
/** Size of the buffers pointed to by ptr's in buffer_ptr */
u32 buffer_size[MAX_RECV_BUFS];
};
#define OCT_RECV_PKT_SIZE (sizeof(struct octeon_recv_pkt))
/** The first parameter of a dispatch function.
* For a raw mode opcode, the driver dispatches with the device
* pointer in this structure.
* For non-raw mode opcode, the driver dispatches the recv_pkt
* created to contain the buffers with data received from Octeon.
* ---------------------
* | *recv_pkt ----|---
* |-------------------| |
* | 0 or more bytes | |
* | reserved by driver| |
* |-------------------|<-/
* | octeon_recv_pkt |
* | |
* |___________________|
*/
struct octeon_recv_info {
void *rsvd;
struct octeon_recv_pkt *recv_pkt;
};
#define OCT_RECV_INFO_SIZE (sizeof(struct octeon_recv_info))
/** Allocate a recv_info structure. The recv_pkt pointer in the recv_info
* structure is filled in before this call returns.
* @param extra_bytes - extra bytes to be allocated at the end of the recv info
* structure.
* @return - pointer to a newly allocated recv_info structure.
*/
static inline struct octeon_recv_info *octeon_alloc_recv_info(int extra_bytes)
{
struct octeon_recv_info *recv_info;
u8 *buf;
buf = kmalloc(OCT_RECV_PKT_SIZE + OCT_RECV_INFO_SIZE +
extra_bytes, GFP_ATOMIC);
if (!buf)
return NULL;
recv_info = (struct octeon_recv_info *)buf;
recv_info->recv_pkt =
(struct octeon_recv_pkt *)(buf + OCT_RECV_INFO_SIZE);
recv_info->rsvd = NULL;
if (extra_bytes)
recv_info->rsvd = buf + OCT_RECV_INFO_SIZE + OCT_RECV_PKT_SIZE;
return recv_info;
}
/** Free a recv_info structure.
* @param recv_info - Pointer to receive_info to be freed
*/
static inline void octeon_free_recv_info(struct octeon_recv_info *recv_info)
{
kfree(recv_info);
}
typedef int (*octeon_dispatch_fn_t)(struct octeon_recv_info *, void *);
/** Used by NIC module to register packet handler and to get device
* information for each octeon device.
*/
struct octeon_droq_ops {
/** This registered function will be called by the driver with
* the octeon id, pointer to buffer from droq and length of
* data in the buffer. The receive header gives the port
* number to the caller. Function pointer is set by caller.
*/
void (*fptr)(u32, void *, u32, union octeon_rh *, void *, void *);
void *farg;
/* This function will be called by the driver for all NAPI related
* events. The first param is the octeon id. The second param is the
* output queue number. The third is the NAPI event that occurred.
*/
void (*napi_fn)(void *);
u32 poll_mode;
/** Flag indicating if the DROQ handler should drop packets that
* it cannot handle in one iteration. Set by caller.
*/
u32 drop_on_max;
};
/** The Descriptor Ring Output Queue structure.
* This structure has all the information required to implement a
* Octeon DROQ.
*/
struct octeon_droq {
u32 q_no;
u32 pkt_count;
struct octeon_droq_ops ops;
struct octeon_device *oct_dev;
/** The 8B aligned descriptor ring starts at this address. */
struct octeon_droq_desc *desc_ring;
/** Index in the ring where the driver should read the next packet */
u32 read_idx;
/** Index in the ring where Octeon will write the next packet */
u32 write_idx;
/** Index in the ring where the driver will refill the descriptor's
* buffer
*/
u32 refill_idx;
/** Packets pending to be processed */
atomic_t pkts_pending;
/** Number of descriptors in this ring. */
u32 max_count;
/** The number of descriptors pending refill. */
u32 refill_count;
u32 pkts_per_intr;
u32 refill_threshold;
/** The max number of descriptors in DROQ without a buffer.
* This field is used to keep track of empty space threshold. If the
* refill_count reaches this value, the DROQ cannot accept a max-sized
* (64K) packet.
*/
u32 max_empty_descs;
/** The receive buffer list. This list has the virtual addresses of the
* buffers.
*/
struct octeon_recv_buffer *recv_buf_list;
/** The size of each buffer pointed by the buffer pointer. */
u32 buffer_size;
/** Pointer to the mapped packet credit register.
* Host writes number of info/buffer ptrs available to this register
*/
void __iomem *pkts_credit_reg;
/** Pointer to the mapped packet sent register.
* Octeon writes the number of packets DMA'ed to host memory
* in this register.
*/
void __iomem *pkts_sent_reg;
struct list_head dispatch_list;
/** Statistics for this DROQ. */
struct oct_droq_stats stats;
/** DMA mapped address of the DROQ descriptor ring. */
size_t desc_ring_dma;
/** application context */
void *app_ctx;
struct napi_struct napi;
u32 cpu_id;
call_single_data_t csd;
};
#define OCT_DROQ_SIZE (sizeof(struct octeon_droq))
/**
* Allocates space for the descriptor ring for the droq and sets the
* base addr, num desc etc in Octeon registers.
*
* @param oct_dev - pointer to the octeon device structure
* @param q_no - droq no. ranges from 0 - 3.
* @param app_ctx - pointer to application context
* @return Success: 0 Failure: 1
*/
int octeon_init_droq(struct octeon_device *oct_dev,
u32 q_no,
u32 num_descs,
u32 desc_size,
void *app_ctx);
/**
* Frees the space for descriptor ring for the droq.
*
* @param oct_dev - pointer to the octeon device structure
* @param q_no - droq no. ranges from 0 - 3.
* @return: Success: 0 Failure: 1
*/
int octeon_delete_droq(struct octeon_device *oct_dev, u32 q_no);
/** Register a change in droq operations. The ops field has a pointer to a
* function which will called by the DROQ handler for all packets arriving
* on output queues given by q_no irrespective of the type of packet.
* The ops field also has a flag which if set tells the DROQ handler to
* drop packets if it receives more than what it can process in one
* invocation of the handler.
* @param oct - octeon device
* @param q_no - octeon output queue number (0 <= q_no <= MAX_OCTEON_DROQ-1
* @param ops - the droq_ops settings for this queue
* @return - 0 on success, -ENODEV or -EINVAL on error.
*/
int
octeon_register_droq_ops(struct octeon_device *oct,
u32 q_no,
struct octeon_droq_ops *ops);
/** Resets the function pointer and flag settings made by
* octeon_register_droq_ops(). After this routine is called, the DROQ handler
* will lookup dispatch function for each arriving packet on the output queue
* given by q_no.
* @param oct - octeon device
* @param q_no - octeon output queue number (0 <= q_no <= MAX_OCTEON_DROQ-1
* @return - 0 on success, -ENODEV or -EINVAL on error.
*/
int octeon_unregister_droq_ops(struct octeon_device *oct, u32 q_no);
/** Register a dispatch function for a opcode/subcode. The driver will call
* this dispatch function when it receives a packet with the given
* opcode/subcode in its output queues along with the user specified
* argument.
* @param oct - the octeon device to register with.
* @param opcode - the opcode for which the dispatch will be registered.
* @param subcode - the subcode for which the dispatch will be registered
* @param fn - the dispatch function.
* @param fn_arg - user specified that will be passed along with the
* dispatch function by the driver.
* @return Success: 0; Failure: 1
*/
int octeon_register_dispatch_fn(struct octeon_device *oct,
u16 opcode,
u16 subcode,
octeon_dispatch_fn_t fn, void *fn_arg);
void *octeon_get_dispatch_arg(struct octeon_device *oct,
u16 opcode, u16 subcode);
u32 octeon_droq_check_hw_for_pkts(struct octeon_droq *droq);
int octeon_create_droq(struct octeon_device *oct, u32 q_no,
u32 num_descs, u32 desc_size, void *app_ctx);
int octeon_droq_process_packets(struct octeon_device *oct,
struct octeon_droq *droq,
u32 budget);
int octeon_droq_process_poll_pkts(struct octeon_device *oct,
struct octeon_droq *droq, u32 budget);
int octeon_enable_irq(struct octeon_device *oct, u32 q_no);
int octeon_retry_droq_refill(struct octeon_droq *droq);
#endif /*__OCTEON_DROQ_H__ */
@@ -0,0 +1,396 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file octeon_iq.h
* \brief Host Driver: Implementation of Octeon input queues. "Input" is
* with respect to the Octeon device on the NIC. From this driver's
* point of view they are egress queues.
*/
#ifndef __OCTEON_IQ_H__
#define __OCTEON_IQ_H__
#define IQ_STATUS_RUNNING 1
#define IQ_SEND_OK 0
#define IQ_SEND_STOP 1
#define IQ_SEND_FAILED -1
/*------------------------- INSTRUCTION QUEUE --------------------------*/
/* \cond */
#define REQTYPE_NONE 0
#define REQTYPE_NORESP_NET 1
#define REQTYPE_NORESP_NET_SG 2
#define REQTYPE_RESP_NET 3
#define REQTYPE_RESP_NET_SG 4
#define REQTYPE_SOFT_COMMAND 5
#define REQTYPE_LAST 5
struct octeon_request_list {
u32 reqtype;
void *buf;
};
/* \endcond */
/** Input Queue statistics. Each input queue has four stats fields. */
struct oct_iq_stats {
u64 instr_posted; /**< Instructions posted to this queue. */
u64 instr_processed; /**< Instructions processed in this queue. */
u64 instr_dropped; /**< Instructions that could not be processed */
u64 bytes_sent; /**< Bytes sent through this queue. */
u64 sgentry_sent;/**< Gather entries sent through this queue. */
u64 tx_done;/**< Num of packets sent to network. */
u64 tx_iq_busy;/**< Numof times this iq was found to be full. */
u64 tx_dropped;/**< Numof pkts dropped dueto xmitpath errors. */
u64 tx_tot_bytes;/**< Total count of bytes sento to network. */
u64 tx_gso; /* count of tso */
u64 tx_vxlan; /* tunnel */
u64 tx_dmamap_fail; /* Number of times dma mapping failed */
u64 tx_restart; /* Number of times this queue restarted */
};
#define OCT_IQ_STATS_SIZE (sizeof(struct oct_iq_stats))
/** The instruction (input) queue.
* The input queue is used to post raw (instruction) mode data or packet
* data to Octeon device from the host. Each input queue (upto 4) for
* a Octeon device has one such structure to represent it.
*/
struct octeon_instr_queue {
struct octeon_device *oct_dev;
/** A spinlock to protect access to the input ring. */
spinlock_t lock;
/** A spinlock to protect while posting on the ring. */
spinlock_t post_lock;
/** This flag indicates if the queue can be used for soft commands.
* If this flag is set, post_lock must be acquired before posting
* a command to the queue.
* If this flag is clear, post_lock is invalid for the queue.
* All control commands (soft commands) will go through only Queue 0
* (control and data queue). So only queue-0 needs post_lock,
* other queues are only data queues and does not need post_lock
*/
bool allow_soft_cmds;
u32 pkt_in_done;
u32 pkts_processed;
/** A spinlock to protect access to the input ring.*/
spinlock_t iq_flush_running_lock;
/** Flag that indicates if the queue uses 64 byte commands. */
u32 iqcmd_64B:1;
/** Queue info. */
union oct_txpciq txpciq;
u32 rsvd:17;
/* Controls whether extra flushing of IQ is done on Tx */
u32 do_auto_flush:1;
u32 status:8;
/** Maximum no. of instructions in this queue. */
u32 max_count;
/** Index in input ring where the driver should write the next packet */
u32 host_write_index;
/** Index in input ring where Octeon is expected to read the next
* packet.
*/
u32 octeon_read_index;
/** This index aids in finding the window in the queue where Octeon
* has read the commands.
*/
u32 flush_index;
/** This field keeps track of the instructions pending in this queue. */
atomic_t instr_pending;
u32 reset_instr_cnt;
/** Pointer to the Virtual Base addr of the input ring. */
u8 *base_addr;
struct octeon_request_list *request_list;
/** Octeon doorbell register for the ring. */
void __iomem *doorbell_reg;
/** Octeon instruction count register for this ring. */
void __iomem *inst_cnt_reg;
/** Number of instructions pending to be posted to Octeon. */
u32 fill_cnt;
/** The max. number of instructions that can be held pending by the
* driver.
*/
u32 fill_threshold;
/** The last time that the doorbell was rung. */
u64 last_db_time;
/** The doorbell timeout. If the doorbell was not rung for this time and
* fill_cnt is non-zero, ring the doorbell again.
*/
u32 db_timeout;
/** Statistics for this input queue. */
struct oct_iq_stats stats;
/** DMA mapped base address of the input descriptor ring. */
dma_addr_t base_addr_dma;
/** Application context */
void *app_ctx;
/* network stack queue index */
int q_index;
/*os ifidx associated with this queue */
int ifidx;
};
/*---------------------- INSTRUCTION FORMAT ----------------------------*/
/** 32-byte instruction format.
* Format of instruction for a 32-byte mode input queue.
*/
struct octeon_instr_32B {
/** Pointer where the input data is available. */
u64 dptr;
/** Instruction Header. */
u64 ih;
/** Pointer where the response for a RAW mode packet will be written
* by Octeon.
*/
u64 rptr;
/** Input Request Header. Additional info about the input. */
u64 irh;
};
#define OCT_32B_INSTR_SIZE (sizeof(struct octeon_instr_32B))
/** 64-byte instruction format.
* Format of instruction for a 64-byte mode input queue.
*/
struct octeon_instr2_64B {
/** Pointer where the input data is available. */
u64 dptr;
/** Instruction Header. */
u64 ih2;
/** Input Request Header. */
u64 irh;
/** opcode/subcode specific parameters */
u64 ossp[2];
/** Return Data Parameters */
u64 rdp;
/** Pointer where the response for a RAW mode packet will be written
* by Octeon.
*/
u64 rptr;
u64 reserved;
};
struct octeon_instr3_64B {
/** Pointer where the input data is available. */
u64 dptr;
/** Instruction Header. */
u64 ih3;
/** Instruction Header. */
u64 pki_ih3;
/** Input Request Header. */
u64 irh;
/** opcode/subcode specific parameters */
u64 ossp[2];
/** Return Data Parameters */
u64 rdp;
/** Pointer where the response for a RAW mode packet will be written
* by Octeon.
*/
u64 rptr;
};
union octeon_instr_64B {
struct octeon_instr2_64B cmd2;
struct octeon_instr3_64B cmd3;
};
#define OCT_64B_INSTR_SIZE (sizeof(union octeon_instr_64B))
/** The size of each buffer in soft command buffer pool
*/
#define SOFT_COMMAND_BUFFER_SIZE 2048
struct octeon_soft_command {
/** Soft command buffer info. */
struct list_head node;
u64 dma_addr;
u32 size;
/** Command and return status */
union octeon_instr_64B cmd;
#define COMPLETION_WORD_INIT 0xffffffffffffffffULL
u64 *status_word;
/** Data buffer info */
void *virtdptr;
u64 dmadptr;
u32 datasize;
/** Return buffer info */
void *virtrptr;
u64 dmarptr;
u32 rdatasize;
/** Context buffer info */
void *ctxptr;
u32 ctxsize;
/** Time out and callback */
size_t expiry_time;
u32 iq_no;
void (*callback)(struct octeon_device *, u32, void *);
void *callback_arg;
int caller_is_done;
u32 sc_status;
struct completion complete;
};
/* max timeout (in milli sec) for soft request */
#define LIO_SC_MAX_TMO_MS 60000
/** Maximum number of buffers to allocate into soft command buffer pool
*/
#define MAX_SOFT_COMMAND_BUFFERS 256
/** Head of a soft command buffer pool.
*/
struct octeon_sc_buffer_pool {
/** List structure to add delete pending entries to */
struct list_head head;
/** A lock for this response list */
spinlock_t lock;
atomic_t alloc_buf_count;
};
#define INCR_INSTRQUEUE_PKT_COUNT(octeon_dev_ptr, iq_no, field, count) \
(((octeon_dev_ptr)->instr_queue[iq_no]->stats.field) += count)
int octeon_setup_sc_buffer_pool(struct octeon_device *oct);
int octeon_free_sc_done_list(struct octeon_device *oct);
int octeon_free_sc_zombie_list(struct octeon_device *oct);
int octeon_free_sc_buffer_pool(struct octeon_device *oct);
struct octeon_soft_command *
octeon_alloc_soft_command(struct octeon_device *oct,
u32 datasize, u32 rdatasize,
u32 ctxsize);
void octeon_free_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc);
/**
* octeon_init_instr_queue()
* @param octeon_dev - pointer to the octeon device structure.
* @param txpciq - queue to be initialized (0 <= q_no <= 3).
*
* Called at driver init time for each input queue. iq_conf has the
* configuration parameters for the queue.
*
* @return Success: 0 Failure: 1
*/
int octeon_init_instr_queue(struct octeon_device *octeon_dev,
union oct_txpciq txpciq,
u32 num_descs);
/**
* octeon_delete_instr_queue()
* @param octeon_dev - pointer to the octeon device structure.
* @param iq_no - queue to be deleted (0 <= q_no <= 3).
*
* Called at driver unload time for each input queue. Deletes all
* allocated resources for the input queue.
*
* @return Success: 0 Failure: 1
*/
int octeon_delete_instr_queue(struct octeon_device *octeon_dev, u32 iq_no);
int lio_wait_for_instr_fetch(struct octeon_device *oct);
void
octeon_ring_doorbell_locked(struct octeon_device *oct, u32 iq_no);
int
octeon_register_reqtype_free_fn(struct octeon_device *oct, int reqtype,
void (*fn)(void *));
int
lio_process_iq_request_list(struct octeon_device *oct,
struct octeon_instr_queue *iq, u32 napi_budget);
int octeon_send_command(struct octeon_device *oct, u32 iq_no,
u32 force_db, void *cmd, void *buf,
u32 datasize, u32 reqtype);
void octeon_prepare_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc,
u8 opcode, u8 subcode,
u32 irh_ossp, u64 ossp0,
u64 ossp1);
int octeon_send_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc);
int octeon_setup_iq(struct octeon_device *oct, int ifidx,
int q_index, union oct_txpciq iq_no, u32 num_descs,
void *app_ctx);
int
octeon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq,
u32 napi_budget);
#endif /* __OCTEON_IQ_H__ */
@@ -0,0 +1,406 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
#include "octeon_mailbox.h"
#include "cn23xx_pf_device.h"
static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no)
{
struct pci_dev *vfdev = NULL;
int vfidx;
if (!oct->sriov_info.rings_per_vf)
return NULL;
if (q_no % oct->sriov_info.rings_per_vf)
return NULL;
vfidx = q_no / oct->sriov_info.rings_per_vf;
if (vfidx >= oct->sriov_info.num_vfs_alloced)
return NULL;
while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
OCTEON_CN23XX_VF_VID, vfdev))) {
if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev &&
pci_iov_vf_id(vfdev) == vfidx)
return vfdev;
}
return NULL;
}
/**
* octeon_mbox_read:
* @mbox: Pointer mailbox
*
* Reads the 8-bytes of data from the mbox register
* Writes back the acknowldgement inidcating completion of read
*/
int octeon_mbox_read(struct octeon_mbox *mbox)
{
union octeon_mbox_message msg;
int ret = 0;
spin_lock(&mbox->lock);
msg.u64 = readq(mbox->mbox_read_reg);
if ((msg.u64 == OCTEON_PFVFACK) || (msg.u64 == OCTEON_PFVFSIG)) {
spin_unlock(&mbox->lock);
return 0;
}
if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) {
mbox->mbox_req.data[mbox->mbox_req.recv_len - 1] = msg.u64;
mbox->mbox_req.recv_len++;
} else {
if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) {
mbox->mbox_resp.data[mbox->mbox_resp.recv_len - 1] =
msg.u64;
mbox->mbox_resp.recv_len++;
} else {
if ((mbox->state & OCTEON_MBOX_STATE_IDLE) &&
(msg.s.type == OCTEON_MBOX_REQUEST)) {
mbox->state &= ~OCTEON_MBOX_STATE_IDLE;
mbox->state |=
OCTEON_MBOX_STATE_REQUEST_RECEIVING;
mbox->mbox_req.msg.u64 = msg.u64;
mbox->mbox_req.q_no = mbox->q_no;
mbox->mbox_req.recv_len = 1;
} else {
if ((mbox->state &
OCTEON_MBOX_STATE_RESPONSE_PENDING) &&
(msg.s.type == OCTEON_MBOX_RESPONSE)) {
mbox->state &=
~OCTEON_MBOX_STATE_RESPONSE_PENDING;
mbox->state |=
OCTEON_MBOX_STATE_RESPONSE_RECEIVING
;
mbox->mbox_resp.msg.u64 = msg.u64;
mbox->mbox_resp.q_no = mbox->q_no;
mbox->mbox_resp.recv_len = 1;
} else {
writeq(OCTEON_PFVFERR,
mbox->mbox_read_reg);
mbox->state |= OCTEON_MBOX_STATE_ERROR;
spin_unlock(&mbox->lock);
return 1;
}
}
}
}
if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) {
if (mbox->mbox_req.recv_len < mbox->mbox_req.msg.s.len) {
ret = 0;
} else {
mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVING;
mbox->state |= OCTEON_MBOX_STATE_REQUEST_RECEIVED;
ret = 1;
}
} else {
if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) {
if (mbox->mbox_resp.recv_len <
mbox->mbox_resp.msg.s.len) {
ret = 0;
} else {
mbox->state &=
~OCTEON_MBOX_STATE_RESPONSE_RECEIVING;
mbox->state |=
OCTEON_MBOX_STATE_RESPONSE_RECEIVED;
ret = 1;
}
} else {
WARN_ON(1);
}
}
writeq(OCTEON_PFVFACK, mbox->mbox_read_reg);
spin_unlock(&mbox->lock);
return ret;
}
/**
* octeon_mbox_write:
* @oct: Pointer Octeon Device
* @mbox_cmd: Cmd to send to mailbox.
*
* Populates the queue specific mbox structure
* with cmd information.
* Write the cmd to mbox register
*/
int octeon_mbox_write(struct octeon_device *oct,
struct octeon_mbox_cmd *mbox_cmd)
{
struct octeon_mbox *mbox = oct->mbox[mbox_cmd->q_no];
u32 count, i, ret = OCTEON_MBOX_STATUS_SUCCESS;
long timeout = LIO_MBOX_WRITE_WAIT_TIME;
unsigned long flags;
spin_lock_irqsave(&mbox->lock, flags);
if ((mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) &&
!(mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED)) {
spin_unlock_irqrestore(&mbox->lock, flags);
return OCTEON_MBOX_STATUS_FAILED;
}
if ((mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) &&
!(mbox->state & OCTEON_MBOX_STATE_IDLE)) {
spin_unlock_irqrestore(&mbox->lock, flags);
return OCTEON_MBOX_STATUS_BUSY;
}
if (mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) {
memcpy(&mbox->mbox_resp, mbox_cmd,
sizeof(struct octeon_mbox_cmd));
mbox->state = OCTEON_MBOX_STATE_RESPONSE_PENDING;
}
spin_unlock_irqrestore(&mbox->lock, flags);
count = 0;
while (readq(mbox->mbox_write_reg) != OCTEON_PFVFSIG) {
schedule_timeout_uninterruptible(timeout);
if (count++ == LIO_MBOX_WRITE_WAIT_CNT) {
ret = OCTEON_MBOX_STATUS_FAILED;
break;
}
}
if (ret == OCTEON_MBOX_STATUS_SUCCESS) {
writeq(mbox_cmd->msg.u64, mbox->mbox_write_reg);
for (i = 0; i < (u32)(mbox_cmd->msg.s.len - 1); i++) {
count = 0;
while (readq(mbox->mbox_write_reg) !=
OCTEON_PFVFACK) {
schedule_timeout_uninterruptible(timeout);
if (count++ == LIO_MBOX_WRITE_WAIT_CNT) {
ret = OCTEON_MBOX_STATUS_FAILED;
break;
}
}
if (ret == OCTEON_MBOX_STATUS_SUCCESS)
writeq(mbox_cmd->data[i], mbox->mbox_write_reg);
else
break;
}
}
spin_lock_irqsave(&mbox->lock, flags);
if (mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) {
mbox->state = OCTEON_MBOX_STATE_IDLE;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
} else {
if ((!mbox_cmd->msg.s.resp_needed) ||
(ret == OCTEON_MBOX_STATUS_FAILED)) {
mbox->state &= ~OCTEON_MBOX_STATE_RESPONSE_PENDING;
if (!(mbox->state &
(OCTEON_MBOX_STATE_REQUEST_RECEIVING |
OCTEON_MBOX_STATE_REQUEST_RECEIVED)))
mbox->state = OCTEON_MBOX_STATE_IDLE;
}
}
spin_unlock_irqrestore(&mbox->lock, flags);
return ret;
}
static void get_vf_stats(struct octeon_device *oct,
struct oct_vf_stats *stats)
{
int i;
for (i = 0; i < oct->num_iqs; i++) {
if (!oct->instr_queue[i])
continue;
stats->tx_packets += oct->instr_queue[i]->stats.tx_done;
stats->tx_bytes += oct->instr_queue[i]->stats.tx_tot_bytes;
}
for (i = 0; i < oct->num_oqs; i++) {
if (!oct->droq[i])
continue;
stats->rx_packets += oct->droq[i]->stats.rx_pkts_received;
stats->rx_bytes += oct->droq[i]->stats.rx_bytes_received;
}
}
/**
* octeon_mbox_process_cmd:
* @mbox: Pointer mailbox
* @mbox_cmd: Pointer to command received
*
* Process the cmd received in mbox
*/
static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
struct octeon_mbox_cmd *mbox_cmd)
{
struct octeon_device *oct = mbox->oct_dev;
struct pci_dev *vfdev;
switch (mbox_cmd->msg.s.cmd) {
case OCTEON_VF_ACTIVE:
dev_dbg(&oct->pci_dev->dev, "got vfactive sending data back\n");
mbox_cmd->msg.s.type = OCTEON_MBOX_RESPONSE;
mbox_cmd->msg.s.resp_needed = 1;
mbox_cmd->msg.s.len = 2;
mbox_cmd->data[0] = 0; /* VF version is in mbox_cmd->data[0] */
((struct lio_version *)&mbox_cmd->data[0])->major =
LIQUIDIO_BASE_MAJOR_VERSION;
((struct lio_version *)&mbox_cmd->data[0])->minor =
LIQUIDIO_BASE_MINOR_VERSION;
((struct lio_version *)&mbox_cmd->data[0])->micro =
LIQUIDIO_BASE_MICRO_VERSION;
memcpy(mbox_cmd->msg.s.params, (uint8_t *)&oct->pfvf_hsword, 6);
/* Sending core cofig info to the corresponding active VF.*/
octeon_mbox_write(oct, mbox_cmd);
break;
case OCTEON_VF_FLR_REQUEST:
dev_info(&oct->pci_dev->dev,
"got a request for FLR from VF that owns DPI ring %u\n",
mbox->q_no);
vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no);
if (!vfdev)
break;
pcie_flr(vfdev);
pci_dev_put(vfdev);
break;
case OCTEON_PF_CHANGED_VF_MACADDR:
if (OCTEON_CN23XX_VF(oct))
octeon_pf_changed_vf_macaddr(oct,
mbox_cmd->msg.s.params);
break;
case OCTEON_GET_VF_STATS:
dev_dbg(&oct->pci_dev->dev, "Got VF stats request. Sending data back\n");
mbox_cmd->msg.s.type = OCTEON_MBOX_RESPONSE;
mbox_cmd->msg.s.resp_needed = 1;
mbox_cmd->msg.s.len = 1 +
sizeof(struct oct_vf_stats) / sizeof(u64);
get_vf_stats(oct, (struct oct_vf_stats *)mbox_cmd->data);
octeon_mbox_write(oct, mbox_cmd);
break;
default:
break;
}
return 0;
}
/**
* octeon_mbox_process_message
* @mbox: mailbox
*
* Process the received mbox message.
*/
int octeon_mbox_process_message(struct octeon_mbox *mbox)
{
struct octeon_mbox_cmd mbox_cmd;
unsigned long flags;
spin_lock_irqsave(&mbox->lock, flags);
if (mbox->state & OCTEON_MBOX_STATE_ERROR) {
if (mbox->state & (OCTEON_MBOX_STATE_RESPONSE_PENDING |
OCTEON_MBOX_STATE_RESPONSE_RECEIVING)) {
memcpy(&mbox_cmd, &mbox->mbox_resp,
sizeof(struct octeon_mbox_cmd));
mbox->state = OCTEON_MBOX_STATE_IDLE;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
spin_unlock_irqrestore(&mbox->lock, flags);
mbox_cmd.recv_status = 1;
if (mbox_cmd.fn)
mbox_cmd.fn(mbox->oct_dev, &mbox_cmd,
mbox_cmd.fn_arg);
return 0;
}
mbox->state = OCTEON_MBOX_STATE_IDLE;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
spin_unlock_irqrestore(&mbox->lock, flags);
return 0;
}
if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVED) {
memcpy(&mbox_cmd, &mbox->mbox_resp,
sizeof(struct octeon_mbox_cmd));
mbox->state = OCTEON_MBOX_STATE_IDLE;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
spin_unlock_irqrestore(&mbox->lock, flags);
mbox_cmd.recv_status = 0;
if (mbox_cmd.fn)
mbox_cmd.fn(mbox->oct_dev, &mbox_cmd, mbox_cmd.fn_arg);
return 0;
}
if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED) {
memcpy(&mbox_cmd, &mbox->mbox_req,
sizeof(struct octeon_mbox_cmd));
if (!mbox_cmd.msg.s.resp_needed) {
mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVED;
if (!(mbox->state &
OCTEON_MBOX_STATE_RESPONSE_PENDING))
mbox->state = OCTEON_MBOX_STATE_IDLE;
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
}
spin_unlock_irqrestore(&mbox->lock, flags);
octeon_mbox_process_cmd(mbox, &mbox_cmd);
return 0;
}
spin_unlock_irqrestore(&mbox->lock, flags);
WARN_ON(1);
return 0;
}
int octeon_mbox_cancel(struct octeon_device *oct, int q_no)
{
struct octeon_mbox *mbox = oct->mbox[q_no];
struct octeon_mbox_cmd *mbox_cmd;
unsigned long flags = 0;
spin_lock_irqsave(&mbox->lock, flags);
mbox_cmd = &mbox->mbox_resp;
if (!(mbox->state & OCTEON_MBOX_STATE_RESPONSE_PENDING)) {
spin_unlock_irqrestore(&mbox->lock, flags);
return 1;
}
mbox->state = OCTEON_MBOX_STATE_IDLE;
memset(mbox_cmd, 0, sizeof(*mbox_cmd));
writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg);
spin_unlock_irqrestore(&mbox->lock, flags);
return 0;
}
@@ -0,0 +1,125 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
#ifndef __MAILBOX_H__
#define __MAILBOX_H__
/* Macros for Mail Box Communication */
#define OCTEON_MBOX_DATA_MAX 32
#define OCTEON_VF_ACTIVE 0x1
#define OCTEON_VF_FLR_REQUEST 0x2
#define OCTEON_PF_CHANGED_VF_MACADDR 0x4
#define OCTEON_GET_VF_STATS 0x8
/*Macro for Read acknowldgement*/
#define OCTEON_PFVFACK 0xffffffffffffffffULL
#define OCTEON_PFVFSIG 0x1122334455667788ULL
#define OCTEON_PFVFERR 0xDEADDEADDEADDEADULL
#define LIO_MBOX_WRITE_WAIT_CNT 1000
#define LIO_MBOX_WRITE_WAIT_TIME msecs_to_jiffies(1)
enum octeon_mbox_cmd_status {
OCTEON_MBOX_STATUS_SUCCESS = 0,
OCTEON_MBOX_STATUS_FAILED = 1,
OCTEON_MBOX_STATUS_BUSY = 2
};
enum octeon_mbox_message_type {
OCTEON_MBOX_REQUEST = 0,
OCTEON_MBOX_RESPONSE = 1
};
union octeon_mbox_message {
u64 u64;
struct {
u16 type : 1;
u16 resp_needed : 1;
u16 cmd : 6;
u16 len : 8;
u8 params[6];
} s;
};
struct octeon_mbox_cmd;
typedef void (*octeon_mbox_callback_t)(struct octeon_device *,
struct octeon_mbox_cmd *, void *);
struct octeon_mbox_cmd {
union octeon_mbox_message msg;
u64 data[OCTEON_MBOX_DATA_MAX];
u32 q_no;
u32 recv_len;
u32 recv_status;
octeon_mbox_callback_t fn;
void *fn_arg;
};
enum octeon_mbox_state {
OCTEON_MBOX_STATE_IDLE = 1,
OCTEON_MBOX_STATE_REQUEST_RECEIVING = 2,
OCTEON_MBOX_STATE_REQUEST_RECEIVED = 4,
OCTEON_MBOX_STATE_RESPONSE_PENDING = 8,
OCTEON_MBOX_STATE_RESPONSE_RECEIVING = 16,
OCTEON_MBOX_STATE_RESPONSE_RECEIVED = 32,
OCTEON_MBOX_STATE_ERROR = 64
};
struct octeon_mbox {
/** A spinlock to protect access to this q_mbox. */
spinlock_t lock;
struct octeon_device *oct_dev;
u32 q_no;
enum octeon_mbox_state state;
struct cavium_wk mbox_poll_wk;
/** SLI_MAC_PF_MBOX_INT for PF, SLI_PKT_MBOX_INT for VF. */
void *mbox_int_reg;
/** SLI_PKT_PF_VF_MBOX_SIG(0) for PF, SLI_PKT_PF_VF_MBOX_SIG(1) for VF.
*/
void *mbox_write_reg;
/** SLI_PKT_PF_VF_MBOX_SIG(1) for PF, SLI_PKT_PF_VF_MBOX_SIG(0) for VF.
*/
void *mbox_read_reg;
struct octeon_mbox_cmd mbox_req;
struct octeon_mbox_cmd mbox_resp;
};
struct oct_vf_stats_ctx {
atomic_t status;
struct oct_vf_stats *stats;
};
int octeon_mbox_read(struct octeon_mbox *mbox);
int octeon_mbox_write(struct octeon_device *oct,
struct octeon_mbox_cmd *mbox_cmd);
int octeon_mbox_process_message(struct octeon_mbox *mbox);
int octeon_mbox_cancel(struct octeon_device *oct, int q_no);
#endif
@@ -0,0 +1,236 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/
/*! \file octeon_main.h
* \brief Host Driver: This file is included by all host driver source files
* to include common definitions.
*/
#ifndef _OCTEON_MAIN_H_
#define _OCTEON_MAIN_H_
#include <linux/sched/signal.h>
#if BITS_PER_LONG == 32
#define CVM_CAST64(v) ((long long)(v))
#elif BITS_PER_LONG == 64
#define CVM_CAST64(v) ((long long)(long)(v))
#else
#error "Unknown system architecture"
#endif
#define DRV_NAME "LiquidIO"
struct octeon_device_priv {
/** Tasklet structures for this device. */
struct tasklet_struct droq_tasklet;
unsigned long napi_mask;
struct octeon_device *dev;
};
/** This structure is used by NIC driver to store information required
* to free the sk_buff when the packet has been fetched by Octeon.
* Bytes offset below assume worst-case of a 64-bit system.
*/
struct octnet_buf_free_info {
/** Bytes 1-8. Pointer to network device private structure. */
struct lio *lio;
/** Bytes 9-16. Pointer to sk_buff. */
struct sk_buff *skb;
/** Bytes 17-24. Pointer to gather list. */
struct octnic_gather *g;
/** Bytes 25-32. Physical address of skb->data or gather list. */
u64 dptr;
/** Bytes 33-47. Piggybacked soft command, if any */
struct octeon_soft_command *sc;
};
/* BQL-related functions */
int octeon_report_sent_bytes_to_bql(void *buf, int reqtype);
void octeon_update_tx_completion_counters(void *buf, int reqtype,
unsigned int *pkts_compl,
unsigned int *bytes_compl);
void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl,
unsigned int bytes_compl);
void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac);
void octeon_schedule_rxq_oom_work(struct octeon_device *oct,
struct octeon_droq *droq);
/** Swap 8B blocks */
static inline void octeon_swap_8B_data(u64 *data, u32 blocks)
{
while (blocks) {
cpu_to_be64s(data);
blocks--;
data++;
}
}
/**
* \brief unmaps a PCI BAR
* @param oct Pointer to Octeon device
* @param baridx bar index
*/
static inline void octeon_unmap_pci_barx(struct octeon_device *oct, int baridx)
{
dev_dbg(&oct->pci_dev->dev, "Freeing PCI mapped regions for Bar%d\n",
baridx);
if (oct->mmio[baridx].done)
iounmap(oct->mmio[baridx].hw_addr);
if (oct->mmio[baridx].start)
pci_release_region(oct->pci_dev, baridx * 2);
}
/**
* \brief maps a PCI BAR
* @param oct Pointer to Octeon device
* @param baridx bar index
* @param max_map_len maximum length of mapped memory
*/
static inline int octeon_map_pci_barx(struct octeon_device *oct,
int baridx, int max_map_len)
{
u32 mapped_len = 0;
if (pci_request_region(oct->pci_dev, baridx * 2, DRV_NAME)) {
dev_err(&oct->pci_dev->dev, "pci_request_region failed for bar %d\n",
baridx);
return 1;
}
oct->mmio[baridx].start = pci_resource_start(oct->pci_dev, baridx * 2);
oct->mmio[baridx].len = pci_resource_len(oct->pci_dev, baridx * 2);
mapped_len = oct->mmio[baridx].len;
if (!mapped_len)
goto err_release_region;
if (max_map_len && (mapped_len > max_map_len))
mapped_len = max_map_len;
oct->mmio[baridx].hw_addr =
ioremap(oct->mmio[baridx].start, mapped_len);
oct->mmio[baridx].mapped_len = mapped_len;
dev_dbg(&oct->pci_dev->dev, "BAR%d start: 0x%llx mapped %u of %u bytes\n",
baridx, oct->mmio[baridx].start, mapped_len,
oct->mmio[baridx].len);
if (!oct->mmio[baridx].hw_addr) {
dev_err(&oct->pci_dev->dev, "error ioremap for bar %d\n",
baridx);
goto err_release_region;
}
oct->mmio[baridx].done = 1;
return 0;
err_release_region:
pci_release_region(oct->pci_dev, baridx * 2);
return 1;
}
/* input parameter:
* sc: pointer to a soft request
* timeout: milli sec which an application wants to wait for the
response of the request.
* 0: the request will wait until its response gets back
* from the firmware within LIO_SC_MAX_TMO_MS milli sec.
* If the response does not return within
* LIO_SC_MAX_TMO_MS milli sec, lio_process_ordered_list()
* will move the request to zombie response list.
*
* return value:
* 0: got the response from firmware for the sc request.
* errno -EINTR: user abort the command.
* errno -ETIME: user spefified timeout value has been expired.
* errno -EBUSY: the response of the request does not return in
* resonable time (LIO_SC_MAX_TMO_MS).
* the sc wll be move to zombie response list by
* lio_process_ordered_list()
*
* A request with non-zero return value, the sc->caller_is_done
* will be marked 1.
* When getting a request with zero return value, the requestor
* should mark sc->caller_is_done with 1 after examing the
* response of sc.
* lio_process_ordered_list() will free the soft command on behalf
* of the soft command requestor.
* This is to fix the possible race condition of both timeout process
* and lio_process_ordered_list()/callback function to free a
* sc strucutre.
*/
static inline int
wait_for_sc_completion_timeout(struct octeon_device *oct_dev,
struct octeon_soft_command *sc,
unsigned long timeout)
{
int errno = 0;
long timeout_jiff;
if (timeout)
timeout_jiff = msecs_to_jiffies(timeout);
else
timeout_jiff = MAX_SCHEDULE_TIMEOUT;
timeout_jiff =
wait_for_completion_interruptible_timeout(&sc->complete,
timeout_jiff);
if (timeout_jiff == 0) {
dev_err(&oct_dev->pci_dev->dev, "%s: sc is timeout\n",
__func__);
WRITE_ONCE(sc->caller_is_done, true);
errno = -ETIME;
} else if (timeout_jiff == -ERESTARTSYS) {
dev_err(&oct_dev->pci_dev->dev, "%s: sc is interrupted\n",
__func__);
WRITE_ONCE(sc->caller_is_done, true);
errno = -EINTR;
} else if (sc->sc_status == OCTEON_REQUEST_TIMEOUT) {
dev_err(&oct_dev->pci_dev->dev, "%s: sc has fatal timeout\n",
__func__);
WRITE_ONCE(sc->caller_is_done, true);
errno = -EBUSY;
}
return errno;
}
#ifndef ROUNDUP4
#define ROUNDUP4(val) (((val) + 3) & 0xfffffffc)
#endif
#ifndef ROUNDUP8
#define ROUNDUP8(val) (((val) + 7) & 0xfffffff8)
#endif
#ifndef ROUNDUP16
#define ROUNDUP16(val) (((val) + 15) & 0xfffffff0)
#endif
#ifndef ROUNDUP128
#define ROUNDUP128(val) (((val) + 127) & 0xffffff80)
#endif
#endif /* _OCTEON_MAIN_H_ */
@@ -0,0 +1,206 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_mem_ops.h"
#define MEMOPS_IDX BAR1_INDEX_DYNAMIC_MAP
#ifdef __BIG_ENDIAN_BITFIELD
static inline void
octeon_toggle_bar1_swapmode(struct octeon_device *oct, u32 idx)
{
u32 mask;
mask = oct->fn_list.bar1_idx_read(oct, idx);
mask = (mask & 0x2) ? (mask & ~2) : (mask | 2);
oct->fn_list.bar1_idx_write(oct, idx, mask);
}
#else
#define octeon_toggle_bar1_swapmode(oct, idx)
#endif
static void
octeon_pci_fastwrite(struct octeon_device *oct, u8 __iomem *mapped_addr,
u8 *hostbuf, u32 len)
{
while ((len) && ((unsigned long)mapped_addr) & 7) {
writeb(*(hostbuf++), mapped_addr++);
len--;
}
octeon_toggle_bar1_swapmode(oct, MEMOPS_IDX);
while (len >= 8) {
writeq(*((u64 *)hostbuf), mapped_addr);
mapped_addr += 8;
hostbuf += 8;
len -= 8;
}
octeon_toggle_bar1_swapmode(oct, MEMOPS_IDX);
while (len--)
writeb(*(hostbuf++), mapped_addr++);
}
static void
octeon_pci_fastread(struct octeon_device *oct, u8 __iomem *mapped_addr,
u8 *hostbuf, u32 len)
{
while ((len) && ((unsigned long)mapped_addr) & 7) {
*(hostbuf++) = readb(mapped_addr++);
len--;
}
octeon_toggle_bar1_swapmode(oct, MEMOPS_IDX);
while (len >= 8) {
*((u64 *)hostbuf) = readq(mapped_addr);
mapped_addr += 8;
hostbuf += 8;
len -= 8;
}
octeon_toggle_bar1_swapmode(oct, MEMOPS_IDX);
while (len--)
*(hostbuf++) = readb(mapped_addr++);
}
/* Core mem read/write with temporary bar1 settings. */
/* op = 1 to read, op = 0 to write. */
static void
__octeon_pci_rw_core_mem(struct octeon_device *oct, u64 addr,
u8 *hostbuf, u32 len, u32 op)
{
u32 copy_len = 0, index_reg_val = 0;
unsigned long flags;
u8 __iomem *mapped_addr;
u64 static_mapping_base;
static_mapping_base = oct->console_nb_info.dram_region_base;
if (static_mapping_base &&
static_mapping_base == (addr & ~(OCTEON_BAR1_ENTRY_SIZE - 1ULL))) {
int bar1_index = oct->console_nb_info.bar1_index;
mapped_addr = oct->mmio[1].hw_addr
+ (bar1_index << ilog2(OCTEON_BAR1_ENTRY_SIZE))
+ (addr & (OCTEON_BAR1_ENTRY_SIZE - 1ULL));
if (op)
octeon_pci_fastread(oct, mapped_addr, hostbuf, len);
else
octeon_pci_fastwrite(oct, mapped_addr, hostbuf, len);
return;
}
spin_lock_irqsave(&oct->mem_access_lock, flags);
/* Save the original index reg value. */
index_reg_val = oct->fn_list.bar1_idx_read(oct, MEMOPS_IDX);
do {
oct->fn_list.bar1_idx_setup(oct, addr, MEMOPS_IDX, 1);
mapped_addr = oct->mmio[1].hw_addr
+ (MEMOPS_IDX << 22) + (addr & 0x3fffff);
/* If operation crosses a 4MB boundary, split the transfer
* at the 4MB
* boundary.
*/
if (((addr + len - 1) & ~(0x3fffff)) != (addr & ~(0x3fffff))) {
copy_len = (u32)(((addr & ~(0x3fffff)) +
(MEMOPS_IDX << 22)) - addr);
} else {
copy_len = len;
}
if (op) { /* read from core */
octeon_pci_fastread(oct, mapped_addr, hostbuf,
copy_len);
} else {
octeon_pci_fastwrite(oct, mapped_addr, hostbuf,
copy_len);
}
len -= copy_len;
addr += copy_len;
hostbuf += copy_len;
} while (len);
oct->fn_list.bar1_idx_write(oct, MEMOPS_IDX, index_reg_val);
spin_unlock_irqrestore(&oct->mem_access_lock, flags);
}
void
octeon_pci_read_core_mem(struct octeon_device *oct,
u64 coreaddr,
u8 *buf,
u32 len)
{
__octeon_pci_rw_core_mem(oct, coreaddr, buf, len, 1);
}
EXPORT_SYMBOL_GPL(octeon_pci_read_core_mem);
void
octeon_pci_write_core_mem(struct octeon_device *oct,
u64 coreaddr,
const u8 *buf,
u32 len)
{
__octeon_pci_rw_core_mem(oct, coreaddr, (u8 *)buf, len, 0);
}
EXPORT_SYMBOL_GPL(octeon_pci_write_core_mem);
u64 octeon_read_device_mem64(struct octeon_device *oct, u64 coreaddr)
{
__be64 ret;
__octeon_pci_rw_core_mem(oct, coreaddr, (u8 *)&ret, 8, 1);
return be64_to_cpu(ret);
}
EXPORT_SYMBOL_GPL(octeon_read_device_mem64);
u32 octeon_read_device_mem32(struct octeon_device *oct, u64 coreaddr)
{
__be32 ret;
__octeon_pci_rw_core_mem(oct, coreaddr, (u8 *)&ret, 4, 1);
return be32_to_cpu(ret);
}
EXPORT_SYMBOL_GPL(octeon_read_device_mem32);
void octeon_write_device_mem32(struct octeon_device *oct, u64 coreaddr,
u32 val)
{
__be32 t = cpu_to_be32(val);
__octeon_pci_rw_core_mem(oct, coreaddr, (u8 *)&t, 4, 0);
}
EXPORT_SYMBOL_GPL(octeon_write_device_mem32);
@@ -0,0 +1,72 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
/*! \file octeon_mem_ops.h
* \brief Host Driver: Routines used to read/write Octeon memory.
*/
#ifndef __OCTEON_MEM_OPS_H__
#define __OCTEON_MEM_OPS_H__
/** Read a 64-bit value from a BAR1 mapped core memory address.
* @param oct - pointer to the octeon device.
* @param core_addr - the address to read from.
*
* The range_idx gives the BAR1 index register for the range of address
* in which core_addr is mapped.
*
* @return 64-bit value read from Core memory
*/
u64 octeon_read_device_mem64(struct octeon_device *oct, u64 core_addr);
/** Read a 32-bit value from a BAR1 mapped core memory address.
* @param oct - pointer to the octeon device.
* @param core_addr - the address to read from.
*
* @return 32-bit value read from Core memory
*/
u32 octeon_read_device_mem32(struct octeon_device *oct, u64 core_addr);
/** Write a 32-bit value to a BAR1 mapped core memory address.
* @param oct - pointer to the octeon device.
* @param core_addr - the address to write to.
* @param val - 32-bit value to write.
*/
void
octeon_write_device_mem32(struct octeon_device *oct,
u64 core_addr,
u32 val);
/** Read multiple bytes from Octeon memory.
*/
void
octeon_pci_read_core_mem(struct octeon_device *oct,
u64 coreaddr,
u8 *buf,
u32 len);
/** Write multiple bytes into Octeon memory.
*/
void
octeon_pci_write_core_mem(struct octeon_device *oct,
u64 coreaddr,
const u8 *buf,
u32 len);
#endif
@@ -0,0 +1,626 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
/*! \file octeon_network.h
* \brief Host NIC Driver: Structure and Macro definitions used by NIC Module.
*/
#ifndef __OCTEON_NETWORK_H__
#define __OCTEON_NETWORK_H__
#include <linux/ptp_clock_kernel.h>
#define LIO_MAX_MTU_SIZE (OCTNET_MAX_FRM_SIZE - OCTNET_FRM_HEADER_SIZE)
#define LIO_MIN_MTU_SIZE ETH_MIN_MTU
/* Bit mask values for lio->ifstate */
#define LIO_IFSTATE_DROQ_OPS 0x01
#define LIO_IFSTATE_REGISTERED 0x02
#define LIO_IFSTATE_RUNNING 0x04
#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08
#define LIO_IFSTATE_RESETTING 0x10
struct liquidio_if_cfg_resp {
u64 rh;
struct liquidio_if_cfg_info cfg_info;
u64 status;
};
#define LIO_IFCFG_WAIT_TIME 3000 /* In milli seconds */
#define LIQUIDIO_NDEV_STATS_POLL_TIME_MS 200
/* Structure of a node in list of gather components maintained by
* NIC driver for each network device.
*/
struct octnic_gather {
/* List manipulation. Next and prev pointers. */
struct list_head list;
/* Size of the gather component at sg in bytes. */
int sg_size;
/* Number of bytes that sg was adjusted to make it 8B-aligned. */
int adjust;
/* Gather component that can accommodate max sized fragment list
* received from the IP layer.
*/
struct octeon_sg_entry *sg;
dma_addr_t sg_dma_ptr;
};
struct oct_nic_stats_resp {
u64 rh;
struct oct_link_stats stats;
u64 status;
};
struct oct_nic_vf_stats_resp {
u64 rh;
u64 spoofmac_cnt;
u64 status;
};
struct oct_nic_stats_ctrl {
struct completion complete;
struct net_device *netdev;
};
struct oct_nic_seapi_resp {
u64 rh;
union {
u32 fec_setting;
u32 speed;
};
u64 status;
};
/** LiquidIO per-interface network private data */
struct lio {
/** State of the interface. Rx/Tx happens only in the RUNNING state. */
atomic_t ifstate;
/** Octeon Interface index number. This device will be represented as
* oct<ifidx> in the system.
*/
int ifidx;
/** Octeon Input queue to use to transmit for this network interface. */
int txq;
/** Octeon Output queue from which pkts arrive
* for this network interface.
*/
int rxq;
/** Guards each glist */
spinlock_t *glist_lock;
/** Array of gather component linked lists */
struct list_head *glist;
void **glists_virt_base;
dma_addr_t *glists_dma_base;
u32 glist_entry_size;
/** Pointer to the NIC properties for the Octeon device this network
* interface is associated with.
*/
struct octdev_props *octprops;
/** Pointer to the octeon device structure. */
struct octeon_device *oct_dev;
struct net_device *netdev;
/** Link information sent by the core application for this interface. */
struct oct_link_info linfo;
/** counter of link changes */
u64 link_changes;
/** Size of Tx queue for this octeon device. */
u32 tx_qsize;
/** Size of Rx queue for this octeon device. */
u32 rx_qsize;
/** Size of MTU this octeon device. */
u32 mtu;
/** msg level flag per interface. */
u32 msg_enable;
/** Copy of Interface capabilities: TSO, TSO6, LRO, Chescksums . */
u64 dev_capability;
/* Copy of transmit encapsulation capabilities:
* TSO, TSO6, Checksums for this device for Kernel
* 3.10.0 onwards
*/
u64 enc_dev_capability;
/** Copy of beacaon reg in phy */
u32 phy_beacon_val;
/** Copy of ctrl reg in phy */
u32 led_ctrl_val;
/* PTP clock information */
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
s64 ptp_adjust;
/* for atomic access to Octeon PTP reg and data struct */
spinlock_t ptp_lock;
/* Interface info */
u32 intf_open;
/* work queue for txq status */
struct cavium_wq txq_status_wq;
/* work queue for rxq oom status */
struct cavium_wq rxq_status_wq[MAX_POSSIBLE_OCTEON_OUTPUT_QUEUES];
/* work queue for link status */
struct cavium_wq link_status_wq;
/* work queue to regularly send local time to octeon firmware */
struct cavium_wq sync_octeon_time_wq;
int netdev_uc_count;
struct cavium_wk stats_wk;
};
#define LIO_SIZE (sizeof(struct lio))
#define GET_LIO(netdev) ((struct lio *)netdev_priv(netdev))
#define LIO_MAX_CORES 16
/**
* \brief Enable or disable feature
* @param netdev pointer to network device
* @param cmd Command that just requires acknowledgment
* @param param1 Parameter to command
*/
int liquidio_set_feature(struct net_device *netdev, int cmd, u16 param1);
int setup_rx_oom_poll_fn(struct net_device *netdev);
void cleanup_rx_oom_poll_fn(struct net_device *netdev);
/**
* \brief Link control command completion callback
* @param nctrl_ptr pointer to control packet structure
*
* This routine is called by the callback function when a ctrl pkt sent to
* core app completes. The nctrl_ptr contains a copy of the command type
* and data sent to the core app. This routine is only called if the ctrl
* pkt was sent successfully to the core app.
*/
void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr);
int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx,
u32 num_iqs, u32 num_oqs);
irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)),
void *dev);
int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs);
void lio_fetch_stats(struct work_struct *work);
int lio_wait_for_clean_oq(struct octeon_device *oct);
/**
* \brief Register ethtool operations
* @param netdev pointer to network device
*/
void liquidio_set_ethtool_ops(struct net_device *netdev);
void lio_delete_glists(struct lio *lio);
int lio_setup_glists(struct octeon_device *oct, struct lio *lio, int num_qs);
int liquidio_get_speed(struct lio *lio);
int liquidio_set_speed(struct lio *lio, int speed);
int liquidio_get_fec(struct lio *lio);
int liquidio_set_fec(struct lio *lio, int on_off);
/**
* \brief Net device change_mtu
* @param netdev network device
*/
int liquidio_change_mtu(struct net_device *netdev, int new_mtu);
#define LIO_CHANGE_MTU_SUCCESS 1
#define LIO_CHANGE_MTU_FAIL 2
#define SKB_ADJ_MASK 0x3F
#define SKB_ADJ (SKB_ADJ_MASK + 1)
#define MIN_SKB_SIZE 256 /* 8 bytes and more - 8 bytes for PTP */
#define LIO_RXBUFFER_SZ 2048
static inline void
*recv_buffer_alloc(struct octeon_device *oct,
struct octeon_skb_page_info *pg_info)
{
struct page *page;
struct sk_buff *skb;
struct octeon_skb_page_info *skb_pg_info;
page = alloc_page(GFP_ATOMIC);
if (unlikely(!page))
return NULL;
skb = dev_alloc_skb(MIN_SKB_SIZE + SKB_ADJ);
if (unlikely(!skb)) {
__free_page(page);
pg_info->page = NULL;
return NULL;
}
if ((unsigned long)skb->data & SKB_ADJ_MASK) {
u32 r = SKB_ADJ - ((unsigned long)skb->data & SKB_ADJ_MASK);
skb_reserve(skb, r);
}
skb_pg_info = ((struct octeon_skb_page_info *)(skb->cb));
/* Get DMA info */
pg_info->dma = dma_map_page(&oct->pci_dev->dev, page, 0,
PAGE_SIZE, DMA_FROM_DEVICE);
/* Mapping failed!! */
if (dma_mapping_error(&oct->pci_dev->dev, pg_info->dma)) {
__free_page(page);
dev_kfree_skb_any((struct sk_buff *)skb);
pg_info->page = NULL;
return NULL;
}
pg_info->page = page;
pg_info->page_offset = 0;
skb_pg_info->page = page;
skb_pg_info->page_offset = 0;
skb_pg_info->dma = pg_info->dma;
return (void *)skb;
}
static inline void
*recv_buffer_fast_alloc(u32 size)
{
struct sk_buff *skb;
struct octeon_skb_page_info *skb_pg_info;
skb = dev_alloc_skb(size + SKB_ADJ);
if (unlikely(!skb))
return NULL;
if ((unsigned long)skb->data & SKB_ADJ_MASK) {
u32 r = SKB_ADJ - ((unsigned long)skb->data & SKB_ADJ_MASK);
skb_reserve(skb, r);
}
skb_pg_info = ((struct octeon_skb_page_info *)(skb->cb));
skb_pg_info->page = NULL;
skb_pg_info->page_offset = 0;
skb_pg_info->dma = 0;
return skb;
}
static inline int
recv_buffer_recycle(struct octeon_device *oct, void *buf)
{
struct octeon_skb_page_info *pg_info = buf;
if (!pg_info->page) {
dev_err(&oct->pci_dev->dev, "%s: pg_info->page NULL\n",
__func__);
return -ENOMEM;
}
if (unlikely(page_count(pg_info->page) != 1) ||
unlikely(page_to_nid(pg_info->page) != numa_node_id())) {
dma_unmap_page(&oct->pci_dev->dev,
pg_info->dma, (PAGE_SIZE << 0),
DMA_FROM_DEVICE);
pg_info->dma = 0;
pg_info->page = NULL;
pg_info->page_offset = 0;
return -ENOMEM;
}
/* Flip to other half of the buffer */
if (pg_info->page_offset == 0)
pg_info->page_offset = LIO_RXBUFFER_SZ;
else
pg_info->page_offset = 0;
page_ref_inc(pg_info->page);
return 0;
}
static inline void
*recv_buffer_reuse(struct octeon_device *oct, void *buf)
{
struct octeon_skb_page_info *pg_info = buf, *skb_pg_info;
struct sk_buff *skb;
skb = dev_alloc_skb(MIN_SKB_SIZE + SKB_ADJ);
if (unlikely(!skb)) {
dma_unmap_page(&oct->pci_dev->dev,
pg_info->dma, (PAGE_SIZE << 0),
DMA_FROM_DEVICE);
return NULL;
}
if ((unsigned long)skb->data & SKB_ADJ_MASK) {
u32 r = SKB_ADJ - ((unsigned long)skb->data & SKB_ADJ_MASK);
skb_reserve(skb, r);
}
skb_pg_info = ((struct octeon_skb_page_info *)(skb->cb));
skb_pg_info->page = pg_info->page;
skb_pg_info->page_offset = pg_info->page_offset;
skb_pg_info->dma = pg_info->dma;
return skb;
}
static inline void
recv_buffer_destroy(void *buffer, struct octeon_skb_page_info *pg_info)
{
struct sk_buff *skb = (struct sk_buff *)buffer;
put_page(pg_info->page);
pg_info->dma = 0;
pg_info->page = NULL;
pg_info->page_offset = 0;
if (skb)
dev_kfree_skb_any(skb);
}
static inline void recv_buffer_free(void *buffer)
{
struct sk_buff *skb = (struct sk_buff *)buffer;
struct octeon_skb_page_info *pg_info;
pg_info = ((struct octeon_skb_page_info *)(skb->cb));
if (pg_info->page) {
put_page(pg_info->page);
pg_info->dma = 0;
pg_info->page = NULL;
pg_info->page_offset = 0;
}
dev_kfree_skb_any((struct sk_buff *)buffer);
}
static inline void
recv_buffer_fast_free(void *buffer)
{
dev_kfree_skb_any((struct sk_buff *)buffer);
}
static inline void tx_buffer_free(void *buffer)
{
dev_kfree_skb_any((struct sk_buff *)buffer);
}
#define lio_dma_alloc(oct, size, dma_addr) \
dma_alloc_coherent(&(oct)->pci_dev->dev, size, dma_addr, GFP_KERNEL)
#define lio_dma_free(oct, size, virt_addr, dma_addr) \
dma_free_coherent(&(oct)->pci_dev->dev, size, virt_addr, dma_addr)
static inline
void *get_rbd(struct sk_buff *skb)
{
struct octeon_skb_page_info *pg_info;
unsigned char *va;
pg_info = ((struct octeon_skb_page_info *)(skb->cb));
va = page_address(pg_info->page) + pg_info->page_offset;
return va;
}
static inline u64
lio_map_ring(void *buf)
{
dma_addr_t dma_addr;
struct sk_buff *skb = (struct sk_buff *)buf;
struct octeon_skb_page_info *pg_info;
pg_info = ((struct octeon_skb_page_info *)(skb->cb));
if (!pg_info->page) {
pr_err("%s: pg_info->page NULL\n", __func__);
WARN_ON(1);
}
/* Get DMA info */
dma_addr = pg_info->dma;
if (!pg_info->dma) {
pr_err("%s: ERROR it should be already available\n",
__func__);
WARN_ON(1);
}
dma_addr += pg_info->page_offset;
return (u64)dma_addr;
}
static inline void
lio_unmap_ring(struct pci_dev *pci_dev,
u64 buf_ptr)
{
dma_unmap_page(&pci_dev->dev,
buf_ptr, (PAGE_SIZE << 0),
DMA_FROM_DEVICE);
}
static inline void *octeon_fast_packet_alloc(u32 size)
{
return recv_buffer_fast_alloc(size);
}
static inline void octeon_fast_packet_next(struct octeon_droq *droq,
struct sk_buff *nicbuf,
int copy_len,
int idx)
{
skb_put_data(nicbuf, get_rbd(droq->recv_buf_list[idx].buffer),
copy_len);
}
/**
* \brief check interface state
* @param lio per-network private data
* @param state_flag flag state to check
*/
static inline int ifstate_check(struct lio *lio, int state_flag)
{
return atomic_read(&lio->ifstate) & state_flag;
}
/**
* \brief set interface state
* @param lio per-network private data
* @param state_flag flag state to set
*/
static inline void ifstate_set(struct lio *lio, int state_flag)
{
atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) | state_flag));
}
/**
* \brief clear interface state
* @param lio per-network private data
* @param state_flag flag state to clear
*/
static inline void ifstate_reset(struct lio *lio, int state_flag)
{
atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag)));
}
/**
* \brief wait for all pending requests to complete
* @param oct Pointer to Octeon device
*
* Called during shutdown sequence
*/
static inline int wait_for_pending_requests(struct octeon_device *oct)
{
int i, pcount = 0;
for (i = 0; i < MAX_IO_PENDING_PKT_COUNT; i++) {
pcount = atomic_read(
&oct->response_list[OCTEON_ORDERED_SC_LIST]
.pending_req_count);
if (pcount)
schedule_timeout_uninterruptible(HZ / 10);
else
break;
}
if (pcount)
return 1;
return 0;
}
/**
* \brief Stop Tx queues
* @param netdev network device
*/
static inline void stop_txqs(struct net_device *netdev)
{
int i;
for (i = 0; i < netdev->real_num_tx_queues; i++)
netif_stop_subqueue(netdev, i);
}
/**
* \brief Wake Tx queues
* @param netdev network device
*/
static inline void wake_txqs(struct net_device *netdev)
{
struct lio *lio = GET_LIO(netdev);
int i, qno;
for (i = 0; i < netdev->real_num_tx_queues; i++) {
qno = lio->linfo.txpciq[i % lio->oct_dev->num_iqs].s.q_no;
if (__netif_subqueue_stopped(netdev, i)) {
INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno,
tx_restart, 1);
netif_wake_subqueue(netdev, i);
}
}
}
/**
* \brief Start Tx queues
* @param netdev network device
*/
static inline void start_txqs(struct net_device *netdev)
{
struct lio *lio = GET_LIO(netdev);
int i;
if (lio->linfo.link.s.link_up) {
for (i = 0; i < netdev->real_num_tx_queues; i++)
netif_start_subqueue(netdev, i);
}
}
static inline int skb_iq(struct octeon_device *oct, struct sk_buff *skb)
{
return skb->queue_mapping % oct->num_iqs;
}
/**
* Remove the node at the head of the list. The list would be empty at
* the end of this call if there are no more nodes in the list.
*/
static inline struct list_head *lio_list_delete_head(struct list_head *root)
{
struct list_head *node;
if (list_empty_careful(root))
node = NULL;
else
node = root->next;
if (node)
list_del(node);
return node;
}
#endif
@@ -0,0 +1,201 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_nic.h"
#include "octeon_main.h"
void *
octeon_alloc_soft_command_resp(struct octeon_device *oct,
union octeon_instr_64B *cmd,
u32 rdatasize)
{
struct octeon_soft_command *sc;
struct octeon_instr_ih3 *ih3;
struct octeon_instr_ih2 *ih2;
struct octeon_instr_irh *irh;
struct octeon_instr_rdp *rdp;
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, 0, rdatasize, 0);
if (!sc)
return NULL;
/* Copy existing command structure into the soft command */
memcpy(&sc->cmd, cmd, sizeof(union octeon_instr_64B));
/* Add in the response related fields. Opcode and Param are already
* there.
*/
if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp;
irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
/*pkiih3 + irh + ossp[0] + ossp[1] + rdp + rptr = 40 bytes */
ih3->fsz = LIO_SOFTCMDRESP_IH3;
} else {
ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp;
irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
/* irh + ossp[0] + ossp[1] + rdp + rptr = 40 bytes */
ih2->fsz = LIO_SOFTCMDRESP_IH2;
}
irh->rflag = 1; /* a response is required */
rdp->pcie_port = oct->pcie_port;
rdp->rlen = rdatasize;
*sc->status_word = COMPLETION_WORD_INIT;
if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct))
sc->cmd.cmd3.rptr = sc->dmarptr;
else
sc->cmd.cmd2.rptr = sc->dmarptr;
sc->expiry_time = jiffies + msecs_to_jiffies(LIO_SC_MAX_TMO_MS);
return sc;
}
EXPORT_SYMBOL_GPL(octeon_alloc_soft_command_resp);
int octnet_send_nic_data_pkt(struct octeon_device *oct,
struct octnic_data_pkt *ndata,
int xmit_more)
{
int ring_doorbell = !xmit_more;
return octeon_send_command(oct, ndata->q_no, ring_doorbell, &ndata->cmd,
ndata->buf, ndata->datasize,
ndata->reqtype);
}
EXPORT_SYMBOL_GPL(octnet_send_nic_data_pkt);
static inline struct octeon_soft_command
*octnic_alloc_ctrl_pkt_sc(struct octeon_device *oct,
struct octnic_ctrl_pkt *nctrl)
{
struct octeon_soft_command *sc = NULL;
u8 *data;
u32 rdatasize;
u32 uddsize = 0, datasize = 0;
uddsize = (u32)(nctrl->ncmd.s.more * 8);
datasize = OCTNET_CMD_SIZE + uddsize;
rdatasize = 16;
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, datasize, rdatasize, 0);
if (!sc)
return NULL;
data = (u8 *)sc->virtdptr;
memcpy(data, &nctrl->ncmd, OCTNET_CMD_SIZE);
octeon_swap_8B_data((u64 *)data, (OCTNET_CMD_SIZE >> 3));
if (uddsize) {
/* Endian-Swap for UDD should have been done by caller. */
memcpy(data + OCTNET_CMD_SIZE, nctrl->udd, uddsize);
}
sc->iq_no = (u32)nctrl->iq_no;
octeon_prepare_soft_command(oct, sc, OPCODE_NIC, OPCODE_NIC_CMD,
0, 0, 0);
init_completion(&sc->complete);
sc->sc_status = OCTEON_REQUEST_PENDING;
return sc;
}
int
octnet_send_nic_ctrl_pkt(struct octeon_device *oct,
struct octnic_ctrl_pkt *nctrl)
{
int retval;
struct octeon_soft_command *sc = NULL;
spin_lock_bh(&oct->cmd_resp_wqlock);
/* Allow only rx ctrl command to stop traffic on the chip
* during offline operations
*/
if ((oct->cmd_resp_state == OCT_DRV_OFFLINE) &&
(nctrl->ncmd.s.cmd != OCTNET_CMD_RX_CTL)) {
spin_unlock_bh(&oct->cmd_resp_wqlock);
dev_err(&oct->pci_dev->dev,
"%s cmd:%d not processed since driver offline\n",
__func__, nctrl->ncmd.s.cmd);
return -1;
}
sc = octnic_alloc_ctrl_pkt_sc(oct, nctrl);
if (!sc) {
dev_err(&oct->pci_dev->dev, "%s soft command alloc failed\n",
__func__);
spin_unlock_bh(&oct->cmd_resp_wqlock);
return -1;
}
retval = octeon_send_soft_command(oct, sc);
if (retval == IQ_SEND_FAILED) {
octeon_free_soft_command(oct, sc);
dev_err(&oct->pci_dev->dev, "%s pf_num:%d soft command:%d send failed status: %x\n",
__func__, oct->pf_num, nctrl->ncmd.s.cmd, retval);
spin_unlock_bh(&oct->cmd_resp_wqlock);
return -1;
}
spin_unlock_bh(&oct->cmd_resp_wqlock);
if (nctrl->ncmd.s.cmdgroup == 0) {
switch (nctrl->ncmd.s.cmd) {
/* caller holds lock, can not sleep */
case OCTNET_CMD_CHANGE_DEVFLAGS:
case OCTNET_CMD_SET_MULTI_LIST:
case OCTNET_CMD_SET_UC_LIST:
WRITE_ONCE(sc->caller_is_done, true);
return retval;
}
}
retval = wait_for_sc_completion_timeout(oct, sc, 0);
if (retval)
return (retval);
nctrl->sc_status = sc->sc_status;
retval = nctrl->sc_status;
if (nctrl->cb_fn)
nctrl->cb_fn(nctrl);
WRITE_ONCE(sc->caller_is_done, true);
return retval;
}
EXPORT_SYMBOL_GPL(octnet_send_nic_ctrl_pkt);
@@ -0,0 +1,288 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
/*! \file octeon_nic.h
* \brief Host NIC Driver: Routine to send network data &
* control packet to Octeon.
*/
#ifndef __OCTEON_NIC_H__
#define __OCTEON_NIC_H__
/* Maximum number of 8-byte words can be sent in a NIC control message.
*/
#define MAX_NCTRL_UDD 32
typedef void (*octnic_ctrl_pkt_cb_fn_t) (void *);
/* Structure of control information passed by the NIC module to the OSI
* layer when sending control commands to Octeon device software.
*/
struct octnic_ctrl_pkt {
/** Command to be passed to the Octeon device software. */
union octnet_cmd ncmd;
/** Send buffer */
void *data;
u64 dmadata;
/** Response buffer */
void *rdata;
u64 dmardata;
/** Additional data that may be needed by some commands. */
u64 udd[MAX_NCTRL_UDD];
/** Input queue to use to send this command. */
u64 iq_no;
/** The network device that issued the control command. */
u64 netpndev;
/** Callback function called when the command has been fetched */
octnic_ctrl_pkt_cb_fn_t cb_fn;
u32 sc_status;
};
#define MAX_UDD_SIZE(nctrl) (sizeof((nctrl)->udd))
/** Structure of data information passed by the NIC module to the OSI
* layer when forwarding data to Octeon device software.
*/
struct octnic_data_pkt {
/** Pointer to information maintained by NIC module for this packet. The
* OSI layer passes this as-is to the driver.
*/
void *buf;
/** Type of buffer passed in "buf" above. */
u32 reqtype;
/** Total data bytes to be transferred in this command. */
u32 datasize;
/** Command to be passed to the Octeon device software. */
union octeon_instr_64B cmd;
/** Input queue to use to send this command. */
u32 q_no;
};
/** Structure passed by NIC module to OSI layer to prepare a command to send
* network data to Octeon.
*/
union octnic_cmd_setup {
struct {
u32 iq_no:8;
u32 gather:1;
u32 timestamp:1;
u32 ip_csum:1;
u32 transport_csum:1;
u32 tnl_csum:1;
u32 rsvd:19;
union {
u32 datasize;
u32 gatherptrs;
} u;
} s;
u64 u64;
};
static inline int octnet_iq_is_full(struct octeon_device *oct, u32 q_no)
{
return ((u32)atomic_read(&oct->instr_queue[q_no]->instr_pending)
>= (oct->instr_queue[q_no]->max_count - 2));
}
static inline void
octnet_prepare_pci_cmd_o2(struct octeon_device *oct,
union octeon_instr_64B *cmd,
union octnic_cmd_setup *setup, u32 tag)
{
struct octeon_instr_ih2 *ih2;
struct octeon_instr_irh *irh;
union octnic_packet_params packet_params;
int port;
memset(cmd, 0, sizeof(union octeon_instr_64B));
ih2 = (struct octeon_instr_ih2 *)&cmd->cmd2.ih2;
/* assume that rflag is cleared so therefore front data will only have
* irh and ossp[0], ossp[1] for a total of 32 bytes
*/
ih2->fsz = LIO_PCICMD_O2;
ih2->tagtype = ORDERED_TAG;
ih2->grp = DEFAULT_POW_GRP;
port = (int)oct->instr_queue[setup->s.iq_no]->txpciq.s.port;
if (tag)
ih2->tag = tag;
else
ih2->tag = LIO_DATA(port);
ih2->raw = 1;
ih2->qos = (port & 3) + 4; /* map qos based on interface */
if (!setup->s.gather) {
ih2->dlengsz = setup->s.u.datasize;
} else {
ih2->gather = 1;
ih2->dlengsz = setup->s.u.gatherptrs;
}
irh = (struct octeon_instr_irh *)&cmd->cmd2.irh;
irh->opcode = OPCODE_NIC;
irh->subcode = OPCODE_NIC_NW_DATA;
packet_params.u32 = 0;
packet_params.s.ip_csum = setup->s.ip_csum;
packet_params.s.transport_csum = setup->s.transport_csum;
packet_params.s.tnl_csum = setup->s.tnl_csum;
packet_params.s.tsflag = setup->s.timestamp;
irh->ossp = packet_params.u32;
}
static inline void
octnet_prepare_pci_cmd_o3(struct octeon_device *oct,
union octeon_instr_64B *cmd,
union octnic_cmd_setup *setup, u32 tag)
{
struct octeon_instr_irh *irh;
struct octeon_instr_ih3 *ih3;
struct octeon_instr_pki_ih3 *pki_ih3;
union octnic_packet_params packet_params;
int port;
memset(cmd, 0, sizeof(union octeon_instr_64B));
ih3 = (struct octeon_instr_ih3 *)&cmd->cmd3.ih3;
pki_ih3 = (struct octeon_instr_pki_ih3 *)&cmd->cmd3.pki_ih3;
/* assume that rflag is cleared so therefore front data will only have
* irh and ossp[1] and ossp[2] for a total of 24 bytes
*/
ih3->pkind = oct->instr_queue[setup->s.iq_no]->txpciq.s.pkind;
/*PKI IH*/
ih3->fsz = LIO_PCICMD_O3;
if (!setup->s.gather) {
ih3->dlengsz = setup->s.u.datasize;
} else {
ih3->gather = 1;
ih3->dlengsz = setup->s.u.gatherptrs;
}
pki_ih3->w = 1;
pki_ih3->raw = 1;
pki_ih3->utag = 1;
pki_ih3->utt = 1;
pki_ih3->uqpg = oct->instr_queue[setup->s.iq_no]->txpciq.s.use_qpg;
port = (int)oct->instr_queue[setup->s.iq_no]->txpciq.s.port;
if (tag)
pki_ih3->tag = tag;
else
pki_ih3->tag = LIO_DATA(port);
pki_ih3->tagtype = ORDERED_TAG;
pki_ih3->qpg = oct->instr_queue[setup->s.iq_no]->txpciq.s.qpg;
pki_ih3->pm = 0x7; /*0x7 - meant for Parse nothing, uninterpreted*/
pki_ih3->sl = 8; /* sl will be sizeof(pki_ih3)*/
irh = (struct octeon_instr_irh *)&cmd->cmd3.irh;
irh->opcode = OPCODE_NIC;
irh->subcode = OPCODE_NIC_NW_DATA;
packet_params.u32 = 0;
packet_params.s.ip_csum = setup->s.ip_csum;
packet_params.s.transport_csum = setup->s.transport_csum;
packet_params.s.tnl_csum = setup->s.tnl_csum;
packet_params.s.tsflag = setup->s.timestamp;
irh->ossp = packet_params.u32;
}
/** Utility function to prepare a 64B NIC instruction based on a setup command
* @param cmd - pointer to instruction to be filled in.
* @param setup - pointer to the setup structure
* @param q_no - which queue for back pressure
*
* Assumes the cmd instruction is pre-allocated, but no fields are filled in.
*/
static inline void
octnet_prepare_pci_cmd(struct octeon_device *oct, union octeon_instr_64B *cmd,
union octnic_cmd_setup *setup, u32 tag)
{
if (OCTEON_CN6XXX(oct))
octnet_prepare_pci_cmd_o2(oct, cmd, setup, tag);
else
octnet_prepare_pci_cmd_o3(oct, cmd, setup, tag);
}
/** Allocate and a soft command with space for a response immediately following
* the commnad.
* @param oct - octeon device pointer
* @param cmd - pointer to the command structure, pre-filled for everything
* except the response.
* @param rdatasize - size in bytes of the response.
*
* @returns pointer to allocated buffer with command copied into it, and
* response space immediately following.
*/
void *
octeon_alloc_soft_command_resp(struct octeon_device *oct,
union octeon_instr_64B *cmd,
u32 rdatasize);
/** Send a NIC data packet to the device
* @param oct - octeon device pointer
* @param ndata - control structure with queueing, and buffer information
*
* @returns IQ_FAILED if it failed to add to the input queue. IQ_STOP if the
* queue should be stopped, and IQ_SEND_OK if it sent okay.
*/
int octnet_send_nic_data_pkt(struct octeon_device *oct,
struct octnic_data_pkt *ndata,
int xmit_more);
/** Send a NIC control packet to the device
* @param oct - octeon device pointer
* @param nctrl - control structure with command, timout, and callback info
* @returns IQ_FAILED if it failed to add to the input queue. IQ_STOP if the
* queue should be stopped, and IQ_SEND_OK if it sent okay.
*/
int
octnet_send_nic_ctrl_pkt(struct octeon_device *oct,
struct octnic_ctrl_pkt *nctrl);
#endif
@@ -0,0 +1,941 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
#include "octeon_network.h"
#include "cn66xx_device.h"
#include "cn23xx_pf_device.h"
#include "cn23xx_vf_device.h"
struct iq_post_status {
int status;
int index;
};
static void check_db_timeout(struct work_struct *work);
static void __check_db_timeout(struct octeon_device *oct, u64 iq_no);
static void (*reqtype_free_fn[MAX_OCTEON_DEVICES][REQTYPE_LAST + 1]) (void *);
/* Define this to return the request status comaptible to old code */
/*#define OCTEON_USE_OLD_REQ_STATUS*/
/* Return 0 on success, 1 on failure */
int octeon_init_instr_queue(struct octeon_device *oct,
union oct_txpciq txpciq,
u32 num_descs)
{
struct octeon_instr_queue *iq;
struct octeon_iq_config *conf = NULL;
u32 iq_no = (u32)txpciq.s.q_no;
u32 q_size;
struct cavium_wq *db_wq;
int numa_node = dev_to_node(&oct->pci_dev->dev);
if (OCTEON_CN6XXX(oct))
conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx)));
else if (OCTEON_CN23XX_PF(oct))
conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf)));
else if (OCTEON_CN23XX_VF(oct))
conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_vf)));
if (!conf) {
dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n",
oct->chip_id);
return 1;
}
q_size = (u32)conf->instr_type * num_descs;
iq = oct->instr_queue[iq_no];
iq->oct_dev = oct;
iq->base_addr = lio_dma_alloc(oct, q_size, &iq->base_addr_dma);
if (!iq->base_addr) {
dev_err(&oct->pci_dev->dev, "Cannot allocate memory for instr queue %d\n",
iq_no);
return 1;
}
iq->max_count = num_descs;
/* Initialize a list to holds requests that have been posted to Octeon
* but has yet to be fetched by octeon
*/
iq->request_list = vzalloc_node(array_size(num_descs, sizeof(*iq->request_list)),
numa_node);
if (!iq->request_list)
iq->request_list = vzalloc(array_size(num_descs, sizeof(*iq->request_list)));
if (!iq->request_list) {
lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
dev_err(&oct->pci_dev->dev, "Alloc failed for IQ[%d] nr free list\n",
iq_no);
return 1;
}
dev_dbg(&oct->pci_dev->dev, "IQ[%d]: base: %p basedma: %pad count: %d\n",
iq_no, iq->base_addr, &iq->base_addr_dma, iq->max_count);
iq->txpciq.u64 = txpciq.u64;
iq->fill_threshold = (u32)conf->db_min;
iq->fill_cnt = 0;
iq->host_write_index = 0;
iq->octeon_read_index = 0;
iq->flush_index = 0;
iq->last_db_time = 0;
iq->do_auto_flush = 1;
iq->db_timeout = (u32)conf->db_timeout;
atomic_set(&iq->instr_pending, 0);
iq->pkts_processed = 0;
/* Initialize the spinlock for this instruction queue */
spin_lock_init(&iq->lock);
if (iq_no == 0) {
iq->allow_soft_cmds = true;
spin_lock_init(&iq->post_lock);
} else {
iq->allow_soft_cmds = false;
}
spin_lock_init(&iq->iq_flush_running_lock);
oct->io_qmask.iq |= BIT_ULL(iq_no);
/* Set the 32B/64B mode for each input queue */
oct->io_qmask.iq64B |= ((u64)(conf->instr_type == 64) << iq_no);
iq->iqcmd_64B = (conf->instr_type == 64);
oct->fn_list.setup_iq_regs(oct, iq_no);
oct->check_db_wq[iq_no].wq = alloc_workqueue("check_iq_db",
WQ_MEM_RECLAIM | WQ_PERCPU,
0);
if (!oct->check_db_wq[iq_no].wq) {
vfree(iq->request_list);
iq->request_list = NULL;
lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
dev_err(&oct->pci_dev->dev, "check db wq create failed for iq %d\n",
iq_no);
return 1;
}
db_wq = &oct->check_db_wq[iq_no];
INIT_DELAYED_WORK(&db_wq->wk.work, check_db_timeout);
db_wq->wk.ctxptr = oct;
db_wq->wk.ctxul = iq_no;
queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(1));
return 0;
}
int octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no)
{
u64 desc_size = 0, q_size;
struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
cancel_delayed_work_sync(&oct->check_db_wq[iq_no].wk.work);
destroy_workqueue(oct->check_db_wq[iq_no].wq);
if (OCTEON_CN6XXX(oct))
desc_size =
CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn6xxx));
else if (OCTEON_CN23XX_PF(oct))
desc_size =
CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf));
else if (OCTEON_CN23XX_VF(oct))
desc_size =
CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_vf));
vfree(iq->request_list);
if (iq->base_addr) {
q_size = iq->max_count * desc_size;
lio_dma_free(oct, (u32)q_size, iq->base_addr,
iq->base_addr_dma);
oct->io_qmask.iq &= ~(1ULL << iq_no);
vfree(oct->instr_queue[iq_no]);
oct->instr_queue[iq_no] = NULL;
oct->num_iqs--;
return 0;
}
return 1;
}
EXPORT_SYMBOL_GPL(octeon_delete_instr_queue);
/* Return 0 on success, 1 on failure */
int octeon_setup_iq(struct octeon_device *oct,
int ifidx,
int q_index,
union oct_txpciq txpciq,
u32 num_descs,
void *app_ctx)
{
u32 iq_no = (u32)txpciq.s.q_no;
int numa_node = dev_to_node(&oct->pci_dev->dev);
if (oct->instr_queue[iq_no]) {
dev_dbg(&oct->pci_dev->dev, "IQ is in use. Cannot create the IQ: %d again\n",
iq_no);
oct->instr_queue[iq_no]->txpciq.u64 = txpciq.u64;
oct->instr_queue[iq_no]->app_ctx = app_ctx;
return 0;
}
oct->instr_queue[iq_no] =
vzalloc_node(sizeof(struct octeon_instr_queue), numa_node);
if (!oct->instr_queue[iq_no])
oct->instr_queue[iq_no] =
vzalloc(sizeof(struct octeon_instr_queue));
if (!oct->instr_queue[iq_no])
return 1;
oct->instr_queue[iq_no]->q_index = q_index;
oct->instr_queue[iq_no]->app_ctx = app_ctx;
oct->instr_queue[iq_no]->ifidx = ifidx;
if (octeon_init_instr_queue(oct, txpciq, num_descs)) {
vfree(oct->instr_queue[iq_no]);
oct->instr_queue[iq_no] = NULL;
return 1;
}
oct->num_iqs++;
if (oct->fn_list.enable_io_queues(oct)) {
octeon_delete_instr_queue(oct, iq_no);
return 1;
}
return 0;
}
int lio_wait_for_instr_fetch(struct octeon_device *oct)
{
int i, retry = 1000, pending, instr_cnt = 0;
do {
instr_cnt = 0;
for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
if (!(oct->io_qmask.iq & BIT_ULL(i)))
continue;
pending =
atomic_read(&oct->instr_queue[i]->instr_pending);
if (pending)
__check_db_timeout(oct, i);
instr_cnt += pending;
}
if (instr_cnt == 0)
break;
schedule_timeout_uninterruptible(1);
} while (retry-- && instr_cnt);
return instr_cnt;
}
EXPORT_SYMBOL_GPL(lio_wait_for_instr_fetch);
static inline void
ring_doorbell(struct octeon_device *oct, struct octeon_instr_queue *iq)
{
if (atomic_read(&oct->status) == OCT_DEV_RUNNING) {
writel(iq->fill_cnt, iq->doorbell_reg);
/* make sure doorbell write goes through */
iq->fill_cnt = 0;
iq->last_db_time = jiffies;
return;
}
}
void
octeon_ring_doorbell_locked(struct octeon_device *oct, u32 iq_no)
{
struct octeon_instr_queue *iq;
iq = oct->instr_queue[iq_no];
spin_lock(&iq->post_lock);
if (iq->fill_cnt)
ring_doorbell(oct, iq);
spin_unlock(&iq->post_lock);
}
EXPORT_SYMBOL_GPL(octeon_ring_doorbell_locked);
static inline void __copy_cmd_into_iq(struct octeon_instr_queue *iq,
u8 *cmd)
{
u8 *iqptr, cmdsize;
cmdsize = ((iq->iqcmd_64B) ? 64 : 32);
iqptr = iq->base_addr + (cmdsize * iq->host_write_index);
memcpy(iqptr, cmd, cmdsize);
}
static inline struct iq_post_status
__post_command2(struct octeon_instr_queue *iq, u8 *cmd)
{
struct iq_post_status st;
st.status = IQ_SEND_OK;
/* This ensures that the read index does not wrap around to the same
* position if queue gets full before Octeon could fetch any instr.
*/
if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 1)) {
st.status = IQ_SEND_FAILED;
st.index = -1;
return st;
}
if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 2))
st.status = IQ_SEND_STOP;
__copy_cmd_into_iq(iq, cmd);
/* "index" is returned, host_write_index is modified. */
st.index = iq->host_write_index;
iq->host_write_index = incr_index(iq->host_write_index, 1,
iq->max_count);
iq->fill_cnt++;
/* Flush the command into memory. We need to be sure the data is in
* memory before indicating that the instruction is pending.
*/
wmb();
atomic_inc(&iq->instr_pending);
return st;
}
int
octeon_register_reqtype_free_fn(struct octeon_device *oct, int reqtype,
void (*fn)(void *))
{
if (reqtype > REQTYPE_LAST) {
dev_err(&oct->pci_dev->dev, "%s: Invalid reqtype: %d\n",
__func__, reqtype);
return -EINVAL;
}
reqtype_free_fn[oct->octeon_id][reqtype] = fn;
return 0;
}
EXPORT_SYMBOL_GPL(octeon_register_reqtype_free_fn);
static inline void
__add_to_request_list(struct octeon_instr_queue *iq,
int idx, void *buf, int reqtype)
{
iq->request_list[idx].buf = buf;
iq->request_list[idx].reqtype = reqtype;
}
/* Can only run in process context */
int
lio_process_iq_request_list(struct octeon_device *oct,
struct octeon_instr_queue *iq, u32 napi_budget)
{
struct cavium_wq *cwq = &oct->dma_comp_wq;
int reqtype;
void *buf;
u32 old = iq->flush_index;
u32 inst_count = 0;
unsigned int pkts_compl = 0, bytes_compl = 0;
struct octeon_soft_command *sc;
unsigned long flags;
while (old != iq->octeon_read_index) {
reqtype = iq->request_list[old].reqtype;
buf = iq->request_list[old].buf;
if (reqtype == REQTYPE_NONE)
goto skip_this;
octeon_update_tx_completion_counters(buf, reqtype, &pkts_compl,
&bytes_compl);
switch (reqtype) {
case REQTYPE_NORESP_NET:
case REQTYPE_NORESP_NET_SG:
case REQTYPE_RESP_NET_SG:
reqtype_free_fn[oct->octeon_id][reqtype](buf);
break;
case REQTYPE_RESP_NET:
case REQTYPE_SOFT_COMMAND:
sc = buf;
/* We're expecting a response from Octeon.
* It's up to lio_process_ordered_list() to
* process sc. Add sc to the ordered soft
* command response list because we expect
* a response from Octeon.
*/
spin_lock_irqsave(&oct->response_list
[OCTEON_ORDERED_SC_LIST].lock, flags);
atomic_inc(&oct->response_list
[OCTEON_ORDERED_SC_LIST].pending_req_count);
list_add_tail(&sc->node, &oct->response_list
[OCTEON_ORDERED_SC_LIST].head);
spin_unlock_irqrestore(&oct->response_list
[OCTEON_ORDERED_SC_LIST].lock,
flags);
break;
default:
dev_err(&oct->pci_dev->dev,
"%s Unknown reqtype: %d buf: %p at idx %d\n",
__func__, reqtype, buf, old);
}
iq->request_list[old].buf = NULL;
iq->request_list[old].reqtype = 0;
skip_this:
inst_count++;
old = incr_index(old, 1, iq->max_count);
if ((napi_budget) && (inst_count >= napi_budget))
break;
}
if (bytes_compl)
octeon_report_tx_completion_to_bql(iq->app_ctx, pkts_compl,
bytes_compl);
iq->flush_index = old;
if (atomic_read(&oct->response_list
[OCTEON_ORDERED_SC_LIST].pending_req_count))
queue_work(cwq->wq, &cwq->wk.work.work);
return inst_count;
}
EXPORT_SYMBOL_GPL(lio_process_iq_request_list);
/* Can only be called from process context */
int
octeon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq,
u32 napi_budget)
{
u32 inst_processed = 0;
u32 tot_inst_processed = 0;
int tx_done = 1;
if (!spin_trylock(&iq->iq_flush_running_lock))
return tx_done;
spin_lock_bh(&iq->lock);
iq->octeon_read_index = oct->fn_list.update_iq_read_idx(iq);
do {
/* Process any outstanding IQ packets. */
if (iq->flush_index == iq->octeon_read_index)
break;
if (napi_budget)
inst_processed =
lio_process_iq_request_list(oct, iq,
napi_budget -
tot_inst_processed);
else
inst_processed =
lio_process_iq_request_list(oct, iq, 0);
if (inst_processed) {
iq->pkts_processed += inst_processed;
atomic_sub(inst_processed, &iq->instr_pending);
iq->stats.instr_processed += inst_processed;
}
tot_inst_processed += inst_processed;
} while (tot_inst_processed < napi_budget);
if (napi_budget && (tot_inst_processed >= napi_budget))
tx_done = 0;
iq->last_db_time = jiffies;
spin_unlock_bh(&iq->lock);
spin_unlock(&iq->iq_flush_running_lock);
return tx_done;
}
/* Process instruction queue after timeout.
* This routine gets called from a workqueue or when removing the module.
*/
static void __check_db_timeout(struct octeon_device *oct, u64 iq_no)
{
struct octeon_instr_queue *iq;
u64 next_time;
if (!oct)
return;
iq = oct->instr_queue[iq_no];
if (!iq)
return;
/* return immediately, if no work pending */
if (!atomic_read(&iq->instr_pending))
return;
/* If jiffies - last_db_time < db_timeout do nothing */
next_time = iq->last_db_time + iq->db_timeout;
if (!time_after(jiffies, (unsigned long)next_time))
return;
iq->last_db_time = jiffies;
/* Flush the instruction queue */
octeon_flush_iq(oct, iq, 0);
lio_enable_irq(NULL, iq);
}
/* Called by the Poll thread at regular intervals to check the instruction
* queue for commands to be posted and for commands that were fetched by Octeon.
*/
static void check_db_timeout(struct work_struct *work)
{
struct cavium_wk *wk = (struct cavium_wk *)work;
struct octeon_device *oct = (struct octeon_device *)wk->ctxptr;
u64 iq_no = wk->ctxul;
struct cavium_wq *db_wq = &oct->check_db_wq[iq_no];
u32 delay = 10;
__check_db_timeout(oct, iq_no);
queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(delay));
}
int
octeon_send_command(struct octeon_device *oct, u32 iq_no,
u32 force_db, void *cmd, void *buf,
u32 datasize, u32 reqtype)
{
int xmit_stopped;
struct iq_post_status st;
struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
/* Get the lock and prevent other tasks and tx interrupt handler from
* running.
*/
if (iq->allow_soft_cmds)
spin_lock_bh(&iq->post_lock);
st = __post_command2(iq, cmd);
if (st.status != IQ_SEND_FAILED) {
xmit_stopped = octeon_report_sent_bytes_to_bql(buf, reqtype);
__add_to_request_list(iq, st.index, buf, reqtype);
INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, bytes_sent, datasize);
INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_posted, 1);
if (iq->fill_cnt >= MAX_OCTEON_FILL_COUNT || force_db ||
xmit_stopped || st.status == IQ_SEND_STOP)
ring_doorbell(oct, iq);
} else {
INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_dropped, 1);
}
if (iq->allow_soft_cmds)
spin_unlock_bh(&iq->post_lock);
/* This is only done here to expedite packets being flushed
* for cases where there are no IQ completion interrupts.
*/
return st.status;
}
EXPORT_SYMBOL_GPL(octeon_send_command);
void
octeon_prepare_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc,
u8 opcode,
u8 subcode,
u32 irh_ossp,
u64 ossp0,
u64 ossp1)
{
struct octeon_config *oct_cfg;
struct octeon_instr_ih2 *ih2;
struct octeon_instr_ih3 *ih3;
struct octeon_instr_pki_ih3 *pki_ih3;
struct octeon_instr_irh *irh;
struct octeon_instr_rdp *rdp;
WARN_ON(opcode > 15);
WARN_ON(subcode > 127);
oct_cfg = octeon_get_conf(oct);
if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind;
pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3;
pki_ih3->w = 1;
pki_ih3->raw = 1;
pki_ih3->utag = 1;
pki_ih3->uqpg =
oct->instr_queue[sc->iq_no]->txpciq.s.use_qpg;
pki_ih3->utt = 1;
pki_ih3->tag = LIO_CONTROL;
pki_ih3->tagtype = ATOMIC_TAG;
pki_ih3->qpg =
oct->instr_queue[sc->iq_no]->txpciq.s.ctrl_qpg;
pki_ih3->pm = 0x7;
pki_ih3->sl = 8;
if (sc->datasize)
ih3->dlengsz = sc->datasize;
irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
irh->opcode = opcode;
irh->subcode = subcode;
/* opcode/subcode specific parameters (ossp) */
irh->ossp = irh_ossp;
sc->cmd.cmd3.ossp[0] = ossp0;
sc->cmd.cmd3.ossp[1] = ossp1;
if (sc->rdatasize) {
rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp;
rdp->pcie_port = oct->pcie_port;
rdp->rlen = sc->rdatasize;
irh->rflag = 1;
/*PKI IH3*/
/* pki_ih3 irh+ossp[0]+ossp[1]+rdp+rptr = 48 bytes */
ih3->fsz = LIO_SOFTCMDRESP_IH3;
} else {
irh->rflag = 0;
/*PKI IH3*/
/* pki_h3 + irh + ossp[0] + ossp[1] = 32 bytes */
ih3->fsz = LIO_PCICMD_O3;
}
} else {
ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
ih2->tagtype = ATOMIC_TAG;
ih2->tag = LIO_CONTROL;
ih2->raw = 1;
ih2->grp = CFG_GET_CTRL_Q_GRP(oct_cfg);
if (sc->datasize) {
ih2->dlengsz = sc->datasize;
ih2->rs = 1;
}
irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
irh->opcode = opcode;
irh->subcode = subcode;
/* opcode/subcode specific parameters (ossp) */
irh->ossp = irh_ossp;
sc->cmd.cmd2.ossp[0] = ossp0;
sc->cmd.cmd2.ossp[1] = ossp1;
if (sc->rdatasize) {
rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp;
rdp->pcie_port = oct->pcie_port;
rdp->rlen = sc->rdatasize;
irh->rflag = 1;
/* irh+ossp[0]+ossp[1]+rdp+rptr = 40 bytes */
ih2->fsz = LIO_SOFTCMDRESP_IH2;
} else {
irh->rflag = 0;
/* irh + ossp[0] + ossp[1] = 24 bytes */
ih2->fsz = LIO_PCICMD_O2;
}
}
}
EXPORT_SYMBOL_GPL(octeon_prepare_soft_command);
int octeon_send_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc)
{
struct octeon_instr_queue *iq;
struct octeon_instr_ih2 *ih2;
struct octeon_instr_ih3 *ih3;
struct octeon_instr_irh *irh;
u32 len;
iq = oct->instr_queue[sc->iq_no];
if (!iq->allow_soft_cmds) {
dev_err(&oct->pci_dev->dev, "Soft commands are not allowed on Queue %d\n",
sc->iq_no);
INCR_INSTRQUEUE_PKT_COUNT(oct, sc->iq_no, instr_dropped, 1);
return IQ_SEND_FAILED;
}
if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
if (ih3->dlengsz) {
WARN_ON(!sc->dmadptr);
sc->cmd.cmd3.dptr = sc->dmadptr;
}
irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
if (irh->rflag) {
WARN_ON(!sc->dmarptr);
WARN_ON(!sc->status_word);
*sc->status_word = COMPLETION_WORD_INIT;
sc->cmd.cmd3.rptr = sc->dmarptr;
}
len = (u32)ih3->dlengsz;
} else {
ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
if (ih2->dlengsz) {
WARN_ON(!sc->dmadptr);
sc->cmd.cmd2.dptr = sc->dmadptr;
}
irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
if (irh->rflag) {
WARN_ON(!sc->dmarptr);
WARN_ON(!sc->status_word);
*sc->status_word = COMPLETION_WORD_INIT;
sc->cmd.cmd2.rptr = sc->dmarptr;
}
len = (u32)ih2->dlengsz;
}
sc->expiry_time = jiffies + msecs_to_jiffies(LIO_SC_MAX_TMO_MS);
return (octeon_send_command(oct, sc->iq_no, 1, &sc->cmd, sc,
len, REQTYPE_SOFT_COMMAND));
}
EXPORT_SYMBOL_GPL(octeon_send_soft_command);
int octeon_setup_sc_buffer_pool(struct octeon_device *oct)
{
int i;
u64 dma_addr;
struct octeon_soft_command *sc;
INIT_LIST_HEAD(&oct->sc_buf_pool.head);
spin_lock_init(&oct->sc_buf_pool.lock);
atomic_set(&oct->sc_buf_pool.alloc_buf_count, 0);
for (i = 0; i < MAX_SOFT_COMMAND_BUFFERS; i++) {
sc = (struct octeon_soft_command *)
lio_dma_alloc(oct,
SOFT_COMMAND_BUFFER_SIZE,
(dma_addr_t *)&dma_addr);
if (!sc) {
octeon_free_sc_buffer_pool(oct);
return 1;
}
sc->dma_addr = dma_addr;
sc->size = SOFT_COMMAND_BUFFER_SIZE;
list_add_tail(&sc->node, &oct->sc_buf_pool.head);
}
return 0;
}
EXPORT_SYMBOL_GPL(octeon_setup_sc_buffer_pool);
int octeon_free_sc_done_list(struct octeon_device *oct)
{
struct octeon_response_list *done_sc_list, *zombie_sc_list;
struct octeon_soft_command *sc;
struct list_head *tmp, *tmp2;
spinlock_t *sc_lists_lock; /* lock for response_list */
done_sc_list = &oct->response_list[OCTEON_DONE_SC_LIST];
zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
if (!atomic_read(&done_sc_list->pending_req_count))
return 0;
sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
spin_lock_bh(sc_lists_lock);
list_for_each_safe(tmp, tmp2, &done_sc_list->head) {
sc = list_entry(tmp, struct octeon_soft_command, node);
if (READ_ONCE(sc->caller_is_done)) {
list_del(&sc->node);
atomic_dec(&done_sc_list->pending_req_count);
if (*sc->status_word == COMPLETION_WORD_INIT) {
/* timeout; move sc to zombie list */
list_add_tail(&sc->node, &zombie_sc_list->head);
atomic_inc(&zombie_sc_list->pending_req_count);
} else {
octeon_free_soft_command(oct, sc);
}
}
}
spin_unlock_bh(sc_lists_lock);
return 0;
}
EXPORT_SYMBOL_GPL(octeon_free_sc_done_list);
int octeon_free_sc_zombie_list(struct octeon_device *oct)
{
struct octeon_response_list *zombie_sc_list;
struct octeon_soft_command *sc;
struct list_head *tmp, *tmp2;
spinlock_t *sc_lists_lock; /* lock for response_list */
zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
spin_lock_bh(sc_lists_lock);
list_for_each_safe(tmp, tmp2, &zombie_sc_list->head) {
list_del(tmp);
atomic_dec(&zombie_sc_list->pending_req_count);
sc = list_entry(tmp, struct octeon_soft_command, node);
octeon_free_soft_command(oct, sc);
}
spin_unlock_bh(sc_lists_lock);
return 0;
}
EXPORT_SYMBOL_GPL(octeon_free_sc_zombie_list);
int octeon_free_sc_buffer_pool(struct octeon_device *oct)
{
struct list_head *tmp, *tmp2;
struct octeon_soft_command *sc;
octeon_free_sc_zombie_list(oct);
spin_lock_bh(&oct->sc_buf_pool.lock);
list_for_each_safe(tmp, tmp2, &oct->sc_buf_pool.head) {
list_del(tmp);
sc = (struct octeon_soft_command *)tmp;
lio_dma_free(oct, sc->size, sc, sc->dma_addr);
}
INIT_LIST_HEAD(&oct->sc_buf_pool.head);
spin_unlock_bh(&oct->sc_buf_pool.lock);
return 0;
}
EXPORT_SYMBOL_GPL(octeon_free_sc_buffer_pool);
struct octeon_soft_command *octeon_alloc_soft_command(struct octeon_device *oct,
u32 datasize,
u32 rdatasize,
u32 ctxsize)
{
u64 dma_addr;
u32 size;
u32 offset = sizeof(struct octeon_soft_command);
struct octeon_soft_command *sc = NULL;
struct list_head *tmp;
if (!rdatasize)
rdatasize = 16;
WARN_ON((offset + datasize + rdatasize + ctxsize) >
SOFT_COMMAND_BUFFER_SIZE);
spin_lock_bh(&oct->sc_buf_pool.lock);
if (list_empty(&oct->sc_buf_pool.head)) {
spin_unlock_bh(&oct->sc_buf_pool.lock);
return NULL;
}
list_for_each(tmp, &oct->sc_buf_pool.head)
break;
list_del(tmp);
atomic_inc(&oct->sc_buf_pool.alloc_buf_count);
spin_unlock_bh(&oct->sc_buf_pool.lock);
sc = (struct octeon_soft_command *)tmp;
dma_addr = sc->dma_addr;
size = sc->size;
memset(sc, 0, sc->size);
sc->dma_addr = dma_addr;
sc->size = size;
if (ctxsize) {
sc->ctxptr = (u8 *)sc + offset;
sc->ctxsize = ctxsize;
}
/* Start data at 128 byte boundary */
offset = (offset + ctxsize + 127) & 0xffffff80;
if (datasize) {
sc->virtdptr = (u8 *)sc + offset;
sc->dmadptr = dma_addr + offset;
sc->datasize = datasize;
}
/* Start rdata at 128 byte boundary */
offset = (offset + datasize + 127) & 0xffffff80;
if (rdatasize) {
WARN_ON(rdatasize < 16);
sc->virtrptr = (u8 *)sc + offset;
sc->dmarptr = dma_addr + offset;
sc->rdatasize = rdatasize;
sc->status_word = (u64 *)((u8 *)(sc->virtrptr) + rdatasize - 8);
}
return sc;
}
EXPORT_SYMBOL_GPL(octeon_alloc_soft_command);
void octeon_free_soft_command(struct octeon_device *oct,
struct octeon_soft_command *sc)
{
spin_lock_bh(&oct->sc_buf_pool.lock);
list_add_tail(&sc->node, &oct->sc_buf_pool.head);
atomic_dec(&oct->sc_buf_pool.alloc_buf_count);
spin_unlock_bh(&oct->sc_buf_pool.lock);
}
EXPORT_SYMBOL_GPL(octeon_free_soft_command);
@@ -0,0 +1,238 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_main.h"
static void oct_poll_req_completion(struct work_struct *work);
int octeon_setup_response_list(struct octeon_device *oct)
{
int i, ret = 0;
struct cavium_wq *cwq;
for (i = 0; i < MAX_RESPONSE_LISTS; i++) {
INIT_LIST_HEAD(&oct->response_list[i].head);
spin_lock_init(&oct->response_list[i].lock);
atomic_set(&oct->response_list[i].pending_req_count, 0);
}
spin_lock_init(&oct->cmd_resp_wqlock);
oct->dma_comp_wq.wq = alloc_workqueue("dma-comp",
WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!oct->dma_comp_wq.wq) {
dev_err(&oct->pci_dev->dev, "failed to create wq thread\n");
return -ENOMEM;
}
cwq = &oct->dma_comp_wq;
INIT_DELAYED_WORK(&cwq->wk.work, oct_poll_req_completion);
cwq->wk.ctxptr = oct;
oct->cmd_resp_state = OCT_DRV_ONLINE;
return ret;
}
EXPORT_SYMBOL_GPL(octeon_setup_response_list);
void octeon_delete_response_list(struct octeon_device *oct)
{
cancel_delayed_work_sync(&oct->dma_comp_wq.wk.work);
destroy_workqueue(oct->dma_comp_wq.wq);
}
EXPORT_SYMBOL_GPL(octeon_delete_response_list);
int lio_process_ordered_list(struct octeon_device *octeon_dev,
u32 force_quit)
{
struct octeon_response_list *ordered_sc_list;
struct octeon_soft_command *sc;
int request_complete = 0;
int resp_to_process = MAX_ORD_REQS_TO_PROCESS;
u32 status;
u64 status64;
octeon_free_sc_done_list(octeon_dev);
ordered_sc_list = &octeon_dev->response_list[OCTEON_ORDERED_SC_LIST];
do {
spin_lock_bh(&ordered_sc_list->lock);
if (list_empty(&ordered_sc_list->head)) {
spin_unlock_bh(&ordered_sc_list->lock);
return 1;
}
sc = list_first_entry(&ordered_sc_list->head,
struct octeon_soft_command, node);
status = OCTEON_REQUEST_PENDING;
/* check if octeon has finished DMA'ing a response
* to where rptr is pointing to
*/
status64 = *sc->status_word;
if (status64 != COMPLETION_WORD_INIT) {
/* This logic ensures that all 64b have been written.
* 1. check byte 0 for non-FF
* 2. if non-FF, then swap result from BE to host order
* 3. check byte 7 (swapped to 0) for non-FF
* 4. if non-FF, use the low 32-bit status code
* 5. if either byte 0 or byte 7 is FF, don't use status
*/
if ((status64 & 0xff) != 0xff) {
octeon_swap_8B_data(&status64, 1);
if (((status64 & 0xff) != 0xff)) {
/* retrieve 16-bit firmware status */
status = (u32)(status64 & 0xffffULL);
if (status) {
status =
FIRMWARE_STATUS_CODE(status);
} else {
/* i.e. no error */
status = OCTEON_REQUEST_DONE;
}
}
}
} else if (unlikely(force_quit) || (sc->expiry_time &&
time_after(jiffies, (unsigned long)sc->expiry_time))) {
struct octeon_instr_irh *irh =
(struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
dev_err(&octeon_dev->pci_dev->dev, "%s: ", __func__);
dev_err(&octeon_dev->pci_dev->dev,
"cmd %x/%x/%llx/%llx failed, ",
irh->opcode, irh->subcode,
sc->cmd.cmd3.ossp[0], sc->cmd.cmd3.ossp[1]);
dev_err(&octeon_dev->pci_dev->dev,
"timeout (%ld, %ld)\n",
(long)jiffies, (long)sc->expiry_time);
status = OCTEON_REQUEST_TIMEOUT;
}
if (status != OCTEON_REQUEST_PENDING) {
sc->sc_status = status;
/* we have received a response or we have timed out */
/* remove node from linked list */
list_del(&sc->node);
atomic_dec(&octeon_dev->response_list
[OCTEON_ORDERED_SC_LIST].
pending_req_count);
if (!sc->callback) {
atomic_inc(&octeon_dev->response_list
[OCTEON_DONE_SC_LIST].
pending_req_count);
list_add_tail(&sc->node,
&octeon_dev->response_list
[OCTEON_DONE_SC_LIST].head);
if (unlikely(READ_ONCE(sc->caller_is_done))) {
/* caller does not wait for response
* from firmware
*/
if (status != OCTEON_REQUEST_DONE) {
struct octeon_instr_irh *irh;
irh =
(struct octeon_instr_irh *)
&sc->cmd.cmd3.irh;
dev_dbg
(&octeon_dev->pci_dev->dev,
"%s: sc failed: opcode=%x, ",
__func__, irh->opcode);
dev_dbg
(&octeon_dev->pci_dev->dev,
"subcode=%x, ossp[0]=%llx, ",
irh->subcode,
sc->cmd.cmd3.ossp[0]);
dev_dbg
(&octeon_dev->pci_dev->dev,
"ossp[1]=%llx, status=%d\n",
sc->cmd.cmd3.ossp[1],
status);
}
} else {
complete(&sc->complete);
}
spin_unlock_bh(&ordered_sc_list->lock);
} else {
/* sc with callback function */
if (status == OCTEON_REQUEST_TIMEOUT) {
atomic_inc(&octeon_dev->response_list
[OCTEON_ZOMBIE_SC_LIST].
pending_req_count);
list_add_tail(&sc->node,
&octeon_dev->response_list
[OCTEON_ZOMBIE_SC_LIST].
head);
}
spin_unlock_bh(&ordered_sc_list->lock);
sc->callback(octeon_dev, status,
sc->callback_arg);
/* sc is freed by caller */
}
request_complete++;
} else {
/* no response yet */
request_complete = 0;
spin_unlock_bh
(&ordered_sc_list->lock);
}
/* If we hit the Max Ordered requests to process every loop,
* we quit
* and let this function be invoked the next time the poll
* thread runs
* to process the remaining requests. This function can take up
* the entire CPU if there is no upper limit to the requests
* processed.
*/
if (request_complete >= resp_to_process)
break;
} while (request_complete);
return 0;
}
EXPORT_SYMBOL_GPL(lio_process_ordered_list);
static void oct_poll_req_completion(struct work_struct *work)
{
struct cavium_wk *wk = (struct cavium_wk *)work;
struct octeon_device *oct = (struct octeon_device *)wk->ctxptr;
struct cavium_wq *cwq = &oct->dma_comp_wq;
lio_process_ordered_list(oct, 0);
if (atomic_read(&oct->response_list
[OCTEON_ORDERED_SC_LIST].pending_req_count))
queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(1));
}
@@ -0,0 +1,143 @@
/**********************************************************************
* Author: Cavium, Inc.
*
* Contact: support@cavium.com
* Please include "LiquidIO" in the subject.
*
* Copyright (c) 2003-2016 Cavium, Inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
* published by the Free Software Foundation.
*
* This file is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more
* details.
**********************************************************************/
/*! \file response_manager.h
* \brief Host Driver: Response queues for host instructions.
*/
#ifndef __RESPONSE_MANAGER_H__
#define __RESPONSE_MANAGER_H__
/** Maximum ordered requests to process in every invocation of
* lio_process_ordered_list(). The function will continue to process requests
* as long as it can find one that has finished processing. If it keeps
* finding requests that have completed, the function can run for ever. The
* value defined here sets an upper limit on the number of requests it can
* process before it returns control to the poll thread.
*/
#define MAX_ORD_REQS_TO_PROCESS 4096
/** Head of a response list. There are several response lists in the
* system. One for each response order- Unordered, ordered
* and 1 for noresponse entries on each instruction queue.
*/
struct octeon_response_list {
/** List structure to add delete pending entries to */
struct list_head head;
/** A lock for this response list */
spinlock_t lock;
atomic_t pending_req_count;
};
/** The type of response list.
*/
enum {
OCTEON_ORDERED_LIST = 0,
OCTEON_UNORDERED_NONBLOCKING_LIST = 1,
OCTEON_UNORDERED_BLOCKING_LIST = 2,
OCTEON_ORDERED_SC_LIST = 3,
OCTEON_DONE_SC_LIST = 4,
OCTEON_ZOMBIE_SC_LIST = 5
};
/** Response Order values for a Octeon Request. */
enum {
OCTEON_RESP_ORDERED = 0,
OCTEON_RESP_UNORDERED = 1,
OCTEON_RESP_NORESPONSE = 2
};
/** Error codes used in Octeon Host-Core communication.
*
* 31 16 15 0
* ---------------------------------
* | | |
* ---------------------------------
* Error codes are 32-bit wide. The upper 16-bits, called Major Error Number,
* are reserved to identify the group to which the error code belongs. The
* lower 16-bits, called Minor Error Number, carry the actual code.
*
* So error codes are (MAJOR NUMBER << 16)| MINOR_NUMBER.
*/
/*------------ Error codes used by host driver -----------------*/
#define DRIVER_MAJOR_ERROR_CODE 0x0000
/*------ Error codes used by firmware (bits 15..0 set by firmware */
#define FIRMWARE_MAJOR_ERROR_CODE 0x0001
/** A value of 0x00000000 indicates no error i.e. success */
#define DRIVER_ERROR_NONE 0x00000000
#define DRIVER_ERROR_REQ_PENDING 0x00000001
#define DRIVER_ERROR_REQ_TIMEOUT 0x00000003
#define DRIVER_ERROR_REQ_EINTR 0x00000004
#define DRIVER_ERROR_REQ_ENXIO 0x00000006
#define DRIVER_ERROR_REQ_ENOMEM 0x0000000C
#define DRIVER_ERROR_REQ_EINVAL 0x00000016
#define DRIVER_ERROR_REQ_FAILED 0x000000ff
/** Status for a request.
* If a request is not queued to Octeon by the driver, the driver returns
* an error condition that's describe by one of the OCTEON_REQ_ERR_* value
* below. If the request is successfully queued, the driver will return
* a OCTEON_REQUEST_PENDING status. OCTEON_REQUEST_TIMEOUT and
* OCTEON_REQUEST_INTERRUPTED are only returned by the driver if the
* response for request failed to arrive before a time-out period or if
* the request processing * got interrupted due to a signal respectively.
*/
enum {
OCTEON_REQUEST_DONE = (DRIVER_ERROR_NONE),
OCTEON_REQUEST_PENDING = (DRIVER_ERROR_REQ_PENDING),
OCTEON_REQUEST_TIMEOUT = (DRIVER_ERROR_REQ_TIMEOUT),
OCTEON_REQUEST_INTERRUPTED = (DRIVER_ERROR_REQ_EINTR),
OCTEON_REQUEST_NO_DEVICE = (0x00000021),
OCTEON_REQUEST_NOT_RUNNING,
OCTEON_REQUEST_INVALID_IQ,
OCTEON_REQUEST_INVALID_BUFCNT,
OCTEON_REQUEST_INVALID_RESP_ORDER,
OCTEON_REQUEST_NO_MEMORY,
OCTEON_REQUEST_INVALID_BUFSIZE,
OCTEON_REQUEST_NO_PENDING_ENTRY,
OCTEON_REQUEST_NO_IQ_SPACE = (0x7FFFFFFF)
};
#define FIRMWARE_STATUS_CODE(status) \
((FIRMWARE_MAJOR_ERROR_CODE << 16) | (status))
/** Initialize the response lists. The number of response lists to create is
* given by count.
* @param octeon_dev - the octeon device structure.
*/
int octeon_setup_response_list(struct octeon_device *octeon_dev);
void octeon_delete_response_list(struct octeon_device *octeon_dev);
/** Check the status of first entry in the ordered list. If the instruction at
* that entry finished processing or has timed-out, the entry is cleaned.
* @param octeon_dev - the octeon device structure.
* @param force_quit - the request is forced to timeout if this is 1
* @return 1 if the ordered list is empty, 0 otherwise.
*/
int lio_process_ordered_list(struct octeon_device *octeon_dev,
u32 force_quit);
#endif