numam-dpdk/drivers/net/octeon_ep/otx2_ep_vf.c
Sathesh Edara d826133ae8 net/octeon_ep: support CN10K SoC
This patch adds the required functionality in the Octeon endpoint
driver to support the CN10K endpoint device. It adds the CN10K SoC
specific routines to configure, enable, and disable input and output
queues to establish basic data transfers.

Signed-off-by: Sathesh Edara <sedara@marvell.com>
2022-09-22 10:43:51 +02:00

377 lines
10 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(C) 2021 Marvell.
*/
#include <errno.h>
#include <rte_common.h>
#include <rte_cycles.h>
#include "otx_ep_common.h"
#include "otx2_ep_vf.h"
static void
otx2_vf_setup_global_iq_reg(struct otx_ep_device *otx_ep, int q_no)
{
volatile uint64_t reg_val = 0ull;
/* Select ES, RO, NS, RDSIZE,DPTR Format#0 for IQs
* IS_64B is by default enabled.
*/
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_CONTROL(q_no));
reg_val |= SDP_VF_R_IN_CTL_RDSIZE;
reg_val |= SDP_VF_R_IN_CTL_IS_64B;
reg_val |= SDP_VF_R_IN_CTL_ESR;
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_IN_CONTROL(q_no));
}
static void
otx2_vf_setup_global_oq_reg(struct otx_ep_device *otx_ep, int q_no)
{
volatile uint64_t reg_val = 0ull;
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(q_no));
reg_val &= ~(SDP_VF_R_OUT_CTL_IMODE);
reg_val &= ~(SDP_VF_R_OUT_CTL_ROR_P);
reg_val &= ~(SDP_VF_R_OUT_CTL_NSR_P);
reg_val &= ~(SDP_VF_R_OUT_CTL_ROR_I);
reg_val &= ~(SDP_VF_R_OUT_CTL_NSR_I);
reg_val &= ~(SDP_VF_R_OUT_CTL_ES_I);
reg_val &= ~(SDP_VF_R_OUT_CTL_ROR_D);
reg_val &= ~(SDP_VF_R_OUT_CTL_NSR_D);
reg_val &= ~(SDP_VF_R_OUT_CTL_ES_D);
/* INFO/DATA ptr swap is required */
reg_val |= (SDP_VF_R_OUT_CTL_ES_P);
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(q_no));
}
static void
otx2_vf_setup_global_input_regs(struct otx_ep_device *otx_ep)
{
uint64_t q_no = 0ull;
for (q_no = 0; q_no < (otx_ep->sriov_info.rings_per_vf); q_no++)
otx2_vf_setup_global_iq_reg(otx_ep, q_no);
}
static void
otx2_vf_setup_global_output_regs(struct otx_ep_device *otx_ep)
{
uint32_t q_no;
for (q_no = 0; q_no < (otx_ep->sriov_info.rings_per_vf); q_no++)
otx2_vf_setup_global_oq_reg(otx_ep, q_no);
}
static void
otx2_vf_setup_device_regs(struct otx_ep_device *otx_ep)
{
otx2_vf_setup_global_input_regs(otx_ep);
otx2_vf_setup_global_output_regs(otx_ep);
}
static void
otx2_vf_setup_iq_regs(struct otx_ep_device *otx_ep, uint32_t iq_no)
{
struct otx_ep_instr_queue *iq = otx_ep->instr_queue[iq_no];
volatile uint64_t reg_val = 0ull;
int loop = SDP_VF_BUSY_LOOP_COUNT;
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_CONTROL(iq_no));
/* Wait till IDLE to set to 1, not supposed to configure BADDR
* as long as IDLE is 0
*/
if (!(reg_val & SDP_VF_R_IN_CTL_IDLE)) {
do {
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_CONTROL(iq_no));
} while ((!(reg_val & SDP_VF_R_IN_CTL_IDLE)) && loop--);
}
if (!loop) {
otx_ep_err("IDLE bit is not set\n");
return;
}
/* Write the start of the input queue's ring and its size */
oct_ep_write64(iq->base_addr_dma, otx_ep->hw_addr + SDP_VF_R_IN_INSTR_BADDR(iq_no));
oct_ep_write64(iq->nb_desc, otx_ep->hw_addr + SDP_VF_R_IN_INSTR_RSIZE(iq_no));
/* Remember the doorbell & instruction count register addr
* for this queue
*/
iq->doorbell_reg = (uint8_t *)otx_ep->hw_addr + SDP_VF_R_IN_INSTR_DBELL(iq_no);
iq->inst_cnt_reg = (uint8_t *)otx_ep->hw_addr + SDP_VF_R_IN_CNTS(iq_no);
otx_ep_dbg("InstQ[%d]:dbell reg @ 0x%p inst_cnt_reg @ 0x%p",
iq_no, iq->doorbell_reg, iq->inst_cnt_reg);
loop = SDP_VF_BUSY_LOOP_COUNT;
do {
reg_val = rte_read32(iq->inst_cnt_reg);
rte_write32(reg_val, iq->inst_cnt_reg);
} while (reg_val != 0 && loop--);
if (!loop) {
otx_ep_err("INST CNT REGISTER is not zero\n");
return;
}
/* IN INTR_THRESHOLD is set to max(FFFFFFFF) which disable the IN INTR
* to raise
*/
oct_ep_write64(OTX_EP_CLEAR_SDP_IN_INT_LVLS,
otx_ep->hw_addr + SDP_VF_R_IN_INT_LEVELS(iq_no));
}
static void
otx2_vf_setup_oq_regs(struct otx_ep_device *otx_ep, uint32_t oq_no)
{
volatile uint64_t reg_val = 0ull;
uint64_t oq_ctl = 0ull;
uint64_t loop = OTX_EP_BUSY_LOOP_COUNT;
struct otx_ep_droq *droq = otx_ep->droq[oq_no];
/* Wait on IDLE to set to 1, supposed to configure BADDR
* as long as IDLE is 0
*/
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(oq_no));
while ((!(reg_val & SDP_VF_R_OUT_CTL_IDLE)) && loop--) {
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(oq_no));
rte_delay_ms(1);
}
if (!loop) {
otx_ep_err("OUT CNT REGISTER value is zero\n");
return;
}
oct_ep_write64(droq->desc_ring_dma, otx_ep->hw_addr + SDP_VF_R_OUT_SLIST_BADDR(oq_no));
oct_ep_write64(droq->nb_desc, otx_ep->hw_addr + SDP_VF_R_OUT_SLIST_RSIZE(oq_no));
oq_ctl = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(oq_no));
/* Clear the ISIZE and BSIZE (22-0) */
oq_ctl &= ~(OTX_EP_CLEAR_ISIZE_BSIZE);
/* Populate the BSIZE (15-0) */
oq_ctl |= (droq->buffer_size & OTX_EP_DROQ_BUFSZ_MASK);
oct_ep_write64(oq_ctl, otx_ep->hw_addr + SDP_VF_R_OUT_CONTROL(oq_no));
/* Mapped address of the pkt_sent and pkts_credit regs */
droq->pkts_sent_reg = (uint8_t *)otx_ep->hw_addr + SDP_VF_R_OUT_CNTS(oq_no);
droq->pkts_credit_reg = (uint8_t *)otx_ep->hw_addr + SDP_VF_R_OUT_SLIST_DBELL(oq_no);
rte_write64(OTX_EP_CLEAR_OUT_INT_LVLS, otx_ep->hw_addr + SDP_VF_R_OUT_INT_LEVELS(oq_no));
/* Clear PKT_CNT register */
rte_write64(OTX_EP_CLEAR_SDP_OUT_PKT_CNT, (uint8_t *)otx_ep->hw_addr +
SDP_VF_R_OUT_PKT_CNT(oq_no));
loop = OTX_EP_BUSY_LOOP_COUNT;
/* Clear the OQ doorbell */
rte_write32(OTX_EP_CLEAR_SLIST_DBELL, droq->pkts_credit_reg);
while ((rte_read32(droq->pkts_credit_reg) != 0ull) && loop--) {
rte_write32(OTX_EP_CLEAR_SLIST_DBELL, droq->pkts_credit_reg);
rte_delay_ms(1);
}
if (!loop) {
otx_ep_err("Packets credit register value is not cleared\n");
return;
}
otx_ep_dbg("SDP_R[%d]_credit:%x", oq_no, rte_read32(droq->pkts_credit_reg));
/* Clear the OQ_OUT_CNTS doorbell */
reg_val = rte_read32(droq->pkts_sent_reg);
rte_write32((uint32_t)reg_val, droq->pkts_sent_reg);
otx_ep_dbg("SDP_R[%d]_sent: %x", oq_no, rte_read32(droq->pkts_sent_reg));
loop = OTX_EP_BUSY_LOOP_COUNT;
while (((rte_read32(droq->pkts_sent_reg)) != 0ull) && loop--) {
reg_val = rte_read32(droq->pkts_sent_reg);
rte_write32((uint32_t)reg_val, droq->pkts_sent_reg);
rte_delay_ms(1);
}
if (!loop) {
otx_ep_err("Packets sent register value is not cleared\n");
return;
}
otx_ep_dbg("SDP_R[%d]_sent: %x", oq_no, rte_read32(droq->pkts_sent_reg));
}
static int
otx2_vf_enable_iq(struct otx_ep_device *otx_ep, uint32_t q_no)
{
uint64_t loop = SDP_VF_BUSY_LOOP_COUNT;
uint64_t reg_val = 0ull;
/* Resetting doorbells during IQ enabling also to handle abrupt
* guest reboot. IQ reset does not clear the doorbells.
*/
oct_ep_write64(0xFFFFFFFF, otx_ep->hw_addr + SDP_VF_R_IN_INSTR_DBELL(q_no));
while (((oct_ep_read64(otx_ep->hw_addr +
SDP_VF_R_IN_INSTR_DBELL(q_no))) != 0ull) && loop--) {
rte_delay_ms(1);
}
if (!loop) {
otx_ep_err("INSTR DBELL not coming back to 0\n");
return -EIO;
}
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_ENABLE(q_no));
reg_val |= 0x1ull;
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_IN_ENABLE(q_no));
otx_ep_info("IQ[%d] enable done", q_no);
return 0;
}
static int
otx2_vf_enable_oq(struct otx_ep_device *otx_ep, uint32_t q_no)
{
uint64_t reg_val = 0ull;
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_ENABLE(q_no));
reg_val |= 0x1ull;
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_OUT_ENABLE(q_no));
otx_ep_info("OQ[%d] enable done", q_no);
return 0;
}
static int
otx2_vf_enable_io_queues(struct otx_ep_device *otx_ep)
{
uint32_t q_no = 0;
int ret;
for (q_no = 0; q_no < otx_ep->nb_tx_queues; q_no++) {
ret = otx2_vf_enable_iq(otx_ep, q_no);
if (ret)
return ret;
}
for (q_no = 0; q_no < otx_ep->nb_rx_queues; q_no++)
otx2_vf_enable_oq(otx_ep, q_no);
return 0;
}
static void
otx2_vf_disable_iq(struct otx_ep_device *otx_ep, uint32_t q_no)
{
uint64_t reg_val = 0ull;
/* Reset the doorbell register for this Input Queue. */
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_ENABLE(q_no));
reg_val &= ~0x1ull;
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_IN_ENABLE(q_no));
}
static void
otx2_vf_disable_oq(struct otx_ep_device *otx_ep, uint32_t q_no)
{
volatile uint64_t reg_val = 0ull;
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_OUT_ENABLE(q_no));
reg_val &= ~0x1ull;
oct_ep_write64(reg_val, otx_ep->hw_addr + SDP_VF_R_OUT_ENABLE(q_no));
}
static void
otx2_vf_disable_io_queues(struct otx_ep_device *otx_ep)
{
uint32_t q_no = 0;
for (q_no = 0; q_no < otx_ep->sriov_info.rings_per_vf; q_no++) {
otx2_vf_disable_iq(otx_ep, q_no);
otx2_vf_disable_oq(otx_ep, q_no);
}
}
static const struct otx_ep_config default_otx2_ep_conf = {
/* IQ attributes */
.iq = {
.max_iqs = OTX_EP_CFG_IO_QUEUES,
.instr_type = OTX_EP_64BYTE_INSTR,
.pending_list_size = (OTX_EP_MAX_IQ_DESCRIPTORS *
OTX_EP_CFG_IO_QUEUES),
},
/* OQ attributes */
.oq = {
.max_oqs = OTX_EP_CFG_IO_QUEUES,
.info_ptr = OTX_EP_OQ_INFOPTR_MODE,
.refill_threshold = OTX_EP_OQ_REFIL_THRESHOLD,
},
.num_iqdef_descs = OTX_EP_MAX_IQ_DESCRIPTORS,
.num_oqdef_descs = OTX_EP_MAX_OQ_DESCRIPTORS,
.oqdef_buf_size = OTX_EP_OQ_BUF_SIZE,
};
static const struct otx_ep_config*
otx2_ep_get_defconf(struct otx_ep_device *otx_ep_dev __rte_unused)
{
const struct otx_ep_config *default_conf = NULL;
default_conf = &default_otx2_ep_conf;
return default_conf;
}
int
otx2_ep_vf_setup_device(struct otx_ep_device *otx_ep)
{
uint64_t reg_val = 0ull;
/* If application doesn't provide its conf, use driver default conf */
if (otx_ep->conf == NULL) {
otx_ep->conf = otx2_ep_get_defconf(otx_ep);
if (otx_ep->conf == NULL) {
otx_ep_err("SDP VF default config not found");
return -ENOENT;
}
otx_ep_info("Default config is used");
}
/* Get IOQs (RPVF] count */
reg_val = oct_ep_read64(otx_ep->hw_addr + SDP_VF_R_IN_CONTROL(0));
otx_ep->sriov_info.rings_per_vf = ((reg_val >> SDP_VF_R_IN_CTL_RPVF_POS)
& SDP_VF_R_IN_CTL_RPVF_MASK);
otx_ep_info("SDP RPVF: %d", otx_ep->sriov_info.rings_per_vf);
otx_ep->fn_list.setup_iq_regs = otx2_vf_setup_iq_regs;
otx_ep->fn_list.setup_oq_regs = otx2_vf_setup_oq_regs;
otx_ep->fn_list.setup_device_regs = otx2_vf_setup_device_regs;
otx_ep->fn_list.enable_io_queues = otx2_vf_enable_io_queues;
otx_ep->fn_list.disable_io_queues = otx2_vf_disable_io_queues;
otx_ep->fn_list.enable_iq = otx2_vf_enable_iq;
otx_ep->fn_list.disable_iq = otx2_vf_disable_iq;
otx_ep->fn_list.enable_oq = otx2_vf_enable_oq;
otx_ep->fn_list.disable_oq = otx2_vf_disable_oq;
return 0;
}