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,10 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_OCTEONTX2_CPT) += rvu_cptcommon.o rvu_cptpf.o rvu_cptvf.o
rvu_cptcommon-objs := cn10k_cpt.o otx2_cptlf.o otx2_cpt_mbox_common.o
rvu_cptpf-objs := otx2_cptpf_main.o otx2_cptpf_mbox.o \
otx2_cptpf_ucode.o otx2_cpt_devlink.o
rvu_cptvf-objs := otx2_cptvf_main.o otx2_cptvf_mbox.o \
otx2_cptvf_reqmgr.o otx2_cptvf_algs.o
ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2021 Marvell. */
#include <linux/soc/marvell/octeontx2/asm.h>
#include "otx2_cptpf.h"
#include "otx2_cptvf.h"
#include "otx2_cptlf.h"
#include "cn10k_cpt.h"
#include "otx2_cpt_common.h"
static void cn10k_cpt_send_cmd(union otx2_cpt_inst_s *cptinst, u32 insts_num,
struct otx2_cptlf_info *lf);
static struct cpt_hw_ops otx2_hw_ops = {
.send_cmd = otx2_cpt_send_cmd,
.cpt_get_compcode = otx2_cpt_get_compcode,
.cpt_get_uc_compcode = otx2_cpt_get_uc_compcode,
.cpt_sg_info_create = otx2_sg_info_create,
};
static struct cpt_hw_ops cn10k_hw_ops = {
.send_cmd = cn10k_cpt_send_cmd,
.cpt_get_compcode = cn10k_cpt_get_compcode,
.cpt_get_uc_compcode = cn10k_cpt_get_uc_compcode,
.cpt_sg_info_create = otx2_sg_info_create,
};
static void cn10k_cpt_send_cmd(union otx2_cpt_inst_s *cptinst, u32 insts_num,
struct otx2_cptlf_info *lf)
{
void *lmtline = lf->lfs->lmt_info.base + (lf->slot * LMTLINE_SIZE);
u64 val = (lf->slot & 0x7FF);
u64 tar_addr = 0;
/* tar_addr<6:4> = Size of first LMTST - 1 in units of 128b. */
tar_addr |= (__force u64)lf->ioreg |
(((OTX2_CPT_INST_SIZE/16) - 1) & 0x7) << 4;
/*
* Make sure memory areas pointed in CPT_INST_S
* are flushed before the instruction is sent to CPT
*/
dma_wmb();
/* Copy CPT command to LMTLINE */
memcpy(lmtline, cptinst, insts_num * OTX2_CPT_INST_SIZE);
cn10k_lmt_flush(val, tar_addr);
}
void cn10k_cpt_lmtst_free(struct pci_dev *pdev, struct otx2_cptlfs_info *lfs)
{
struct otx2_lmt_info *lmt_info = &lfs->lmt_info;
if (!lmt_info->base)
return;
dma_free_attrs(&pdev->dev, lmt_info->size,
lmt_info->base - lmt_info->align,
lmt_info->iova - lmt_info->align,
DMA_ATTR_FORCE_CONTIGUOUS);
}
EXPORT_SYMBOL_NS_GPL(cn10k_cpt_lmtst_free, "CRYPTO_DEV_OCTEONTX2_CPT");
static int cn10k_cpt_lmtst_alloc(struct pci_dev *pdev,
struct otx2_cptlfs_info *lfs, u32 size)
{
struct otx2_lmt_info *lmt_info = &lfs->lmt_info;
dma_addr_t align_iova;
dma_addr_t iova;
lmt_info->base = dma_alloc_attrs(&pdev->dev, size, &iova, GFP_KERNEL,
DMA_ATTR_FORCE_CONTIGUOUS);
if (!lmt_info->base)
return -ENOMEM;
align_iova = ALIGN((u64)iova, LMTLINE_ALIGN);
lmt_info->iova = align_iova;
lmt_info->align = align_iova - iova;
lmt_info->size = size;
lmt_info->base += lmt_info->align;
return 0;
}
int cn10k_cptpf_lmtst_init(struct otx2_cptpf_dev *cptpf)
{
struct pci_dev *pdev = cptpf->pdev;
u32 size;
int ret;
if (!test_bit(CN10K_LMTST, &cptpf->cap_flag)) {
cptpf->lfs.ops = &otx2_hw_ops;
return 0;
}
cptpf->lfs.ops = &cn10k_hw_ops;
size = OTX2_CPT_MAX_VFS_NUM * LMTLINE_SIZE + LMTLINE_ALIGN;
ret = cn10k_cpt_lmtst_alloc(pdev, &cptpf->lfs, size);
if (ret) {
dev_err(&pdev->dev, "PF-%d LMTLINE memory allocation failed\n",
cptpf->pf_id);
return ret;
}
ret = otx2_cpt_lmtst_tbl_setup_msg(&cptpf->lfs);
if (ret) {
dev_err(&pdev->dev, "PF-%d: LMTST Table setup failed\n",
cptpf->pf_id);
cn10k_cpt_lmtst_free(pdev, &cptpf->lfs);
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cn10k_cptpf_lmtst_init, "CRYPTO_DEV_OCTEONTX2_CPT");
int cn10k_cptvf_lmtst_init(struct otx2_cptvf_dev *cptvf)
{
struct pci_dev *pdev = cptvf->pdev;
u32 size;
int ret;
if (!test_bit(CN10K_LMTST, &cptvf->cap_flag))
return 0;
size = cptvf->lfs.lfs_num * LMTLINE_SIZE + LMTLINE_ALIGN;
ret = cn10k_cpt_lmtst_alloc(pdev, &cptvf->lfs, size);
if (ret) {
dev_err(&pdev->dev, "VF-%d LMTLINE memory allocation failed\n",
cptvf->vf_id);
return ret;
}
ret = otx2_cpt_lmtst_tbl_setup_msg(&cptvf->lfs);
if (ret) {
dev_err(&pdev->dev, "VF-%d: LMTST Table setup failed\n",
cptvf->vf_id);
cn10k_cpt_lmtst_free(pdev, &cptvf->lfs);
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cn10k_cptvf_lmtst_init, "CRYPTO_DEV_OCTEONTX2_CPT");
void cn10k_cpt_hw_ctx_clear(struct pci_dev *pdev,
struct cn10k_cpt_errata_ctx *er_ctx)
{
u64 cptr_dma;
if (!is_dev_cn10ka_ax(pdev))
return;
cptr_dma = er_ctx->cptr_dma & ~(BIT_ULL(60));
cn10k_cpt_ctx_flush(pdev, cptr_dma, true);
dma_unmap_single(&pdev->dev, cptr_dma, CN10K_CPT_HW_CTX_SIZE,
DMA_BIDIRECTIONAL);
kfree(er_ctx->hw_ctx);
}
EXPORT_SYMBOL_NS_GPL(cn10k_cpt_hw_ctx_clear, "CRYPTO_DEV_OCTEONTX2_CPT");
void cn10k_cpt_hw_ctx_set(union cn10k_cpt_hw_ctx *hctx, u16 ctx_sz)
{
hctx->w0.aop_valid = 1;
hctx->w0.ctx_hdr_sz = 0;
hctx->w0.ctx_sz = ctx_sz;
hctx->w0.ctx_push_sz = 1;
}
EXPORT_SYMBOL_NS_GPL(cn10k_cpt_hw_ctx_set, "CRYPTO_DEV_OCTEONTX2_CPT");
int cn10k_cpt_hw_ctx_init(struct pci_dev *pdev,
struct cn10k_cpt_errata_ctx *er_ctx)
{
union cn10k_cpt_hw_ctx *hctx;
u64 cptr_dma;
er_ctx->cptr_dma = 0;
er_ctx->hw_ctx = NULL;
if (!is_dev_cn10ka_ax(pdev))
return 0;
hctx = kmalloc(CN10K_CPT_HW_CTX_SIZE, GFP_KERNEL);
if (unlikely(!hctx))
return -ENOMEM;
cptr_dma = dma_map_single(&pdev->dev, hctx, CN10K_CPT_HW_CTX_SIZE,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(&pdev->dev, cptr_dma)) {
kfree(hctx);
return -ENOMEM;
}
cn10k_cpt_hw_ctx_set(hctx, 1);
er_ctx->hw_ctx = hctx;
er_ctx->cptr_dma = cptr_dma | BIT_ULL(60);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cn10k_cpt_hw_ctx_init, "CRYPTO_DEV_OCTEONTX2_CPT");
void cn10k_cpt_ctx_flush(struct pci_dev *pdev, u64 cptr, bool inval)
{
struct otx2_cptvf_dev *cptvf = pci_get_drvdata(pdev);
struct otx2_cptlfs_info *lfs = &cptvf->lfs;
u64 reg;
reg = (uintptr_t)cptr >> 7;
if (inval)
reg = reg | BIT_ULL(46);
otx2_cpt_write64(lfs->reg_base, lfs->blkaddr, lfs->lf[0].slot,
OTX2_CPT_LF_CTX_FLUSH, reg);
/* Make sure that the FLUSH operation is complete */
wmb();
otx2_cpt_read64(lfs->reg_base, lfs->blkaddr, lfs->lf[0].slot,
OTX2_CPT_LF_CTX_ERR);
}
EXPORT_SYMBOL_NS_GPL(cn10k_cpt_ctx_flush, "CRYPTO_DEV_OCTEONTX2_CPT");
void cptvf_hw_ops_get(struct otx2_cptvf_dev *cptvf)
{
if (test_bit(CN10K_LMTST, &cptvf->cap_flag))
cptvf->lfs.ops = &cn10k_hw_ops;
else
cptvf->lfs.ops = &otx2_hw_ops;
}
EXPORT_SYMBOL_NS_GPL(cptvf_hw_ops_get, "CRYPTO_DEV_OCTEONTX2_CPT");
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2021 Marvell.
*/
#ifndef __CN10K_CPT_H
#define __CN10K_CPT_H
#include "otx2_cpt_common.h"
#include "otx2_cptpf.h"
#include "otx2_cptvf.h"
#define CN10K_CPT_HW_CTX_SIZE 256
union cn10k_cpt_hw_ctx {
u64 u;
struct {
u64 reserved_0_47:48;
u64 ctx_push_sz:7;
u64 reserved_55:1;
u64 ctx_hdr_sz:2;
u64 aop_valid:1;
u64 reserved_59:1;
u64 ctx_sz:4;
} w0;
};
struct cn10k_cpt_errata_ctx {
union cn10k_cpt_hw_ctx *hw_ctx;
u64 cptr_dma;
};
static inline u8 cn10k_cpt_get_compcode(union otx2_cpt_res_s *result)
{
return ((struct cn10k_cpt_res_s *)result)->compcode;
}
static inline u8 cn10k_cpt_get_uc_compcode(union otx2_cpt_res_s *result)
{
return ((struct cn10k_cpt_res_s *)result)->uc_compcode;
}
static inline u8 otx2_cpt_get_compcode(union otx2_cpt_res_s *result)
{
return ((struct cn9k_cpt_res_s *)result)->compcode;
}
static inline u8 otx2_cpt_get_uc_compcode(union otx2_cpt_res_s *result)
{
return ((struct cn9k_cpt_res_s *)result)->uc_compcode;
}
int cn10k_cptpf_lmtst_init(struct otx2_cptpf_dev *cptpf);
int cn10k_cptvf_lmtst_init(struct otx2_cptvf_dev *cptvf);
void cn10k_cpt_lmtst_free(struct pci_dev *pdev, struct otx2_cptlfs_info *lfs);
void cn10k_cpt_ctx_flush(struct pci_dev *pdev, u64 cptr, bool inval);
int cn10k_cpt_hw_ctx_init(struct pci_dev *pdev,
struct cn10k_cpt_errata_ctx *er_ctx);
void cn10k_cpt_hw_ctx_clear(struct pci_dev *pdev,
struct cn10k_cpt_errata_ctx *er_ctx);
void cn10k_cpt_hw_ctx_set(union cn10k_cpt_hw_ctx *hctx, u16 ctx_sz);
void cptvf_hw_ops_get(struct otx2_cptvf_dev *cptvf);
#endif /* __CN10K_CPTLF_H */
@@ -0,0 +1,213 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPT_COMMON_H
#define __OTX2_CPT_COMMON_H
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/crypto.h>
#include <net/devlink.h>
#include "otx2_cpt_hw_types.h"
#include "rvu.h"
#include "mbox.h"
#define OTX2_CPT_MAX_VFS_NUM 128
#define OTX2_CPT_RVU_FUNC_ADDR_S(blk, slot, offs) \
(((blk) << 20) | ((slot) << 12) | (offs))
#define OTX2_CPT_RVU_PFFUNC(pdev, pf, func) rvu_make_pcifunc(pdev, pf, func)
#define OTX2_CPT_INVALID_CRYPTO_ENG_GRP 0xFF
#define OTX2_CPT_NAME_LENGTH 64
#define OTX2_CPT_DMA_MINALIGN 128
/* HW capability flags */
#define CN10K_MBOX 0
#define CN10K_LMTST 1
#define BAD_OTX2_CPT_ENG_TYPE OTX2_CPT_MAX_ENG_TYPES
enum otx2_cpt_eng_type {
OTX2_CPT_AE_TYPES = 1,
OTX2_CPT_SE_TYPES = 2,
OTX2_CPT_IE_TYPES = 3,
OTX2_CPT_MAX_ENG_TYPES,
};
/* Take mbox id from end of CPT mbox range in AF (range 0xA00 - 0xBFF) */
#define MBOX_MSG_RX_INLINE_IPSEC_LF_CFG 0xBFE
#define MBOX_MSG_GET_ENG_GRP_NUM 0xBFF
#define MBOX_MSG_GET_CAPS 0xBFD
#define MBOX_MSG_GET_KVF_LIMITS 0xBFC
/*
* Message request to config cpt lf for inline inbound ipsec.
* This message is only used between CPT PF <-> CPT VF
*/
struct otx2_cpt_rx_inline_lf_cfg {
struct mbox_msghdr hdr;
u16 sso_pf_func;
u16 param1;
u16 param2;
u16 opcode;
u32 credit;
u32 credit_th;
u16 bpid;
u32 reserved;
u8 ctx_ilen_valid : 1;
u8 ctx_ilen : 7;
};
/*
* Message request and response to get engine group number
* which has attached a given type of engines (SE, AE, IE)
* This messages are only used between CPT PF <=> CPT VF
*/
struct otx2_cpt_egrp_num_msg {
struct mbox_msghdr hdr;
u8 eng_type;
};
struct otx2_cpt_egrp_num_rsp {
struct mbox_msghdr hdr;
u8 eng_type;
u8 eng_grp_num;
};
/*
* Message request and response to get kernel crypto limits
* This messages are only used between CPT PF <-> CPT VF
*/
struct otx2_cpt_kvf_limits_msg {
struct mbox_msghdr hdr;
};
struct otx2_cpt_kvf_limits_rsp {
struct mbox_msghdr hdr;
u8 kvf_limits;
};
/* CPT HW capabilities */
union otx2_cpt_eng_caps {
u64 u;
struct {
u64 reserved_0_4:5;
u64 mul:1;
u64 sha1_sha2:1;
u64 chacha20:1;
u64 zuc_snow3g:1;
u64 sha3:1;
u64 aes:1;
u64 kasumi:1;
u64 des:1;
u64 crc:1;
u64 mmul:1;
u64 reserved_15_33:19;
u64 pdcp_chain:1;
u64 reserved_35_63:29;
};
};
/*
* Message request and response to get HW capabilities for each
* engine type (SE, IE, AE).
* This messages are only used between CPT PF <=> CPT VF
*/
struct otx2_cpt_caps_msg {
struct mbox_msghdr hdr;
};
struct otx2_cpt_caps_rsp {
struct mbox_msghdr hdr;
u16 cpt_pf_drv_version;
u8 cpt_revision;
union otx2_cpt_eng_caps eng_caps[OTX2_CPT_MAX_ENG_TYPES];
};
static inline void otx2_cpt_write64(void __iomem *reg_base, u64 blk, u64 slot,
u64 offs, u64 val)
{
writeq_relaxed(val, reg_base +
OTX2_CPT_RVU_FUNC_ADDR_S(blk, slot, offs));
}
static inline u64 otx2_cpt_read64(void __iomem *reg_base, u64 blk, u64 slot,
u64 offs)
{
return readq_relaxed(reg_base +
OTX2_CPT_RVU_FUNC_ADDR_S(blk, slot, offs));
}
static inline bool is_dev_otx2(struct pci_dev *pdev)
{
return pdev->device == OTX2_CPT_PCI_PF_DEVICE_ID ||
pdev->device == OTX2_CPT_PCI_VF_DEVICE_ID;
}
static inline bool is_dev_cn10ka(struct pci_dev *pdev)
{
return pdev->subsystem_device == CPT_PCI_SUBSYS_DEVID_CN10K_A;
}
static inline bool is_dev_cn10ka_ax(struct pci_dev *pdev)
{
return pdev->subsystem_device == CPT_PCI_SUBSYS_DEVID_CN10K_A &&
((pdev->revision & 0xFF) == 4 ||
(pdev->revision & 0xFF) == 0x50 ||
(pdev->revision & 0xFF) == 0x51);
}
static inline bool is_dev_cn10kb(struct pci_dev *pdev)
{
return pdev->subsystem_device == CPT_PCI_SUBSYS_DEVID_CN10K_B;
}
static inline bool is_dev_cn10ka_b0(struct pci_dev *pdev)
{
return pdev->subsystem_device == CPT_PCI_SUBSYS_DEVID_CN10K_A &&
(pdev->revision & 0xFF) == 0x54;
}
static inline void otx2_cpt_set_hw_caps(struct pci_dev *pdev,
unsigned long *cap_flag)
{
if (!is_dev_otx2(pdev)) {
__set_bit(CN10K_MBOX, cap_flag);
__set_bit(CN10K_LMTST, cap_flag);
}
}
static inline bool cpt_is_errata_38550_exists(struct pci_dev *pdev)
{
return is_dev_otx2(pdev) || is_dev_cn10ka_ax(pdev);
}
static inline bool cpt_feature_sgv2(struct pci_dev *pdev)
{
return !is_dev_otx2(pdev) && !is_dev_cn10ka_ax(pdev);
}
int otx2_cpt_send_ready_msg(struct otx2_mbox *mbox, struct pci_dev *pdev);
int otx2_cpt_send_mbox_msg(struct otx2_mbox *mbox, struct pci_dev *pdev);
int otx2_cpt_send_af_reg_requests(struct otx2_mbox *mbox,
struct pci_dev *pdev);
int otx2_cpt_add_write_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 val, int blkaddr);
int otx2_cpt_read_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 *val, int blkaddr);
int otx2_cpt_write_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 val, int blkaddr);
struct otx2_cptlfs_info;
int otx2_cpt_attach_rscrs_msg(struct otx2_cptlfs_info *lfs);
int otx2_cpt_detach_rsrcs_msg(struct otx2_cptlfs_info *lfs);
int otx2_cpt_msix_offset_msg(struct otx2_cptlfs_info *lfs);
int otx2_cpt_sync_mbox_msg(struct otx2_mbox *mbox);
int otx2_cpt_lf_reset_msg(struct otx2_cptlfs_info *lfs, int slot);
int otx2_cpt_lmtst_tbl_setup_msg(struct otx2_cptlfs_info *lfs);
#endif /* __OTX2_CPT_COMMON_H */
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2021 Marvell. */
#include "otx2_cpt_devlink.h"
static int otx2_cpt_dl_egrp_create(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
return otx2_cpt_dl_custom_egrp_create(cptpf, ctx);
}
static int otx2_cpt_dl_egrp_delete(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
return otx2_cpt_dl_custom_egrp_delete(cptpf, ctx);
}
static int otx2_cpt_dl_uc_info(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
ctx->val.vstr[0] = '\0';
return 0;
}
static int otx2_cpt_dl_t106_mode_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
struct pci_dev *pdev = cptpf->pdev;
u64 reg_val = 0;
otx2_cpt_read_af_reg(&cptpf->afpf_mbox, pdev, CPT_AF_CTL, &reg_val,
BLKADDR_CPT0);
ctx->val.vu8 = (reg_val >> 18) & 0x1;
return 0;
}
static int otx2_cpt_dl_t106_mode_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
struct pci_dev *pdev = cptpf->pdev;
u64 reg_val = 0;
if (cptpf->enabled_vfs != 0 || cptpf->eng_grps.is_grps_created)
return -EPERM;
if (cpt_feature_sgv2(pdev)) {
otx2_cpt_read_af_reg(&cptpf->afpf_mbox, pdev, CPT_AF_CTL,
&reg_val, BLKADDR_CPT0);
reg_val &= ~(0x1ULL << 18);
reg_val |= ((u64)ctx->val.vu8 & 0x1) << 18;
return otx2_cpt_write_af_reg(&cptpf->afpf_mbox, pdev,
CPT_AF_CTL, reg_val, BLKADDR_CPT0);
}
return 0;
}
enum otx2_cpt_dl_param_id {
OTX2_CPT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
OTX2_CPT_DEVLINK_PARAM_ID_EGRP_CREATE,
OTX2_CPT_DEVLINK_PARAM_ID_EGRP_DELETE,
OTX2_CPT_DEVLINK_PARAM_ID_T106_MODE,
};
static const struct devlink_param otx2_cpt_dl_params[] = {
DEVLINK_PARAM_DRIVER(OTX2_CPT_DEVLINK_PARAM_ID_EGRP_CREATE,
"egrp_create", DEVLINK_PARAM_TYPE_STRING,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
otx2_cpt_dl_uc_info, otx2_cpt_dl_egrp_create,
NULL),
DEVLINK_PARAM_DRIVER(OTX2_CPT_DEVLINK_PARAM_ID_EGRP_DELETE,
"egrp_delete", DEVLINK_PARAM_TYPE_STRING,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
otx2_cpt_dl_uc_info, otx2_cpt_dl_egrp_delete,
NULL),
DEVLINK_PARAM_DRIVER(OTX2_CPT_DEVLINK_PARAM_ID_T106_MODE,
"t106_mode", DEVLINK_PARAM_TYPE_U8,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
otx2_cpt_dl_t106_mode_get, otx2_cpt_dl_t106_mode_set,
NULL),
};
static int otx2_cpt_dl_info_firmware_version_put(struct devlink_info_req *req,
struct otx2_cpt_eng_grp_info grp[],
const char *ver_name, int eng_type)
{
struct otx2_cpt_engs_rsvd *eng;
int i;
for (i = 0; i < OTX2_CPT_MAX_ENGINE_GROUPS; i++) {
eng = find_engines_by_type(&grp[i], eng_type);
if (eng)
return devlink_info_version_running_put(req, ver_name,
eng->ucode->ver_str);
}
return 0;
}
static int otx2_cpt_devlink_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
int err;
err = otx2_cpt_dl_info_firmware_version_put(req, cptpf->eng_grps.grp,
"fw.ae", OTX2_CPT_AE_TYPES);
if (err)
return err;
err = otx2_cpt_dl_info_firmware_version_put(req, cptpf->eng_grps.grp,
"fw.se", OTX2_CPT_SE_TYPES);
if (err)
return err;
return otx2_cpt_dl_info_firmware_version_put(req, cptpf->eng_grps.grp,
"fw.ie", OTX2_CPT_IE_TYPES);
}
static const struct devlink_ops otx2_cpt_devlink_ops = {
.info_get = otx2_cpt_devlink_info_get,
};
int otx2_cpt_register_dl(struct otx2_cptpf_dev *cptpf)
{
struct device *dev = &cptpf->pdev->dev;
struct otx2_cpt_devlink *cpt_dl;
struct devlink *dl;
int ret;
dl = devlink_alloc(&otx2_cpt_devlink_ops,
sizeof(struct otx2_cpt_devlink), dev);
if (!dl) {
dev_warn(dev, "devlink_alloc failed\n");
return -ENOMEM;
}
cpt_dl = devlink_priv(dl);
cpt_dl->dl = dl;
cpt_dl->cptpf = cptpf;
cptpf->dl = dl;
ret = devlink_params_register(dl, otx2_cpt_dl_params,
ARRAY_SIZE(otx2_cpt_dl_params));
if (ret) {
dev_err(dev, "devlink params register failed with error %d",
ret);
devlink_free(dl);
return ret;
}
devlink_register(dl);
return 0;
}
void otx2_cpt_unregister_dl(struct otx2_cptpf_dev *cptpf)
{
struct devlink *dl = cptpf->dl;
if (!dl)
return;
devlink_unregister(dl);
devlink_params_unregister(dl, otx2_cpt_dl_params,
ARRAY_SIZE(otx2_cpt_dl_params));
devlink_free(dl);
}
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2021 Marvell.
*/
#ifndef __OTX2_CPT_DEVLINK_H
#define __OTX2_CPT_DEVLINK_H
#include "otx2_cpt_common.h"
#include "otx2_cptpf.h"
struct otx2_cpt_devlink {
struct devlink *dl;
struct otx2_cptpf_dev *cptpf;
};
/* Devlink APIs */
int otx2_cpt_register_dl(struct otx2_cptpf_dev *cptpf);
void otx2_cpt_unregister_dl(struct otx2_cptpf_dev *cptpf);
#endif /* __OTX2_CPT_DEVLINK_H */
@@ -0,0 +1,483 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPT_HW_TYPES_H
#define __OTX2_CPT_HW_TYPES_H
#include <linux/types.h>
/* Device IDs */
#define OTX2_CPT_PCI_PF_DEVICE_ID 0xA0FD
#define OTX2_CPT_PCI_VF_DEVICE_ID 0xA0FE
#define CN10K_CPT_PCI_PF_DEVICE_ID 0xA0F2
#define CN10K_CPT_PCI_VF_DEVICE_ID 0xA0F3
#define CPT_PCI_SUBSYS_DEVID_CN10K_A 0xB900
#define CPT_PCI_SUBSYS_DEVID_CN10K_B 0xBD00
/* Mailbox interrupts offset */
#define OTX2_CPT_PF_MBOX_INT 6
#define OTX2_CPT_PF_INT_VEC_E_MBOXX(x, a) ((x) + (a))
/* Maximum supported microcode groups */
#define OTX2_CPT_MAX_ENGINE_GROUPS 8
/* CPT instruction size in bytes */
#define OTX2_CPT_INST_SIZE 64
/*
* CPT VF MSIX vectors and their offsets
*/
#define OTX2_CPT_VF_MSIX_VECTORS 1
#define OTX2_CPT_VF_INTR_MBOX_MASK BIT(0)
#define CN10K_CPT_VF_MBOX_REGION (0xC0000)
/* CPT LF MSIX vectors */
#define OTX2_CPT_LF_MSIX_VECTORS 2
/* OcteonTX2 CPT PF registers */
#define OTX2_CPT_PF_CONSTANTS (0x0)
#define OTX2_CPT_PF_RESET (0x100)
#define OTX2_CPT_PF_DIAG (0x120)
#define OTX2_CPT_PF_BIST_STATUS (0x160)
#define OTX2_CPT_PF_ECC0_CTL (0x200)
#define OTX2_CPT_PF_ECC0_FLIP (0x210)
#define OTX2_CPT_PF_ECC0_INT (0x220)
#define OTX2_CPT_PF_ECC0_INT_W1S (0x230)
#define OTX2_CPT_PF_ECC0_ENA_W1S (0x240)
#define OTX2_CPT_PF_ECC0_ENA_W1C (0x250)
#define OTX2_CPT_PF_MBOX_INTX(b) (0x400 | (b) << 3)
#define OTX2_CPT_PF_MBOX_INT_W1SX(b) (0x420 | (b) << 3)
#define OTX2_CPT_PF_MBOX_ENA_W1CX(b) (0x440 | (b) << 3)
#define OTX2_CPT_PF_MBOX_ENA_W1SX(b) (0x460 | (b) << 3)
#define OTX2_CPT_PF_EXEC_INT (0x500)
#define OTX2_CPT_PF_EXEC_INT_W1S (0x520)
#define OTX2_CPT_PF_EXEC_ENA_W1C (0x540)
#define OTX2_CPT_PF_EXEC_ENA_W1S (0x560)
#define OTX2_CPT_PF_GX_EN(b) (0x600 | (b) << 3)
#define OTX2_CPT_PF_EXEC_INFO (0x700)
#define OTX2_CPT_PF_EXEC_BUSY (0x800)
#define OTX2_CPT_PF_EXEC_INFO0 (0x900)
#define OTX2_CPT_PF_EXEC_INFO1 (0x910)
#define OTX2_CPT_PF_INST_REQ_PC (0x10000)
#define OTX2_CPT_PF_INST_LATENCY_PC (0x10020)
#define OTX2_CPT_PF_RD_REQ_PC (0x10040)
#define OTX2_CPT_PF_RD_LATENCY_PC (0x10060)
#define OTX2_CPT_PF_RD_UC_PC (0x10080)
#define OTX2_CPT_PF_ACTIVE_CYCLES_PC (0x10100)
#define OTX2_CPT_PF_EXE_CTL (0x4000000)
#define OTX2_CPT_PF_EXE_STATUS (0x4000008)
#define OTX2_CPT_PF_EXE_CLK (0x4000010)
#define OTX2_CPT_PF_EXE_DBG_CTL (0x4000018)
#define OTX2_CPT_PF_EXE_DBG_DATA (0x4000020)
#define OTX2_CPT_PF_EXE_BIST_STATUS (0x4000028)
#define OTX2_CPT_PF_EXE_REQ_TIMER (0x4000030)
#define OTX2_CPT_PF_EXE_MEM_CTL (0x4000038)
#define OTX2_CPT_PF_EXE_PERF_CTL (0x4001000)
#define OTX2_CPT_PF_EXE_DBG_CNTX(b) (0x4001100 | (b) << 3)
#define OTX2_CPT_PF_EXE_PERF_EVENT_CNT (0x4001180)
#define OTX2_CPT_PF_EXE_EPCI_INBX_CNT(b) (0x4001200 | (b) << 3)
#define OTX2_CPT_PF_EXE_EPCI_OUTBX_CNT(b) (0x4001240 | (b) << 3)
#define OTX2_CPT_PF_ENGX_UCODE_BASE(b) (0x4002000 | (b) << 3)
#define OTX2_CPT_PF_QX_CTL(b) (0x8000000 | (b) << 20)
#define OTX2_CPT_PF_QX_GMCTL(b) (0x8000020 | (b) << 20)
#define OTX2_CPT_PF_QX_CTL2(b) (0x8000100 | (b) << 20)
#define OTX2_CPT_PF_VFX_MBOXX(b, c) (0x8001000 | (b) << 20 | \
(c) << 8)
/* OcteonTX2 CPT LF registers */
#define OTX2_CPT_LF_CTL (0x10)
#define OTX2_CPT_LF_DONE_WAIT (0x30)
#define OTX2_CPT_LF_INPROG (0x40)
#define OTX2_CPT_LF_DONE (0x50)
#define OTX2_CPT_LF_DONE_ACK (0x60)
#define OTX2_CPT_LF_DONE_INT_ENA_W1S (0x90)
#define OTX2_CPT_LF_DONE_INT_ENA_W1C (0xa0)
#define OTX2_CPT_LF_MISC_INT (0xb0)
#define OTX2_CPT_LF_MISC_INT_W1S (0xc0)
#define OTX2_CPT_LF_MISC_INT_ENA_W1S (0xd0)
#define OTX2_CPT_LF_MISC_INT_ENA_W1C (0xe0)
#define OTX2_CPT_LF_Q_BASE (0xf0)
#define OTX2_CPT_LF_Q_SIZE (0x100)
#define OTX2_CPT_LF_Q_INST_PTR (0x110)
#define OTX2_CPT_LF_Q_GRP_PTR (0x120)
#define OTX2_CPT_LF_NQX(a) (0x400 | (a) << 3)
#define OTX2_CPT_LF_CTX_CTL (0x500)
#define OTX2_CPT_LF_CTX_FLUSH (0x510)
#define OTX2_CPT_LF_CTX_ERR (0x520)
#define OTX2_CPT_RVU_FUNC_BLKADDR_SHIFT 20
/* LMT LF registers */
#define OTX2_CPT_LMT_LFBASE BIT_ULL(OTX2_CPT_RVU_FUNC_BLKADDR_SHIFT)
#define OTX2_CPT_LMT_LF_LMTLINEX(a) (OTX2_CPT_LMT_LFBASE | 0x000 | \
(a) << 12)
/* RVU VF registers */
#define OTX2_RVU_VF_INT (0x20)
#define OTX2_RVU_VF_INT_W1S (0x28)
#define OTX2_RVU_VF_INT_ENA_W1S (0x30)
#define OTX2_RVU_VF_INT_ENA_W1C (0x38)
/*
* Enumeration otx2_cpt_ucode_error_code_e
*
* Enumerates ucode errors
*/
enum otx2_cpt_ucode_comp_code_e {
OTX2_CPT_UCC_SUCCESS = 0x00,
OTX2_CPT_UCC_INVALID_OPCODE = 0x01,
/* Scatter gather */
OTX2_CPT_UCC_SG_WRITE_LENGTH = 0x02,
OTX2_CPT_UCC_SG_LIST = 0x03,
OTX2_CPT_UCC_SG_NOT_SUPPORTED = 0x04,
};
/*
* Enumeration otx2_cpt_comp_e
*
* OcteonTX2 CPT Completion Enumeration
* Enumerates the values of CPT_RES_S[COMPCODE].
*/
enum otx2_cpt_comp_e {
OTX2_CPT_COMP_E_NOTDONE = 0x00,
OTX2_CPT_COMP_E_GOOD = 0x01,
OTX2_CPT_COMP_E_FAULT = 0x02,
OTX2_CPT_COMP_E_HWERR = 0x04,
OTX2_CPT_COMP_E_INSTERR = 0x05,
OTX2_CPT_COMP_E_WARN = 0x06
};
/*
* Enumeration otx2_cpt_vf_int_vec_e
*
* OcteonTX2 CPT VF MSI-X Vector Enumeration
* Enumerates the MSI-X interrupt vectors.
*/
enum otx2_cpt_vf_int_vec_e {
OTX2_CPT_VF_INT_VEC_E_MBOX = 0x00
};
/*
* Enumeration otx2_cpt_lf_int_vec_e
*
* OcteonTX2 CPT LF MSI-X Vector Enumeration
* Enumerates the MSI-X interrupt vectors.
*/
enum otx2_cpt_lf_int_vec_e {
OTX2_CPT_LF_INT_VEC_E_MISC = 0x00,
OTX2_CPT_LF_INT_VEC_E_DONE = 0x01
};
/*
* Structure otx2_cpt_inst_s
*
* CPT Instruction Structure
* This structure specifies the instruction layout. Instructions are
* stored in memory as little-endian unless CPT()_PF_Q()_CTL[INST_BE] is set.
* cpt_inst_s_s
* Word 0
* doneint:1 Done interrupt.
* 0 = No interrupts related to this instruction.
* 1 = When the instruction completes, CPT()_VQ()_DONE[DONE] will be
* incremented,and based on the rules described there an interrupt may
* occur.
* Word 1
* res_addr [127: 64] Result IOVA.
* If nonzero, specifies where to write CPT_RES_S.
* If zero, no result structure will be written.
* Address must be 16-byte aligned.
* Bits <63:49> are ignored by hardware; software should use a
* sign-extended bit <48> for forward compatibility.
* Word 2
* grp:10 [171:162] If [WQ_PTR] is nonzero, the SSO guest-group to use when
* CPT submits work SSO.
* For the SSO to not discard the add-work request, FPA_PF_MAP() must map
* [GRP] and CPT()_PF_Q()_GMCTL[GMID] as valid.
* tt:2 [161:160] If [WQ_PTR] is nonzero, the SSO tag type to use when CPT
* submits work to SSO
* tag:32 [159:128] If [WQ_PTR] is nonzero, the SSO tag to use when CPT
* submits work to SSO.
* Word 3
* wq_ptr [255:192] If [WQ_PTR] is nonzero, it is a pointer to a
* work-queue entry that CPT submits work to SSO after all context,
* output data, and result write operations are visible to other
* CNXXXX units and the cores. Bits <2:0> must be zero.
* Bits <63:49> are ignored by hardware; software should
* use a sign-extended bit <48> for forward compatibility.
* Internal:
* Bits <63:49>, <2:0> are ignored by hardware, treated as always 0x0.
* Word 4
* ei0; [319:256] Engine instruction word 0. Passed to the AE/SE.
* Word 5
* ei1; [383:320] Engine instruction word 1. Passed to the AE/SE.
* Word 6
* ei2; [447:384] Engine instruction word 1. Passed to the AE/SE.
* Word 7
* ei3; [511:448] Engine instruction word 1. Passed to the AE/SE.
*
*/
union otx2_cpt_inst_s {
u64 u[8];
struct {
/* Word 0 */
u64 nixtxl:3;
u64 doneint:1;
u64 nixtx_addr:60;
/* Word 1 */
u64 res_addr;
/* Word 2 */
u64 tag:32;
u64 tt:2;
u64 grp:10;
u64 reserved_172_175:4;
u64 rvu_pf_func:16;
/* Word 3 */
u64 qord:1;
u64 reserved_194_193:2;
u64 wq_ptr:61;
/* Word 4 */
u64 ei0;
/* Word 5 */
u64 ei1;
/* Word 6 */
u64 ei2;
/* Word 7 */
u64 ei3;
} s;
};
/*
* Structure otx2_cpt_res_s
*
* CPT Result Structure
* The CPT coprocessor writes the result structure after it completes a
* CPT_INST_S instruction. The result structure is exactly 16 bytes, and
* each instruction completion produces exactly one result structure.
*
* This structure is stored in memory as little-endian unless
* CPT()_PF_Q()_CTL[INST_BE] is set.
* cpt_res_s_s
* Word 0
* doneint:1 [16:16] Done interrupt. This bit is copied from the
* corresponding instruction's CPT_INST_S[DONEINT].
* compcode:8 [7:0] Indicates completion/error status of the CPT coprocessor
* for the associated instruction, as enumerated by CPT_COMP_E.
* Core software may write the memory location containing [COMPCODE] to
* 0x0 before ringing the doorbell, and then poll for completion by
* checking for a nonzero value.
* Once the core observes a nonzero [COMPCODE] value in this case,the CPT
* coprocessor will have also completed L2/DRAM write operations.
* Word 1
* reserved
*
*/
union otx2_cpt_res_s {
u64 u[2];
struct cn9k_cpt_res_s {
u64 compcode:8;
u64 uc_compcode:8;
u64 doneint:1;
u64 reserved_17_63:47;
u64 reserved_64_127;
} s;
struct cn10k_cpt_res_s {
u64 compcode:7;
u64 doneint:1;
u64 uc_compcode:8;
u64 rlen:16;
u64 spi:32;
u64 esn;
} cn10k;
};
/*
* Register (RVU_PF_BAR0) cpt#_af_constants1
*
* CPT AF Constants Register
* This register contains implementation-related parameters of CPT.
*/
union otx2_cptx_af_constants1 {
u64 u;
struct otx2_cptx_af_constants1_s {
u64 se:16;
u64 ie:16;
u64 ae:16;
u64 reserved_48_63:16;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_misc_int
*
* This register contain the per-queue miscellaneous interrupts.
*
*/
union otx2_cptx_lf_misc_int {
u64 u;
struct otx2_cptx_lf_misc_int_s {
u64 reserved_0:1;
u64 nqerr:1;
u64 irde:1;
u64 nwrp:1;
u64 reserved_4:1;
u64 hwerr:1;
u64 fault:1;
u64 reserved_7_63:57;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_misc_int_ena_w1s
*
* This register sets interrupt enable bits.
*
*/
union otx2_cptx_lf_misc_int_ena_w1s {
u64 u;
struct otx2_cptx_lf_misc_int_ena_w1s_s {
u64 reserved_0:1;
u64 nqerr:1;
u64 irde:1;
u64 nwrp:1;
u64 reserved_4:1;
u64 hwerr:1;
u64 fault:1;
u64 reserved_7_63:57;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_ctl
*
* This register configures the queue.
*
* When the queue is not execution-quiescent (see CPT_LF_INPROG[EENA,INFLIGHT]),
* software must only write this register with [ENA]=0.
*/
union otx2_cptx_lf_ctl {
u64 u;
struct otx2_cptx_lf_ctl_s {
u64 ena:1;
u64 fc_ena:1;
u64 fc_up_crossing:1;
u64 reserved_3:1;
u64 fc_hyst_bits:4;
u64 reserved_8_63:56;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_done_wait
*
* This register specifies the per-queue interrupt coalescing settings.
*/
union otx2_cptx_lf_done_wait {
u64 u;
struct otx2_cptx_lf_done_wait_s {
u64 num_wait:20;
u64 reserved_20_31:12;
u64 time_wait:16;
u64 reserved_48_63:16;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_done
*
* This register contain the per-queue instruction done count.
*/
union otx2_cptx_lf_done {
u64 u;
struct otx2_cptx_lf_done_s {
u64 done:20;
u64 reserved_20_63:44;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_inprog
*
* These registers contain the per-queue instruction in flight registers.
*
*/
union otx2_cptx_lf_inprog {
u64 u;
struct otx2_cptx_lf_inprog_s {
u64 inflight:9;
u64 reserved_9_15:7;
u64 eena:1;
u64 grp_drp:1;
u64 reserved_18_30:13;
u64 grb_partial:1;
u64 grb_cnt:8;
u64 gwb_cnt:8;
u64 reserved_48_63:16;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_q_base
*
* CPT initializes these CSR fields to these values on any CPT_LF_Q_BASE write:
* _ CPT_LF_Q_INST_PTR[XQ_XOR]=0.
* _ CPT_LF_Q_INST_PTR[NQ_PTR]=2.
* _ CPT_LF_Q_INST_PTR[DQ_PTR]=2.
* _ CPT_LF_Q_GRP_PTR[XQ_XOR]=0.
* _ CPT_LF_Q_GRP_PTR[NQ_PTR]=1.
* _ CPT_LF_Q_GRP_PTR[DQ_PTR]=1.
*/
union otx2_cptx_lf_q_base {
u64 u;
struct otx2_cptx_lf_q_base_s {
u64 fault:1;
u64 reserved_1_6:6;
u64 addr:46;
u64 reserved_53_63:11;
} s;
};
/*
* RVU_PFVF_BAR2 - cpt_lf_q_size
*
* CPT initializes these CSR fields to these values on any CPT_LF_Q_SIZE write:
* _ CPT_LF_Q_INST_PTR[XQ_XOR]=0.
* _ CPT_LF_Q_INST_PTR[NQ_PTR]=2.
* _ CPT_LF_Q_INST_PTR[DQ_PTR]=2.
* _ CPT_LF_Q_GRP_PTR[XQ_XOR]=0.
* _ CPT_LF_Q_GRP_PTR[NQ_PTR]=1.
* _ CPT_LF_Q_GRP_PTR[DQ_PTR]=1.
*/
union otx2_cptx_lf_q_size {
u64 u;
struct otx2_cptx_lf_q_size_s {
u64 size_div40:15;
u64 reserved_15_63:49;
} s;
};
/*
* RVU_PF_BAR0 - cpt_af_lf_ctl
*
* This register configures queues. This register should be written only
* when the queue is execution-quiescent (see CPT_LF_INPROG[INFLIGHT]).
*/
union otx2_cptx_af_lf_ctrl {
u64 u;
struct otx2_cptx_af_lf_ctrl_s {
u64 pri:1;
u64 reserved_1_8:8;
u64 pf_func_inst:1;
u64 cont_err:1;
u64 reserved_11_15:5;
u64 nixtx_en:1;
u64 ctx_ilen:3;
u64 reserved_17_47:28;
u64 grp:8;
u64 reserved_56_63:8;
} s;
};
#endif /* __OTX2_CPT_HW_TYPES_H */
@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cpt_common.h"
#include "otx2_cptlf.h"
int otx2_cpt_send_mbox_msg(struct otx2_mbox *mbox, struct pci_dev *pdev)
{
int ret;
otx2_mbox_msg_send(mbox, 0);
ret = otx2_mbox_wait_for_rsp(mbox, 0);
if (ret == -EIO) {
dev_err(&pdev->dev, "RVU MBOX timeout.\n");
return ret;
} else if (ret) {
dev_err(&pdev->dev, "RVU MBOX error: %d.\n", ret);
return -EFAULT;
}
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_send_mbox_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_send_ready_msg(struct otx2_mbox *mbox, struct pci_dev *pdev)
{
struct mbox_msghdr *req;
req = otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct ready_msg_rsp));
if (req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->id = MBOX_MSG_READY;
req->sig = OTX2_MBOX_REQ_SIG;
req->pcifunc = 0;
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_send_ready_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_send_af_reg_requests(struct otx2_mbox *mbox, struct pci_dev *pdev)
{
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_send_af_reg_requests, "CRYPTO_DEV_OCTEONTX2_CPT");
static int otx2_cpt_add_read_af_reg(struct otx2_mbox *mbox,
struct pci_dev *pdev, u64 reg,
u64 *val, int blkaddr)
{
struct cpt_rd_wr_reg_msg *reg_msg;
reg_msg = (struct cpt_rd_wr_reg_msg *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*reg_msg),
sizeof(*reg_msg));
if (reg_msg == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
reg_msg->hdr.id = MBOX_MSG_CPT_RD_WR_REGISTER;
reg_msg->hdr.sig = OTX2_MBOX_REQ_SIG;
reg_msg->hdr.pcifunc = 0;
reg_msg->is_write = 0;
reg_msg->reg_offset = reg;
reg_msg->ret_val = val;
reg_msg->blkaddr = blkaddr;
return 0;
}
int otx2_cpt_add_write_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 val, int blkaddr)
{
struct cpt_rd_wr_reg_msg *reg_msg;
reg_msg = (struct cpt_rd_wr_reg_msg *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*reg_msg),
sizeof(*reg_msg));
if (reg_msg == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
reg_msg->hdr.id = MBOX_MSG_CPT_RD_WR_REGISTER;
reg_msg->hdr.sig = OTX2_MBOX_REQ_SIG;
reg_msg->hdr.pcifunc = 0;
reg_msg->is_write = 1;
reg_msg->reg_offset = reg;
reg_msg->val = val;
reg_msg->blkaddr = blkaddr;
return 0;
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_add_write_af_reg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_read_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 *val, int blkaddr)
{
int ret;
ret = otx2_cpt_add_read_af_reg(mbox, pdev, reg, val, blkaddr);
if (ret)
return ret;
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_read_af_reg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_write_af_reg(struct otx2_mbox *mbox, struct pci_dev *pdev,
u64 reg, u64 val, int blkaddr)
{
int ret;
ret = otx2_cpt_add_write_af_reg(mbox, pdev, reg, val, blkaddr);
if (ret)
return ret;
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_write_af_reg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_attach_rscrs_msg(struct otx2_cptlfs_info *lfs)
{
struct otx2_mbox *mbox = lfs->mbox;
struct rsrc_attach *req;
int ret;
req = (struct rsrc_attach *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msg_rsp));
if (req == NULL) {
dev_err(&lfs->pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->hdr.id = MBOX_MSG_ATTACH_RESOURCES;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = 0;
req->cptlfs = lfs->lfs_num;
req->cpt_blkaddr = lfs->blkaddr;
req->modify = 1;
ret = otx2_cpt_send_mbox_msg(mbox, lfs->pdev);
if (ret)
return ret;
if (!lfs->are_lfs_attached)
ret = -EINVAL;
return ret;
}
int otx2_cpt_detach_rsrcs_msg(struct otx2_cptlfs_info *lfs)
{
struct otx2_mbox *mbox = lfs->mbox;
struct rsrc_detach *req;
int ret;
req = (struct rsrc_detach *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msg_rsp));
if (req == NULL) {
dev_err(&lfs->pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->hdr.id = MBOX_MSG_DETACH_RESOURCES;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = 0;
req->cptlfs = 1;
ret = otx2_cpt_send_mbox_msg(mbox, lfs->pdev);
if (ret)
return ret;
if (lfs->are_lfs_attached)
ret = -EINVAL;
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_detach_rsrcs_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_msix_offset_msg(struct otx2_cptlfs_info *lfs)
{
struct otx2_mbox *mbox = lfs->mbox;
struct pci_dev *pdev = lfs->pdev;
struct mbox_msghdr *req;
int ret, i;
req = otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msix_offset_rsp));
if (req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->id = MBOX_MSG_MSIX_OFFSET;
req->sig = OTX2_MBOX_REQ_SIG;
req->pcifunc = 0;
ret = otx2_cpt_send_mbox_msg(mbox, pdev);
if (ret)
return ret;
for (i = 0; i < lfs->lfs_num; i++) {
if (lfs->lf[i].msix_offset == MSIX_VECTOR_INVALID) {
dev_err(&pdev->dev,
"Invalid msix offset %d for LF %d\n",
lfs->lf[i].msix_offset, i);
return -EINVAL;
}
}
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_msix_offset_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_sync_mbox_msg(struct otx2_mbox *mbox)
{
int err;
if (!otx2_mbox_nonempty(mbox, 0))
return 0;
otx2_mbox_msg_send(mbox, 0);
err = otx2_mbox_wait_for_rsp(mbox, 0);
if (err)
return err;
return otx2_mbox_check_rsp_msgs(mbox, 0);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_sync_mbox_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_lf_reset_msg(struct otx2_cptlfs_info *lfs, int slot)
{
struct otx2_mbox *mbox = lfs->mbox;
struct pci_dev *pdev = lfs->pdev;
struct cpt_lf_rst_req *req;
int ret;
req = (struct cpt_lf_rst_req *)otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msg_rsp));
if (!req) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->hdr.id = MBOX_MSG_CPT_LF_RESET;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = 0;
req->slot = slot;
ret = otx2_cpt_send_mbox_msg(mbox, pdev);
if (ret)
return ret;
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_lf_reset_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cpt_lmtst_tbl_setup_msg(struct otx2_cptlfs_info *lfs)
{
struct otx2_mbox *mbox = lfs->mbox;
struct pci_dev *pdev = lfs->pdev;
struct lmtst_tbl_setup_req *req;
req = (struct lmtst_tbl_setup_req *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msg_rsp));
if (!req) {
dev_err(&pdev->dev, "RVU MBOX failed to alloc message.\n");
return -EFAULT;
}
req->hdr.id = MBOX_MSG_LMTST_TBL_SETUP;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = 0;
req->use_local_lmt_region = true;
req->lmt_iova = lfs->lmt_info.iova;
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
EXPORT_SYMBOL_NS_GPL(otx2_cpt_lmtst_tbl_setup_msg, "CRYPTO_DEV_OCTEONTX2_CPT");
@@ -0,0 +1,561 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPT_REQMGR_H
#define __OTX2_CPT_REQMGR_H
#include "otx2_cpt_common.h"
/* Completion code size and initial value */
#define OTX2_CPT_COMPLETION_CODE_SIZE 8
#define OTX2_CPT_COMPLETION_CODE_INIT OTX2_CPT_COMP_E_NOTDONE
/*
* Maximum total number of SG buffers is 100, we divide it equally
* between input and output
*/
#define OTX2_CPT_MAX_SG_IN_CNT 50
#define OTX2_CPT_MAX_SG_OUT_CNT 50
/* DMA mode direct or SG */
#define OTX2_CPT_DMA_MODE_DIRECT 0
#define OTX2_CPT_DMA_MODE_SG 1
/* Context source CPTR or DPTR */
#define OTX2_CPT_FROM_CPTR 0
#define OTX2_CPT_FROM_DPTR 1
#define OTX2_CPT_MAX_REQ_SIZE 65535
#define SG_COMPS_MAX 4
#define SGV2_COMPS_MAX 3
#define SG_COMP_3 3
#define SG_COMP_2 2
#define SG_COMP_1 1
#define OTX2_CPT_DPTR_RPTR_ALIGN 8
#define OTX2_CPT_RES_ADDR_ALIGN 32
union otx2_cpt_opcode {
u16 flags;
struct {
u8 major;
u8 minor;
} s;
};
struct otx2_cptvf_request {
u32 param1;
u32 param2;
u16 dlen;
union otx2_cpt_opcode opcode;
dma_addr_t cptr_dma;
void *cptr;
};
/*
* CPT_INST_S software command definitions
* Words EI (0-3)
*/
union otx2_cpt_iq_cmd_word0 {
u64 u;
struct {
__be16 opcode;
__be16 param1;
__be16 param2;
__be16 dlen;
} s;
};
union otx2_cpt_iq_cmd_word3 {
u64 u;
struct {
u64 cptr:61;
u64 grp:3;
} s;
};
struct otx2_cpt_iq_command {
union otx2_cpt_iq_cmd_word0 cmd;
u64 dptr;
u64 rptr;
union otx2_cpt_iq_cmd_word3 cptr;
};
struct otx2_cpt_pending_entry {
void *completion_addr; /* Completion address */
void *info;
/* Kernel async request callback */
void (*callback)(int status, void *arg1, void *arg2);
struct crypto_async_request *areq; /* Async request callback arg */
u8 resume_sender; /* Notify sender to resume sending requests */
u8 busy; /* Entry status (free/busy) */
};
struct otx2_cpt_pending_queue {
struct otx2_cpt_pending_entry *head; /* Head of the queue */
u32 front; /* Process work from here */
u32 rear; /* Append new work here */
u32 pending_count; /* Pending requests count */
u32 qlen; /* Queue length */
spinlock_t lock; /* Queue lock */
};
struct otx2_cpt_buf_ptr {
u8 *vptr;
dma_addr_t dma_addr;
u16 size;
};
union otx2_cpt_ctrl_info {
u32 flags;
struct {
#if defined(__BIG_ENDIAN_BITFIELD)
u32 reserved_6_31:26;
u32 grp:3; /* Group bits */
u32 dma_mode:2; /* DMA mode */
u32 se_req:1; /* To SE core */
#else
u32 se_req:1; /* To SE core */
u32 dma_mode:2; /* DMA mode */
u32 grp:3; /* Group bits */
u32 reserved_6_31:26;
#endif
} s;
};
struct otx2_cpt_req_info {
/* Kernel async request callback */
void (*callback)(int status, void *arg1, void *arg2);
struct crypto_async_request *areq; /* Async request callback arg */
struct otx2_cptvf_request req;/* Request information (core specific) */
union otx2_cpt_ctrl_info ctrl;/* User control information */
struct otx2_cpt_buf_ptr in[OTX2_CPT_MAX_SG_IN_CNT];
struct otx2_cpt_buf_ptr out[OTX2_CPT_MAX_SG_OUT_CNT];
u8 *iv_out; /* IV to send back */
u16 rlen; /* Output length */
u8 in_cnt; /* Number of input buffers */
u8 out_cnt; /* Number of output buffers */
u8 req_type; /* Type of request */
u8 is_enc; /* Is a request an encryption request */
u8 is_trunc_hmac;/* Is truncated hmac used */
};
struct otx2_cpt_inst_info {
struct otx2_cpt_pending_entry *pentry;
struct otx2_cpt_req_info *req;
struct pci_dev *pdev;
void *completion_addr;
u8 *out_buffer;
u8 *in_buffer;
dma_addr_t dptr_baddr;
dma_addr_t rptr_baddr;
dma_addr_t comp_baddr;
unsigned long time_in;
u32 dlen;
u32 dma_len;
u64 gthr_sz;
u64 sctr_sz;
u8 extra_time;
};
struct otx2_cpt_sglist_component {
__be16 len0;
__be16 len1;
__be16 len2;
__be16 len3;
__be64 ptr0;
__be64 ptr1;
__be64 ptr2;
__be64 ptr3;
};
struct cn10kb_cpt_sglist_component {
u16 len0;
u16 len1;
u16 len2;
u16 valid_segs;
u64 ptr0;
u64 ptr1;
u64 ptr2;
};
static inline void otx2_cpt_info_destroy(struct pci_dev *pdev,
struct otx2_cpt_inst_info *info)
{
struct otx2_cpt_req_info *req;
int i;
if (info->dptr_baddr)
dma_unmap_single(&pdev->dev, info->dptr_baddr,
info->dma_len, DMA_BIDIRECTIONAL);
if (info->req) {
req = info->req;
for (i = 0; i < req->out_cnt; i++) {
if (req->out[i].dma_addr)
dma_unmap_single(&pdev->dev,
req->out[i].dma_addr,
req->out[i].size,
DMA_BIDIRECTIONAL);
}
for (i = 0; i < req->in_cnt; i++) {
if (req->in[i].dma_addr)
dma_unmap_single(&pdev->dev,
req->in[i].dma_addr,
req->in[i].size,
DMA_BIDIRECTIONAL);
}
}
kfree(info);
}
static inline int setup_sgio_components(struct pci_dev *pdev,
struct otx2_cpt_buf_ptr *list,
int buf_count, u8 *buffer)
{
struct otx2_cpt_sglist_component *sg_ptr;
int components;
int i, j;
if (unlikely(!list)) {
dev_err(&pdev->dev, "Input list pointer is NULL\n");
return -EINVAL;
}
for (i = 0; i < buf_count; i++) {
if (unlikely(!list[i].vptr))
continue;
list[i].dma_addr = dma_map_single(&pdev->dev, list[i].vptr,
list[i].size,
DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(&pdev->dev, list[i].dma_addr))) {
dev_err(&pdev->dev, "Dma mapping failed\n");
goto sg_cleanup;
}
}
components = buf_count / SG_COMPS_MAX;
sg_ptr = (struct otx2_cpt_sglist_component *)buffer;
for (i = 0; i < components; i++) {
sg_ptr->len0 = cpu_to_be16(list[i * SG_COMPS_MAX + 0].size);
sg_ptr->len1 = cpu_to_be16(list[i * SG_COMPS_MAX + 1].size);
sg_ptr->len2 = cpu_to_be16(list[i * SG_COMPS_MAX + 2].size);
sg_ptr->len3 = cpu_to_be16(list[i * SG_COMPS_MAX + 3].size);
sg_ptr->ptr0 = cpu_to_be64(list[i * SG_COMPS_MAX + 0].dma_addr);
sg_ptr->ptr1 = cpu_to_be64(list[i * SG_COMPS_MAX + 1].dma_addr);
sg_ptr->ptr2 = cpu_to_be64(list[i * SG_COMPS_MAX + 2].dma_addr);
sg_ptr->ptr3 = cpu_to_be64(list[i * SG_COMPS_MAX + 3].dma_addr);
sg_ptr++;
}
components = buf_count % SG_COMPS_MAX;
switch (components) {
case SG_COMP_3:
sg_ptr->len2 = cpu_to_be16(list[i * SG_COMPS_MAX + 2].size);
sg_ptr->ptr2 = cpu_to_be64(list[i * SG_COMPS_MAX + 2].dma_addr);
fallthrough;
case SG_COMP_2:
sg_ptr->len1 = cpu_to_be16(list[i * SG_COMPS_MAX + 1].size);
sg_ptr->ptr1 = cpu_to_be64(list[i * SG_COMPS_MAX + 1].dma_addr);
fallthrough;
case SG_COMP_1:
sg_ptr->len0 = cpu_to_be16(list[i * SG_COMPS_MAX + 0].size);
sg_ptr->ptr0 = cpu_to_be64(list[i * SG_COMPS_MAX + 0].dma_addr);
break;
default:
break;
}
return 0;
sg_cleanup:
for (j = 0; j < i; j++) {
if (list[j].dma_addr) {
dma_unmap_single(&pdev->dev, list[j].dma_addr,
list[j].size, DMA_BIDIRECTIONAL);
}
list[j].dma_addr = 0;
}
return -EIO;
}
static inline int sgv2io_components_setup(struct pci_dev *pdev,
struct otx2_cpt_buf_ptr *list,
int buf_count, u8 *buffer)
{
struct cn10kb_cpt_sglist_component *sg_ptr;
int components;
int i, j;
if (unlikely(!list)) {
dev_err(&pdev->dev, "Input list pointer is NULL\n");
return -EFAULT;
}
for (i = 0; i < buf_count; i++) {
if (unlikely(!list[i].vptr))
continue;
list[i].dma_addr = dma_map_single(&pdev->dev, list[i].vptr,
list[i].size,
DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(&pdev->dev, list[i].dma_addr))) {
dev_err(&pdev->dev, "Dma mapping failed\n");
goto sg_cleanup;
}
}
components = buf_count / SGV2_COMPS_MAX;
sg_ptr = (struct cn10kb_cpt_sglist_component *)buffer;
for (i = 0; i < components; i++) {
sg_ptr->len0 = list[i * SGV2_COMPS_MAX + 0].size;
sg_ptr->len1 = list[i * SGV2_COMPS_MAX + 1].size;
sg_ptr->len2 = list[i * SGV2_COMPS_MAX + 2].size;
sg_ptr->ptr0 = list[i * SGV2_COMPS_MAX + 0].dma_addr;
sg_ptr->ptr1 = list[i * SGV2_COMPS_MAX + 1].dma_addr;
sg_ptr->ptr2 = list[i * SGV2_COMPS_MAX + 2].dma_addr;
sg_ptr->valid_segs = SGV2_COMPS_MAX;
sg_ptr++;
}
components = buf_count % SGV2_COMPS_MAX;
sg_ptr->valid_segs = components;
switch (components) {
case SG_COMP_2:
sg_ptr->len1 = list[i * SGV2_COMPS_MAX + 1].size;
sg_ptr->ptr1 = list[i * SGV2_COMPS_MAX + 1].dma_addr;
fallthrough;
case SG_COMP_1:
sg_ptr->len0 = list[i * SGV2_COMPS_MAX + 0].size;
sg_ptr->ptr0 = list[i * SGV2_COMPS_MAX + 0].dma_addr;
break;
default:
break;
}
return 0;
sg_cleanup:
for (j = 0; j < i; j++) {
if (list[j].dma_addr) {
dma_unmap_single(&pdev->dev, list[j].dma_addr,
list[j].size, DMA_BIDIRECTIONAL);
}
list[j].dma_addr = 0;
}
return -EIO;
}
static inline struct otx2_cpt_inst_info *
cn10k_sgv2_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
gfp_t gfp)
{
u32 dlen = 0, g_len, s_len, sg_len, info_len;
struct otx2_cpt_inst_info *info;
u32 total_mem_len;
int i;
/* Allocate memory to meet below alignment requirement:
* ------------------------------------
* | struct otx2_cpt_inst_info |
* | (No alignment required) |
* | --------------------------------|
* | | padding for ARCH_DMA_MINALIGN |
* | | alignment |
* |------------------------------------|
* | SG List Gather/Input memory |
* | Length = multiple of 32Bytes |
* | Alignment = 8Byte |
* |---------------------------------- |
* | SG List Scatter/Output memory |
* | Length = multiple of 32Bytes |
* | Alignment = 8Byte |
* | -------------------------------|
* | | padding for 32B alignment |
* |------------------------------------|
* | Result response memory |
* | Alignment = 32Byte |
* ------------------------------------
*/
info_len = sizeof(*info);
g_len = ((req->in_cnt + 2) / 3) *
sizeof(struct cn10kb_cpt_sglist_component);
s_len = ((req->out_cnt + 2) / 3) *
sizeof(struct cn10kb_cpt_sglist_component);
sg_len = g_len + s_len;
/* Allocate extra memory for SG and response address alignment */
total_mem_len = ALIGN(info_len, OTX2_CPT_DPTR_RPTR_ALIGN);
total_mem_len += (ARCH_DMA_MINALIGN - 1) &
~(OTX2_CPT_DPTR_RPTR_ALIGN - 1);
total_mem_len += ALIGN(sg_len, OTX2_CPT_RES_ADDR_ALIGN);
total_mem_len += sizeof(union otx2_cpt_res_s);
info = kzalloc(total_mem_len, gfp);
if (unlikely(!info))
return NULL;
for (i = 0; i < req->in_cnt; i++)
dlen += req->in[i].size;
info->dlen = dlen;
info->in_buffer = PTR_ALIGN((u8 *)info + info_len, ARCH_DMA_MINALIGN);
info->out_buffer = info->in_buffer + g_len;
info->gthr_sz = req->in_cnt;
info->sctr_sz = req->out_cnt;
/* Setup gather (input) components */
if (sgv2io_components_setup(pdev, req->in, req->in_cnt,
info->in_buffer)) {
dev_err(&pdev->dev, "Failed to setup gather list\n");
goto destroy_info;
}
if (sgv2io_components_setup(pdev, req->out, req->out_cnt,
info->out_buffer)) {
dev_err(&pdev->dev, "Failed to setup scatter list\n");
goto destroy_info;
}
info->dma_len = total_mem_len - info_len;
info->dptr_baddr = dma_map_single(&pdev->dev, info->in_buffer,
info->dma_len, DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(&pdev->dev, info->dptr_baddr))) {
dev_err(&pdev->dev, "DMA Mapping failed for cpt req\n");
goto destroy_info;
}
info->rptr_baddr = info->dptr_baddr + g_len;
/*
* Get buffer for union otx2_cpt_res_s response
* structure and its physical address
*/
info->completion_addr = PTR_ALIGN((info->in_buffer + sg_len),
OTX2_CPT_RES_ADDR_ALIGN);
info->comp_baddr = ALIGN((info->dptr_baddr + sg_len),
OTX2_CPT_RES_ADDR_ALIGN);
return info;
destroy_info:
otx2_cpt_info_destroy(pdev, info);
return NULL;
}
/* SG list header size in bytes */
#define SG_LIST_HDR_SIZE 8
static inline struct otx2_cpt_inst_info *
otx2_sg_info_create(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
gfp_t gfp)
{
struct otx2_cpt_inst_info *info;
u32 dlen, info_len;
u16 g_len, s_len;
u32 total_mem_len;
if (unlikely(req->in_cnt > OTX2_CPT_MAX_SG_IN_CNT ||
req->out_cnt > OTX2_CPT_MAX_SG_OUT_CNT)) {
dev_err(&pdev->dev, "Error too many sg components\n");
return NULL;
}
/* Allocate memory to meet below alignment requirement:
* ------------------------------------
* | struct otx2_cpt_inst_info |
* | (No alignment required) |
* | --------------------------------|
* | | padding for ARCH_DMA_MINALIGN |
* | | alignment |
* |------------------------------------|
* | SG List Header of 8 Byte |
* |------------------------------------|
* | SG List Gather/Input memory |
* | Length = multiple of 32Bytes |
* | Alignment = 8Byte |
* |---------------------------------- |
* | SG List Scatter/Output memory |
* | Length = multiple of 32Bytes |
* | Alignment = 8Byte |
* | -------------------------------|
* | | padding for 32B alignment |
* |------------------------------------|
* | Result response memory |
* | Alignment = 32Byte |
* ------------------------------------
*/
info_len = sizeof(*info);
g_len = ((req->in_cnt + 3) / 4) *
sizeof(struct otx2_cpt_sglist_component);
s_len = ((req->out_cnt + 3) / 4) *
sizeof(struct otx2_cpt_sglist_component);
dlen = g_len + s_len + SG_LIST_HDR_SIZE;
/* Allocate extra memory for SG and response address alignment */
total_mem_len = ALIGN(info_len, OTX2_CPT_DPTR_RPTR_ALIGN);
total_mem_len += (ARCH_DMA_MINALIGN - 1) &
~(OTX2_CPT_DPTR_RPTR_ALIGN - 1);
total_mem_len += ALIGN(dlen, OTX2_CPT_RES_ADDR_ALIGN);
total_mem_len += sizeof(union otx2_cpt_res_s);
info = kzalloc(total_mem_len, gfp);
if (unlikely(!info))
return NULL;
info->dlen = dlen;
info->in_buffer = PTR_ALIGN((u8 *)info + info_len, ARCH_DMA_MINALIGN);
info->out_buffer = info->in_buffer + SG_LIST_HDR_SIZE + g_len;
((u16 *)info->in_buffer)[0] = req->out_cnt;
((u16 *)info->in_buffer)[1] = req->in_cnt;
((u16 *)info->in_buffer)[2] = 0;
((u16 *)info->in_buffer)[3] = 0;
cpu_to_be64s((u64 *)info->in_buffer);
/* Setup gather (input) components */
if (setup_sgio_components(pdev, req->in, req->in_cnt,
&info->in_buffer[8])) {
dev_err(&pdev->dev, "Failed to setup gather list\n");
goto destroy_info;
}
if (setup_sgio_components(pdev, req->out, req->out_cnt,
info->out_buffer)) {
dev_err(&pdev->dev, "Failed to setup scatter list\n");
goto destroy_info;
}
info->dma_len = total_mem_len - info_len;
info->dptr_baddr = dma_map_single(&pdev->dev, info->in_buffer,
info->dma_len, DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(&pdev->dev, info->dptr_baddr))) {
dev_err(&pdev->dev, "DMA Mapping failed for cpt req\n");
goto destroy_info;
}
/*
* Get buffer for union otx2_cpt_res_s response
* structure and its physical address
*/
info->completion_addr = PTR_ALIGN((info->in_buffer + dlen),
OTX2_CPT_RES_ADDR_ALIGN);
info->comp_baddr = ALIGN((info->dptr_baddr + dlen),
OTX2_CPT_RES_ADDR_ALIGN);
return info;
destroy_info:
otx2_cpt_info_destroy(pdev, info);
return NULL;
}
struct otx2_cptlf_wqe;
int otx2_cpt_do_request(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
int cpu_num);
void otx2_cpt_post_process(struct otx2_cptlf_wqe *wqe);
int otx2_cpt_get_eng_grp_num(struct pci_dev *pdev,
enum otx2_cpt_eng_type);
#endif /* __OTX2_CPT_REQMGR_H */
@@ -0,0 +1,498 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cpt_common.h"
#include "otx2_cptlf.h"
#include "rvu_reg.h"
#define CPT_TIMER_HOLD 0x03F
#define CPT_COUNT_HOLD 32
static void cptlf_do_set_done_time_wait(struct otx2_cptlf_info *lf,
int time_wait)
{
union otx2_cptx_lf_done_wait done_wait;
done_wait.u = otx2_cpt_read64(lf->lfs->reg_base, lf->lfs->blkaddr,
lf->slot, OTX2_CPT_LF_DONE_WAIT);
done_wait.s.time_wait = time_wait;
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_DONE_WAIT, done_wait.u);
}
static void cptlf_do_set_done_num_wait(struct otx2_cptlf_info *lf, int num_wait)
{
union otx2_cptx_lf_done_wait done_wait;
done_wait.u = otx2_cpt_read64(lf->lfs->reg_base, lf->lfs->blkaddr,
lf->slot, OTX2_CPT_LF_DONE_WAIT);
done_wait.s.num_wait = num_wait;
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_DONE_WAIT, done_wait.u);
}
static void cptlf_set_done_time_wait(struct otx2_cptlfs_info *lfs,
int time_wait)
{
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++)
cptlf_do_set_done_time_wait(&lfs->lf[slot], time_wait);
}
static void cptlf_set_done_num_wait(struct otx2_cptlfs_info *lfs, int num_wait)
{
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++)
cptlf_do_set_done_num_wait(&lfs->lf[slot], num_wait);
}
static int cptlf_set_pri(struct otx2_cptlf_info *lf, int pri)
{
struct otx2_cptlfs_info *lfs = lf->lfs;
union otx2_cptx_af_lf_ctrl lf_ctrl;
int ret;
ret = otx2_cpt_read_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
&lf_ctrl.u, lfs->blkaddr);
if (ret)
return ret;
lf_ctrl.s.pri = pri ? 1 : 0;
ret = otx2_cpt_write_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
lf_ctrl.u, lfs->blkaddr);
return ret;
}
static int cptlf_set_eng_grps_mask(struct otx2_cptlf_info *lf,
int eng_grps_mask)
{
struct otx2_cptlfs_info *lfs = lf->lfs;
union otx2_cptx_af_lf_ctrl lf_ctrl;
int ret;
ret = otx2_cpt_read_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
&lf_ctrl.u, lfs->blkaddr);
if (ret)
return ret;
lf_ctrl.s.grp = eng_grps_mask;
ret = otx2_cpt_write_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
lf_ctrl.u, lfs->blkaddr);
return ret;
}
static int cptlf_set_grp_and_pri(struct otx2_cptlfs_info *lfs,
int eng_grp_mask, int pri)
{
int slot, ret = 0;
for (slot = 0; slot < lfs->lfs_num; slot++) {
ret = cptlf_set_pri(&lfs->lf[slot], pri);
if (ret)
return ret;
ret = cptlf_set_eng_grps_mask(&lfs->lf[slot], eng_grp_mask);
if (ret)
return ret;
}
return ret;
}
static int cptlf_set_ctx_ilen(struct otx2_cptlfs_info *lfs, int ctx_ilen)
{
union otx2_cptx_af_lf_ctrl lf_ctrl;
struct otx2_cptlf_info *lf;
int slot, ret = 0;
for (slot = 0; slot < lfs->lfs_num; slot++) {
lf = &lfs->lf[slot];
ret = otx2_cpt_read_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
&lf_ctrl.u, lfs->blkaddr);
if (ret)
return ret;
lf_ctrl.s.ctx_ilen = ctx_ilen;
ret = otx2_cpt_write_af_reg(lfs->mbox, lfs->pdev,
CPT_AF_LFX_CTL(lf->slot),
lf_ctrl.u, lfs->blkaddr);
if (ret)
return ret;
}
return ret;
}
static void cptlf_hw_init(struct otx2_cptlfs_info *lfs)
{
/* Disable instruction queues */
otx2_cptlf_disable_iqueues(lfs);
/* Set instruction queues base addresses */
otx2_cptlf_set_iqueues_base_addr(lfs);
/* Set instruction queues sizes */
otx2_cptlf_set_iqueues_size(lfs);
/* Set done interrupts time wait */
cptlf_set_done_time_wait(lfs, CPT_TIMER_HOLD);
/* Set done interrupts num wait */
cptlf_set_done_num_wait(lfs, CPT_COUNT_HOLD);
/* Enable instruction queues */
otx2_cptlf_enable_iqueues(lfs);
}
static void cptlf_hw_cleanup(struct otx2_cptlfs_info *lfs)
{
/* Disable instruction queues */
otx2_cptlf_disable_iqueues(lfs);
}
static void cptlf_set_misc_intrs(struct otx2_cptlfs_info *lfs, u8 enable)
{
union otx2_cptx_lf_misc_int_ena_w1s irq_misc = { .u = 0x0 };
u64 reg = enable ? OTX2_CPT_LF_MISC_INT_ENA_W1S :
OTX2_CPT_LF_MISC_INT_ENA_W1C;
int slot;
irq_misc.s.fault = 0x1;
irq_misc.s.hwerr = 0x1;
irq_misc.s.irde = 0x1;
irq_misc.s.nqerr = 0x1;
irq_misc.s.nwrp = 0x1;
for (slot = 0; slot < lfs->lfs_num; slot++)
otx2_cpt_write64(lfs->reg_base, lfs->blkaddr, slot, reg,
irq_misc.u);
}
static void cptlf_set_done_intrs(struct otx2_cptlfs_info *lfs, u8 enable)
{
u64 reg = enable ? OTX2_CPT_LF_DONE_INT_ENA_W1S :
OTX2_CPT_LF_DONE_INT_ENA_W1C;
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++)
otx2_cpt_write64(lfs->reg_base, lfs->blkaddr, slot, reg, 0x1);
}
static inline int cptlf_read_done_cnt(struct otx2_cptlf_info *lf)
{
union otx2_cptx_lf_done irq_cnt;
irq_cnt.u = otx2_cpt_read64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_DONE);
return irq_cnt.s.done;
}
static irqreturn_t cptlf_misc_intr_handler(int __always_unused irq, void *arg)
{
union otx2_cptx_lf_misc_int irq_misc, irq_misc_ack;
struct otx2_cptlf_info *lf = arg;
struct device *dev;
dev = &lf->lfs->pdev->dev;
irq_misc.u = otx2_cpt_read64(lf->lfs->reg_base, lf->lfs->blkaddr,
lf->slot, OTX2_CPT_LF_MISC_INT);
irq_misc_ack.u = 0x0;
if (irq_misc.s.fault) {
dev_err(dev, "Memory error detected while executing CPT_INST_S, LF %d.\n",
lf->slot);
irq_misc_ack.s.fault = 0x1;
} else if (irq_misc.s.hwerr) {
dev_err(dev, "HW error from an engine executing CPT_INST_S, LF %d.",
lf->slot);
irq_misc_ack.s.hwerr = 0x1;
} else if (irq_misc.s.nwrp) {
dev_err(dev, "SMMU fault while writing CPT_RES_S to CPT_INST_S[RES_ADDR], LF %d.\n",
lf->slot);
irq_misc_ack.s.nwrp = 0x1;
} else if (irq_misc.s.irde) {
dev_err(dev, "Memory error when accessing instruction memory queue CPT_LF_Q_BASE[ADDR].\n");
irq_misc_ack.s.irde = 0x1;
} else if (irq_misc.s.nqerr) {
dev_err(dev, "Error enqueuing an instruction received at CPT_LF_NQ.\n");
irq_misc_ack.s.nqerr = 0x1;
} else {
dev_err(dev, "Unhandled interrupt in CPT LF %d\n", lf->slot);
return IRQ_NONE;
}
/* Acknowledge interrupts */
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_MISC_INT, irq_misc_ack.u);
return IRQ_HANDLED;
}
static irqreturn_t cptlf_done_intr_handler(int irq, void *arg)
{
union otx2_cptx_lf_done_wait done_wait;
struct otx2_cptlf_info *lf = arg;
int irq_cnt;
/* Read the number of completed requests */
irq_cnt = cptlf_read_done_cnt(lf);
if (irq_cnt) {
done_wait.u = otx2_cpt_read64(lf->lfs->reg_base, lf->lfs->blkaddr,
lf->slot, OTX2_CPT_LF_DONE_WAIT);
/* Acknowledge the number of completed requests */
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_DONE_ACK, irq_cnt);
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_DONE_WAIT, done_wait.u);
if (unlikely(!lf->wqe)) {
dev_err(&lf->lfs->pdev->dev, "No work for LF %d\n",
lf->slot);
return IRQ_NONE;
}
/* Schedule processing of completed requests */
tasklet_hi_schedule(&lf->wqe->work);
}
return IRQ_HANDLED;
}
void otx2_cptlf_unregister_misc_interrupts(struct otx2_cptlfs_info *lfs)
{
int i, irq_offs, vector;
irq_offs = OTX2_CPT_LF_INT_VEC_E_MISC;
for (i = 0; i < lfs->lfs_num; i++) {
if (!lfs->lf[i].is_irq_reg[irq_offs])
continue;
vector = pci_irq_vector(lfs->pdev,
lfs->lf[i].msix_offset + irq_offs);
free_irq(vector, &lfs->lf[i]);
lfs->lf[i].is_irq_reg[irq_offs] = false;
}
cptlf_set_misc_intrs(lfs, false);
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_unregister_misc_interrupts, "CRYPTO_DEV_OCTEONTX2_CPT");
void otx2_cptlf_unregister_done_interrupts(struct otx2_cptlfs_info *lfs)
{
int i, irq_offs, vector;
irq_offs = OTX2_CPT_LF_INT_VEC_E_DONE;
for (i = 0; i < lfs->lfs_num; i++) {
if (!lfs->lf[i].is_irq_reg[irq_offs])
continue;
vector = pci_irq_vector(lfs->pdev,
lfs->lf[i].msix_offset + irq_offs);
free_irq(vector, &lfs->lf[i]);
lfs->lf[i].is_irq_reg[irq_offs] = false;
}
cptlf_set_done_intrs(lfs, false);
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_unregister_done_interrupts, "CRYPTO_DEV_OCTEONTX2_CPT");
static int cptlf_do_register_interrrupts(struct otx2_cptlfs_info *lfs,
int lf_num, int irq_offset,
irq_handler_t handler)
{
int ret, vector;
vector = pci_irq_vector(lfs->pdev, lfs->lf[lf_num].msix_offset +
irq_offset);
ret = request_irq(vector, handler, 0,
lfs->lf[lf_num].irq_name[irq_offset],
&lfs->lf[lf_num]);
if (ret)
return ret;
lfs->lf[lf_num].is_irq_reg[irq_offset] = true;
return ret;
}
int otx2_cptlf_register_misc_interrupts(struct otx2_cptlfs_info *lfs)
{
bool is_cpt1 = (lfs->blkaddr == BLKADDR_CPT1);
int irq_offs, ret, i;
irq_offs = OTX2_CPT_LF_INT_VEC_E_MISC;
for (i = 0; i < lfs->lfs_num; i++) {
snprintf(lfs->lf[i].irq_name[irq_offs], 32, "CPT%dLF Misc%d",
is_cpt1, i);
ret = cptlf_do_register_interrrupts(lfs, i, irq_offs,
cptlf_misc_intr_handler);
if (ret)
goto free_irq;
}
cptlf_set_misc_intrs(lfs, true);
return 0;
free_irq:
otx2_cptlf_unregister_misc_interrupts(lfs);
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_register_misc_interrupts, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cptlf_register_done_interrupts(struct otx2_cptlfs_info *lfs)
{
bool is_cpt1 = (lfs->blkaddr == BLKADDR_CPT1);
int irq_offs, ret, i;
irq_offs = OTX2_CPT_LF_INT_VEC_E_DONE;
for (i = 0; i < lfs->lfs_num; i++) {
snprintf(lfs->lf[i].irq_name[irq_offs], 32,
"OTX2_CPT%dLF Done%d", is_cpt1, i);
ret = cptlf_do_register_interrrupts(lfs, i, irq_offs,
cptlf_done_intr_handler);
if (ret)
goto free_irq;
}
cptlf_set_done_intrs(lfs, true);
return 0;
free_irq:
otx2_cptlf_unregister_done_interrupts(lfs);
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_register_done_interrupts, "CRYPTO_DEV_OCTEONTX2_CPT");
void otx2_cptlf_free_irqs_affinity(struct otx2_cptlfs_info *lfs)
{
int slot, offs;
for (slot = 0; slot < lfs->lfs_num; slot++) {
for (offs = 0; offs < OTX2_CPT_LF_MSIX_VECTORS; offs++)
irq_set_affinity_hint(pci_irq_vector(lfs->pdev,
lfs->lf[slot].msix_offset +
offs), NULL);
free_cpumask_var(lfs->lf[slot].affinity_mask);
}
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_free_irqs_affinity, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cptlf_set_irqs_affinity(struct otx2_cptlfs_info *lfs)
{
struct otx2_cptlf_info *lf = lfs->lf;
int slot, offs, ret;
for (slot = 0; slot < lfs->lfs_num; slot++) {
if (!zalloc_cpumask_var(&lf[slot].affinity_mask, GFP_KERNEL)) {
dev_err(&lfs->pdev->dev,
"cpumask allocation failed for LF %d", slot);
ret = -ENOMEM;
goto free_affinity_mask;
}
cpumask_set_cpu(cpumask_local_spread(slot,
dev_to_node(&lfs->pdev->dev)),
lf[slot].affinity_mask);
for (offs = 0; offs < OTX2_CPT_LF_MSIX_VECTORS; offs++) {
ret = irq_set_affinity_hint(pci_irq_vector(lfs->pdev,
lf[slot].msix_offset + offs),
lf[slot].affinity_mask);
if (ret)
goto free_affinity_mask;
}
}
return 0;
free_affinity_mask:
otx2_cptlf_free_irqs_affinity(lfs);
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_set_irqs_affinity, "CRYPTO_DEV_OCTEONTX2_CPT");
int otx2_cptlf_init(struct otx2_cptlfs_info *lfs, u8 eng_grp_mask, int pri,
int lfs_num)
{
int slot, ret;
if (!lfs->pdev || !lfs->reg_base)
return -EINVAL;
lfs->lfs_num = lfs_num;
for (slot = 0; slot < lfs->lfs_num; slot++) {
lfs->lf[slot].lfs = lfs;
lfs->lf[slot].slot = slot;
if (!lfs->lmt_info.base)
lfs->lf[slot].lmtline = lfs->reg_base +
OTX2_CPT_RVU_FUNC_ADDR_S(BLKADDR_LMT, slot,
OTX2_CPT_LMT_LF_LMTLINEX(0));
lfs->lf[slot].ioreg = lfs->reg_base +
OTX2_CPT_RVU_FUNC_ADDR_S(lfs->blkaddr, slot,
OTX2_CPT_LF_NQX(0));
}
/* Send request to attach LFs */
ret = otx2_cpt_attach_rscrs_msg(lfs);
if (ret)
goto clear_lfs_num;
ret = otx2_cpt_alloc_instruction_queues(lfs);
if (ret) {
dev_err(&lfs->pdev->dev,
"Allocating instruction queues failed\n");
goto detach_rsrcs;
}
cptlf_hw_init(lfs);
/*
* Allow each LF to execute requests destined to any of 8 engine
* groups and set queue priority of each LF to high
*/
ret = cptlf_set_grp_and_pri(lfs, eng_grp_mask, pri);
if (ret)
goto free_iq;
if (lfs->ctx_ilen_ovrd) {
ret = cptlf_set_ctx_ilen(lfs, lfs->ctx_ilen);
if (ret)
goto free_iq;
}
return 0;
free_iq:
cptlf_hw_cleanup(lfs);
otx2_cpt_free_instruction_queues(lfs);
detach_rsrcs:
otx2_cpt_detach_rsrcs_msg(lfs);
clear_lfs_num:
lfs->lfs_num = 0;
return ret;
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_init, "CRYPTO_DEV_OCTEONTX2_CPT");
void otx2_cptlf_shutdown(struct otx2_cptlfs_info *lfs)
{
/* Cleanup LFs hardware side */
cptlf_hw_cleanup(lfs);
/* Free instruction queues */
otx2_cpt_free_instruction_queues(lfs);
/* Send request to detach LFs */
otx2_cpt_detach_rsrcs_msg(lfs);
lfs->lfs_num = 0;
}
EXPORT_SYMBOL_NS_GPL(otx2_cptlf_shutdown, "CRYPTO_DEV_OCTEONTX2_CPT");
MODULE_AUTHOR("Marvell");
MODULE_DESCRIPTION("Marvell RVU CPT Common module");
MODULE_LICENSE("GPL");
@@ -0,0 +1,448 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPTLF_H
#define __OTX2_CPTLF_H
#include <linux/soc/marvell/octeontx2/asm.h>
#include <linux/bitfield.h>
#include <mbox.h>
#include <rvu.h>
#include "otx2_cpt_common.h"
#include "otx2_cpt_reqmgr.h"
/*
* CPT instruction and pending queues user requested length in CPT_INST_S msgs
*/
#define OTX2_CPT_USER_REQUESTED_QLEN_MSGS 8200
/*
* CPT instruction queue size passed to HW is in units of 40*CPT_INST_S
* messages.
*/
#define OTX2_CPT_SIZE_DIV40 (OTX2_CPT_USER_REQUESTED_QLEN_MSGS/40)
/*
* CPT instruction and pending queues length in CPT_INST_S messages
*/
#define OTX2_CPT_INST_QLEN_MSGS ((OTX2_CPT_SIZE_DIV40 - 1) * 40)
/*
* LDWB is getting incorrectly used when IQB_LDWB = 1 and CPT instruction
* queue has less than 320 free entries. So, increase HW instruction queue
* size by 320 and give 320 entries less for SW/NIX RX as a workaround.
*/
#define OTX2_CPT_INST_QLEN_EXTRA_BYTES (320 * OTX2_CPT_INST_SIZE)
#define OTX2_CPT_EXTRA_SIZE_DIV40 (320/40)
/* CPT instruction queue length in bytes */
#define OTX2_CPT_INST_QLEN_BYTES \
((OTX2_CPT_SIZE_DIV40 * 40 * OTX2_CPT_INST_SIZE) + \
OTX2_CPT_INST_QLEN_EXTRA_BYTES)
/* CPT instruction group queue length in bytes */
#define OTX2_CPT_INST_GRP_QLEN_BYTES \
((OTX2_CPT_SIZE_DIV40 + OTX2_CPT_EXTRA_SIZE_DIV40) * 16)
/* CPT FC length in bytes */
#define OTX2_CPT_Q_FC_LEN 128
/* CPT instruction queue alignment */
#define OTX2_CPT_INST_Q_ALIGNMENT 128
/* Mask which selects all engine groups */
#define OTX2_CPT_ALL_ENG_GRPS_MASK 0xFF
/* Maximum LFs supported in OcteonTX2 for CPT */
#define OTX2_CPT_MAX_LFS_NUM 64
/* Queue priority */
#define OTX2_CPT_QUEUE_HI_PRIO 0x1
#define OTX2_CPT_QUEUE_LOW_PRIO 0x0
enum otx2_cptlf_state {
OTX2_CPTLF_IN_RESET,
OTX2_CPTLF_STARTED,
};
struct otx2_cpt_inst_queue {
u8 *vaddr;
u8 *real_vaddr;
dma_addr_t dma_addr;
dma_addr_t real_dma_addr;
u32 size;
};
struct otx2_cptlfs_info;
struct otx2_cptlf_wqe {
struct tasklet_struct work;
struct otx2_cptlfs_info *lfs;
u8 lf_num;
};
struct otx2_cptlf_info {
struct otx2_cptlfs_info *lfs; /* Ptr to cptlfs_info struct */
void __iomem *lmtline; /* Address of LMTLINE */
void __iomem *ioreg; /* LMTLINE send register */
int msix_offset; /* MSI-X interrupts offset */
cpumask_var_t affinity_mask; /* IRQs affinity mask */
u8 irq_name[OTX2_CPT_LF_MSIX_VECTORS][32];/* Interrupts name */
u8 is_irq_reg[OTX2_CPT_LF_MSIX_VECTORS]; /* Is interrupt registered */
u8 slot; /* Slot number of this LF */
struct otx2_cpt_inst_queue iqueue;/* Instruction queue */
struct otx2_cpt_pending_queue pqueue; /* Pending queue */
struct otx2_cptlf_wqe *wqe; /* Tasklet work info */
};
struct cpt_hw_ops {
void (*send_cmd)(union otx2_cpt_inst_s *cptinst, u32 insts_num,
struct otx2_cptlf_info *lf);
u8 (*cpt_get_compcode)(union otx2_cpt_res_s *result);
u8 (*cpt_get_uc_compcode)(union otx2_cpt_res_s *result);
struct otx2_cpt_inst_info *
(*cpt_sg_info_create)(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
gfp_t gfp);
};
#define LMTLINE_SIZE 128
#define LMTLINE_ALIGN 128
struct otx2_lmt_info {
void *base;
dma_addr_t iova;
u32 size;
u8 align;
};
struct otx2_cptlfs_info {
/* Registers start address of VF/PF LFs are attached to */
void __iomem *reg_base;
struct otx2_lmt_info lmt_info;
struct pci_dev *pdev; /* Device LFs are attached to */
struct otx2_cptlf_info lf[OTX2_CPT_MAX_LFS_NUM];
struct otx2_mbox *mbox;
struct cpt_hw_ops *ops;
u8 are_lfs_attached; /* Whether CPT LFs are attached */
u8 lfs_num; /* Number of CPT LFs */
u8 kcrypto_se_eng_grp_num; /* Crypto symmetric engine group number */
u8 kcrypto_ae_eng_grp_num; /* Crypto asymmetric engine group number */
u8 kvf_limits; /* Kernel crypto limits */
atomic_t state; /* LF's state. started/reset */
int blkaddr; /* CPT blkaddr: BLKADDR_CPT0/BLKADDR_CPT1 */
int global_slot; /* Global slot across the blocks */
u8 ctx_ilen;
u8 ctx_ilen_ovrd;
};
static inline void otx2_cpt_free_instruction_queues(
struct otx2_cptlfs_info *lfs)
{
struct otx2_cpt_inst_queue *iq;
int i;
for (i = 0; i < lfs->lfs_num; i++) {
iq = &lfs->lf[i].iqueue;
if (iq->real_vaddr)
dma_free_coherent(&lfs->pdev->dev,
iq->size,
iq->real_vaddr,
iq->real_dma_addr);
iq->real_vaddr = NULL;
iq->vaddr = NULL;
}
}
static inline int otx2_cpt_alloc_instruction_queues(
struct otx2_cptlfs_info *lfs)
{
struct otx2_cpt_inst_queue *iq;
int ret = 0, i;
if (!lfs->lfs_num)
return -EINVAL;
for (i = 0; i < lfs->lfs_num; i++) {
iq = &lfs->lf[i].iqueue;
iq->size = OTX2_CPT_INST_QLEN_BYTES +
OTX2_CPT_Q_FC_LEN +
OTX2_CPT_INST_GRP_QLEN_BYTES +
OTX2_CPT_INST_Q_ALIGNMENT;
iq->real_vaddr = dma_alloc_coherent(&lfs->pdev->dev, iq->size,
&iq->real_dma_addr, GFP_KERNEL);
if (!iq->real_vaddr) {
ret = -ENOMEM;
goto error;
}
iq->vaddr = iq->real_vaddr + OTX2_CPT_INST_GRP_QLEN_BYTES;
iq->dma_addr = iq->real_dma_addr + OTX2_CPT_INST_GRP_QLEN_BYTES;
/* Align pointers */
iq->vaddr = PTR_ALIGN(iq->vaddr, OTX2_CPT_INST_Q_ALIGNMENT);
iq->dma_addr = PTR_ALIGN(iq->dma_addr,
OTX2_CPT_INST_Q_ALIGNMENT);
}
return 0;
error:
otx2_cpt_free_instruction_queues(lfs);
return ret;
}
static inline void otx2_cptlf_set_iqueues_base_addr(
struct otx2_cptlfs_info *lfs)
{
union otx2_cptx_lf_q_base lf_q_base;
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++) {
lf_q_base.u = lfs->lf[slot].iqueue.dma_addr;
otx2_cpt_write64(lfs->reg_base, lfs->blkaddr, slot,
OTX2_CPT_LF_Q_BASE, lf_q_base.u);
}
}
static inline void otx2_cptlf_do_set_iqueue_size(struct otx2_cptlf_info *lf)
{
union otx2_cptx_lf_q_size lf_q_size = { .u = 0x0 };
lf_q_size.s.size_div40 = OTX2_CPT_SIZE_DIV40 +
OTX2_CPT_EXTRA_SIZE_DIV40;
otx2_cpt_write64(lf->lfs->reg_base, lf->lfs->blkaddr, lf->slot,
OTX2_CPT_LF_Q_SIZE, lf_q_size.u);
}
static inline void otx2_cptlf_set_iqueues_size(struct otx2_cptlfs_info *lfs)
{
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++)
otx2_cptlf_do_set_iqueue_size(&lfs->lf[slot]);
}
#define INFLIGHT GENMASK_ULL(8, 0)
#define GRB_CNT GENMASK_ULL(39, 32)
#define GWB_CNT GENMASK_ULL(47, 40)
#define XQ_XOR GENMASK_ULL(63, 63)
#define DQPTR GENMASK_ULL(19, 0)
#define NQPTR GENMASK_ULL(51, 32)
static inline void otx2_cptlf_do_disable_iqueue(struct otx2_cptlf_info *lf)
{
void __iomem *reg_base = lf->lfs->reg_base;
struct pci_dev *pdev = lf->lfs->pdev;
u8 blkaddr = lf->lfs->blkaddr;
int timeout = 1000000;
u64 inprog, inst_ptr;
u64 slot = lf->slot;
u64 qsize, pending;
int i = 0;
/* Disable instructions enqueuing */
otx2_cpt_write64(reg_base, blkaddr, slot, OTX2_CPT_LF_CTL, 0x0);
inprog = otx2_cpt_read64(reg_base, blkaddr, slot, OTX2_CPT_LF_INPROG);
inprog |= BIT_ULL(16);
otx2_cpt_write64(reg_base, blkaddr, slot, OTX2_CPT_LF_INPROG, inprog);
qsize = otx2_cpt_read64(reg_base, blkaddr, slot, OTX2_CPT_LF_Q_SIZE) & 0x7FFF;
do {
inst_ptr = otx2_cpt_read64(reg_base, blkaddr, slot, OTX2_CPT_LF_Q_INST_PTR);
pending = (FIELD_GET(XQ_XOR, inst_ptr) * qsize * 40) +
FIELD_GET(NQPTR, inst_ptr) - FIELD_GET(DQPTR, inst_ptr);
udelay(1);
timeout--;
} while ((pending != 0) && (timeout != 0));
if (timeout == 0)
dev_warn(&pdev->dev, "TIMEOUT: CPT poll on pending instructions\n");
timeout = 1000000;
/* Wait for CPT queue to become execution-quiescent */
do {
inprog = otx2_cpt_read64(reg_base, blkaddr, slot, OTX2_CPT_LF_INPROG);
if ((FIELD_GET(INFLIGHT, inprog) == 0) &&
(FIELD_GET(GRB_CNT, inprog) == 0)) {
i++;
} else {
i = 0;
timeout--;
}
} while ((timeout != 0) && (i < 10));
if (timeout == 0)
dev_warn(&pdev->dev, "TIMEOUT: CPT poll on inflight count\n");
/* Wait for 2 us to flush all queue writes to memory */
udelay(2);
}
static inline void otx2_cptlf_disable_iqueues(struct otx2_cptlfs_info *lfs)
{
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++) {
otx2_cptlf_do_disable_iqueue(&lfs->lf[slot]);
otx2_cpt_lf_reset_msg(lfs, lfs->global_slot + slot);
}
}
static inline void otx2_cptlf_set_iqueue_enq(struct otx2_cptlf_info *lf,
bool enable)
{
u8 blkaddr = lf->lfs->blkaddr;
union otx2_cptx_lf_ctl lf_ctl;
lf_ctl.u = otx2_cpt_read64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_CTL);
/* Set iqueue's enqueuing */
lf_ctl.s.ena = enable ? 0x1 : 0x0;
otx2_cpt_write64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_CTL, lf_ctl.u);
}
static inline void otx2_cptlf_enable_iqueue_enq(struct otx2_cptlf_info *lf)
{
otx2_cptlf_set_iqueue_enq(lf, true);
}
static inline void otx2_cptlf_set_iqueue_exec(struct otx2_cptlf_info *lf,
bool enable)
{
union otx2_cptx_lf_inprog lf_inprog;
u8 blkaddr = lf->lfs->blkaddr;
lf_inprog.u = otx2_cpt_read64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_INPROG);
/* Set iqueue's execution */
lf_inprog.s.eena = enable ? 0x1 : 0x0;
otx2_cpt_write64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_INPROG, lf_inprog.u);
}
static inline void otx2_cptlf_set_ctx_flr_flush(struct otx2_cptlf_info *lf)
{
u8 blkaddr = lf->lfs->blkaddr;
u64 val;
val = otx2_cpt_read64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_CTX_CTL);
val |= BIT_ULL(0);
otx2_cpt_write64(lf->lfs->reg_base, blkaddr, lf->slot,
OTX2_CPT_LF_CTX_CTL, val);
}
static inline void otx2_cptlf_enable_iqueue_exec(struct otx2_cptlf_info *lf)
{
otx2_cptlf_set_iqueue_exec(lf, true);
}
static inline void otx2_cptlf_disable_iqueue_exec(struct otx2_cptlf_info *lf)
{
otx2_cptlf_set_iqueue_exec(lf, false);
}
static inline void otx2_cptlf_enable_iqueues(struct otx2_cptlfs_info *lfs)
{
int slot;
for (slot = 0; slot < lfs->lfs_num; slot++) {
/* Enable flush on FLR for Errata */
if (is_dev_cn10kb(lfs->pdev))
otx2_cptlf_set_ctx_flr_flush(&lfs->lf[slot]);
otx2_cptlf_enable_iqueue_exec(&lfs->lf[slot]);
otx2_cptlf_enable_iqueue_enq(&lfs->lf[slot]);
}
}
static inline void otx2_cpt_fill_inst(union otx2_cpt_inst_s *cptinst,
struct otx2_cpt_iq_command *iq_cmd,
u64 comp_baddr)
{
cptinst->u[0] = 0x0;
cptinst->s.doneint = true;
cptinst->s.res_addr = comp_baddr;
cptinst->u[2] = 0x0;
cptinst->u[3] = 0x0;
cptinst->s.ei0 = iq_cmd->cmd.u;
cptinst->s.ei1 = iq_cmd->dptr;
cptinst->s.ei2 = iq_cmd->rptr;
cptinst->s.ei3 = iq_cmd->cptr.u;
}
/*
* On OcteonTX2 platform the parameter insts_num is used as a count of
* instructions to be enqueued. The valid values for insts_num are:
* 1 - 1 CPT instruction will be enqueued during LMTST operation
* 2 - 2 CPT instructions will be enqueued during LMTST operation
*/
static inline void otx2_cpt_send_cmd(union otx2_cpt_inst_s *cptinst,
u32 insts_num, struct otx2_cptlf_info *lf)
{
void __iomem *lmtline = lf->lmtline;
long ret;
/*
* Make sure memory areas pointed in CPT_INST_S
* are flushed before the instruction is sent to CPT
*/
dma_wmb();
do {
/* Copy CPT command to LMTLINE */
memcpy_toio(lmtline, cptinst, insts_num * OTX2_CPT_INST_SIZE);
/*
* LDEOR initiates atomic transfer to I/O device
* The following will cause the LMTST to fail (the LDEOR
* returns zero):
* - No stores have been performed to the LMTLINE since it was
* last invalidated.
* - The bytes which have been stored to LMTLINE since it was
* last invalidated form a pattern that is non-contiguous, does
* not start at byte 0, or does not end on a 8-byte boundary.
* (i.e.comprises a formation of other than 1–16 8-byte
* words.)
*
* These rules are designed such that an operating system
* context switch or hypervisor guest switch need have no
* knowledge of the LMTST operations; the switch code does not
* need to store to LMTCANCEL. Also note as LMTLINE data cannot
* be read, there is no information leakage between processes.
*/
ret = otx2_lmt_flush(lf->ioreg);
} while (!ret);
}
static inline bool otx2_cptlf_started(struct otx2_cptlfs_info *lfs)
{
return atomic_read(&lfs->state) == OTX2_CPTLF_STARTED;
}
static inline void otx2_cptlf_set_dev_info(struct otx2_cptlfs_info *lfs,
struct pci_dev *pdev,
void __iomem *reg_base,
struct otx2_mbox *mbox,
int blkaddr)
{
lfs->pdev = pdev;
lfs->reg_base = reg_base;
lfs->mbox = mbox;
lfs->blkaddr = blkaddr;
}
int otx2_cptlf_init(struct otx2_cptlfs_info *lfs, u8 eng_grp_msk, int pri,
int lfs_num);
void otx2_cptlf_shutdown(struct otx2_cptlfs_info *lfs);
int otx2_cptlf_register_misc_interrupts(struct otx2_cptlfs_info *lfs);
int otx2_cptlf_register_done_interrupts(struct otx2_cptlfs_info *lfs);
void otx2_cptlf_unregister_misc_interrupts(struct otx2_cptlfs_info *lfs);
void otx2_cptlf_unregister_done_interrupts(struct otx2_cptlfs_info *lfs);
void otx2_cptlf_free_irqs_affinity(struct otx2_cptlfs_info *lfs);
int otx2_cptlf_set_irqs_affinity(struct otx2_cptlfs_info *lfs);
#endif /* __OTX2_CPTLF_H */
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPTPF_H
#define __OTX2_CPTPF_H
#include "otx2_cpt_common.h"
#include "otx2_cptpf_ucode.h"
#include "otx2_cptlf.h"
struct otx2_cptpf_dev;
struct otx2_cptvf_info {
struct otx2_cptpf_dev *cptpf; /* PF pointer this VF belongs to */
struct work_struct vfpf_mbox_work;
struct pci_dev *vf_dev;
int vf_id;
int intr_idx;
};
struct cptpf_flr_work {
struct work_struct work;
struct otx2_cptpf_dev *pf;
};
struct otx2_cptpf_dev {
void __iomem *reg_base; /* CPT PF registers start address */
void __iomem *afpf_mbox_base; /* PF-AF mbox start address */
void __iomem *vfpf_mbox_base; /* VF-PF mbox start address */
struct pci_dev *pdev; /* PCI device handle */
struct otx2_cptvf_info vf[OTX2_CPT_MAX_VFS_NUM];
struct otx2_cpt_eng_grps eng_grps;/* Engine groups information */
struct otx2_cptlfs_info lfs; /* CPT LFs attached to this PF */
struct otx2_cptlfs_info cpt1_lfs; /* CPT1 LFs attached to this PF */
/* HW capabilities for each engine type */
union otx2_cpt_eng_caps eng_caps[OTX2_CPT_MAX_ENG_TYPES];
bool is_eng_caps_discovered;
/* AF <=> PF mbox */
struct otx2_mbox afpf_mbox;
struct work_struct afpf_mbox_work;
struct workqueue_struct *afpf_mbox_wq;
struct otx2_mbox afpf_mbox_up;
struct work_struct afpf_mbox_up_work;
/* VF <=> PF mbox */
struct otx2_mbox vfpf_mbox;
struct workqueue_struct *vfpf_mbox_wq;
struct workqueue_struct *flr_wq;
struct cptpf_flr_work *flr_work;
struct mutex lock; /* serialize mailbox access */
unsigned long cap_flag;
u8 pf_id; /* RVU PF number */
u8 max_vfs; /* Maximum number of VFs supported by CPT */
u8 enabled_vfs; /* Number of enabled VFs */
u8 sso_pf_func_ovrd; /* SSO PF_FUNC override bit */
u8 kvf_limits; /* Kernel crypto limits */
bool has_cpt1;
u8 rsrc_req_blkaddr;
/* Devlink */
struct devlink *dl;
};
irqreturn_t otx2_cptpf_afpf_mbox_intr(int irq, void *arg);
void otx2_cptpf_afpf_mbox_handler(struct work_struct *work);
void otx2_cptpf_afpf_mbox_up_handler(struct work_struct *work);
irqreturn_t otx2_cptpf_vfpf_mbox_intr(int irq, void *arg);
void otx2_cptpf_vfpf_mbox_handler(struct work_struct *work);
int otx2_inline_cptlf_setup(struct otx2_cptpf_dev *cptpf,
struct otx2_cptlfs_info *lfs, u8 egrp, int num_lfs);
void otx2_inline_cptlf_cleanup(struct otx2_cptlfs_info *lfs);
#endif /* __OTX2_CPTPF_H */
@@ -0,0 +1,886 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include <linux/firmware.h>
#include <linux/sysfs.h>
#include "otx2_cpt_hw_types.h"
#include "otx2_cpt_common.h"
#include "otx2_cpt_devlink.h"
#include "otx2_cptpf_ucode.h"
#include "otx2_cptpf.h"
#include "cn10k_cpt.h"
#include "rvu_reg.h"
#define OTX2_CPT_DRV_NAME "rvu_cptpf"
#define OTX2_CPT_DRV_STRING "Marvell RVU CPT Physical Function Driver"
#define CPT_UC_RID_CN9K_B0 1
#define CPT_UC_RID_CN10K_A 4
#define CPT_UC_RID_CN10K_B 5
static void cptpf_enable_vfpf_mbox_intr(struct otx2_cptpf_dev *cptpf,
int num_vfs)
{
int ena_bits;
/* Clear any pending interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INTX(0), ~0x0ULL);
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INTX(1), ~0x0ULL);
/* Enable VF interrupts for VFs from 0 to 63 */
ena_bits = ((num_vfs - 1) % 64);
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0),
GENMASK_ULL(ena_bits, 0));
if (num_vfs > 64) {
/* Enable VF interrupts for VFs from 64 to 127 */
ena_bits = num_vfs - 64 - 1;
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1),
GENMASK_ULL(ena_bits, 0));
}
}
static void cptpf_disable_vfpf_mbox_intr(struct otx2_cptpf_dev *cptpf,
int num_vfs)
{
int vector;
/* Disable VF-PF interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), ~0ULL);
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1), ~0ULL);
/* Clear any pending interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INTX(0), ~0ULL);
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFPF_MBOX0);
free_irq(vector, cptpf);
if (num_vfs > 64) {
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INTX(1), ~0ULL);
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFPF_MBOX1);
free_irq(vector, cptpf);
}
}
static void cptpf_enable_vf_flr_me_intrs(struct otx2_cptpf_dev *cptpf,
int num_vfs)
{
/* Clear FLR interrupt if any */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(0),
INTR_MASK(num_vfs));
/* Enable VF FLR interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(num_vfs));
/* Clear ME interrupt if any */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFME_INTX(0),
INTR_MASK(num_vfs));
/* Enable VF ME interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(num_vfs));
if (num_vfs <= 64)
return;
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(1),
INTR_MASK(num_vfs - 64));
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(num_vfs - 64));
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFME_INTX(1),
INTR_MASK(num_vfs - 64));
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(num_vfs - 64));
}
static void cptpf_disable_vf_flr_me_intrs(struct otx2_cptpf_dev *cptpf,
int num_vfs)
{
int vector;
/* Disable VF FLR interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(num_vfs));
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFFLR0);
free_irq(vector, cptpf);
/* Disable VF ME interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(num_vfs));
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFME0);
free_irq(vector, cptpf);
if (num_vfs <= 64)
return;
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(num_vfs - 64));
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFFLR1);
free_irq(vector, cptpf);
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(num_vfs - 64));
vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFME1);
free_irq(vector, cptpf);
}
static void cptpf_flr_wq_handler(struct work_struct *work)
{
struct cptpf_flr_work *flr_work;
struct otx2_cptpf_dev *pf;
struct mbox_msghdr *req;
struct otx2_mbox *mbox;
int vf, reg = 0;
flr_work = container_of(work, struct cptpf_flr_work, work);
pf = flr_work->pf;
mbox = &pf->afpf_mbox;
vf = flr_work - pf->flr_work;
mutex_lock(&pf->lock);
req = otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct msg_rsp));
if (!req) {
mutex_unlock(&pf->lock);
return;
}
req->sig = OTX2_MBOX_REQ_SIG;
req->id = MBOX_MSG_VF_FLR;
req->pcifunc &= RVU_PFVF_FUNC_MASK;
req->pcifunc |= (vf + 1) & RVU_PFVF_FUNC_MASK;
otx2_cpt_send_mbox_msg(mbox, pf->pdev);
if (!otx2_cpt_sync_mbox_msg(&pf->afpf_mbox)) {
if (vf >= 64) {
reg = 1;
vf = vf - 64;
}
/* Clear transaction pending register */
otx2_cpt_write64(pf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFTRPENDX(reg), BIT_ULL(vf));
otx2_cpt_write64(pf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf));
}
mutex_unlock(&pf->lock);
}
static irqreturn_t cptpf_vf_flr_intr(int __always_unused irq, void *arg)
{
int reg, dev, vf, start_vf, num_reg = 1;
struct otx2_cptpf_dev *cptpf = arg;
u64 intr;
if (cptpf->max_vfs > 64)
num_reg = 2;
for (reg = 0; reg < num_reg; reg++) {
intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INTX(reg));
if (!intr)
continue;
start_vf = 64 * reg;
for (vf = 0; vf < 64; vf++) {
if (!(intr & BIT_ULL(vf)))
continue;
dev = vf + start_vf;
queue_work(cptpf->flr_wq, &cptpf->flr_work[dev].work);
/* Clear interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf));
/* Disable the interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFFLR_INT_ENA_W1CX(reg),
BIT_ULL(vf));
}
}
return IRQ_HANDLED;
}
static irqreturn_t cptpf_vf_me_intr(int __always_unused irq, void *arg)
{
struct otx2_cptpf_dev *cptpf = arg;
int reg, vf, num_reg = 1;
u64 intr;
if (cptpf->max_vfs > 64)
num_reg = 2;
for (reg = 0; reg < num_reg; reg++) {
intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INTX(reg));
if (!intr)
continue;
for (vf = 0; vf < 64; vf++) {
if (!(intr & BIT_ULL(vf)))
continue;
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFTRPENDX(reg), BIT_ULL(vf));
/* Clear interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFME_INTX(reg), BIT_ULL(vf));
}
}
return IRQ_HANDLED;
}
static void cptpf_unregister_vfpf_intr(struct otx2_cptpf_dev *cptpf,
int num_vfs)
{
cptpf_disable_vfpf_mbox_intr(cptpf, num_vfs);
cptpf_disable_vf_flr_me_intrs(cptpf, num_vfs);
}
static int cptpf_register_vfpf_intr(struct otx2_cptpf_dev *cptpf, int num_vfs)
{
struct pci_dev *pdev = cptpf->pdev;
struct device *dev = &pdev->dev;
int ret, vector;
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX0);
/* Register VF-PF mailbox interrupt handler */
ret = request_irq(vector, otx2_cptpf_vfpf_mbox_intr, 0, "CPTVFPF Mbox0",
cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for PFVF mbox0 irq\n");
return ret;
}
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR0);
/* Register VF FLR interrupt handler */
ret = request_irq(vector, cptpf_vf_flr_intr, 0, "CPTPF FLR0", cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for VFFLR0 irq\n");
goto free_mbox0_irq;
}
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME0);
/* Register VF ME interrupt handler */
ret = request_irq(vector, cptpf_vf_me_intr, 0, "CPTPF ME0", cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for PFVF mbox0 irq\n");
goto free_flr0_irq;
}
if (num_vfs > 64) {
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX1);
ret = request_irq(vector, otx2_cptpf_vfpf_mbox_intr, 0,
"CPTVFPF Mbox1", cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for PFVF mbox1 irq\n");
goto free_me0_irq;
}
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR1);
/* Register VF FLR interrupt handler */
ret = request_irq(vector, cptpf_vf_flr_intr, 0, "CPTPF FLR1",
cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for VFFLR1 irq\n");
goto free_mbox1_irq;
}
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME1);
/* Register VF FLR interrupt handler */
ret = request_irq(vector, cptpf_vf_me_intr, 0, "CPTPF ME1",
cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for VFFLR1 irq\n");
goto free_flr1_irq;
}
}
cptpf_enable_vfpf_mbox_intr(cptpf, num_vfs);
cptpf_enable_vf_flr_me_intrs(cptpf, num_vfs);
return 0;
free_flr1_irq:
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR1);
free_irq(vector, cptpf);
free_mbox1_irq:
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX1);
free_irq(vector, cptpf);
free_me0_irq:
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME0);
free_irq(vector, cptpf);
free_flr0_irq:
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR0);
free_irq(vector, cptpf);
free_mbox0_irq:
vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX0);
free_irq(vector, cptpf);
return ret;
}
static void cptpf_flr_wq_destroy(struct otx2_cptpf_dev *pf)
{
if (!pf->flr_wq)
return;
destroy_workqueue(pf->flr_wq);
pf->flr_wq = NULL;
kfree(pf->flr_work);
}
static int cptpf_flr_wq_init(struct otx2_cptpf_dev *cptpf, int num_vfs)
{
int vf;
cptpf->flr_wq = alloc_ordered_workqueue("cptpf_flr_wq", 0);
if (!cptpf->flr_wq)
return -ENOMEM;
cptpf->flr_work = kzalloc_objs(struct cptpf_flr_work, num_vfs);
if (!cptpf->flr_work)
goto destroy_wq;
for (vf = 0; vf < num_vfs; vf++) {
cptpf->flr_work[vf].pf = cptpf;
INIT_WORK(&cptpf->flr_work[vf].work, cptpf_flr_wq_handler);
}
return 0;
destroy_wq:
destroy_workqueue(cptpf->flr_wq);
return -ENOMEM;
}
static int cptpf_vfpf_mbox_init(struct otx2_cptpf_dev *cptpf, int num_vfs)
{
struct device *dev = &cptpf->pdev->dev;
u64 vfpf_mbox_base;
int err, i;
cptpf->vfpf_mbox_wq =
alloc_ordered_workqueue("cpt_vfpf_mailbox",
WQ_HIGHPRI | WQ_MEM_RECLAIM);
if (!cptpf->vfpf_mbox_wq)
return -ENOMEM;
/* Map VF-PF mailbox memory */
if (test_bit(CN10K_MBOX, &cptpf->cap_flag))
vfpf_mbox_base = readq(cptpf->reg_base + RVU_PF_VF_MBOX_ADDR);
else
vfpf_mbox_base = readq(cptpf->reg_base + RVU_PF_VF_BAR4_ADDR);
if (!vfpf_mbox_base) {
dev_err(dev, "VF-PF mailbox address not configured\n");
err = -ENOMEM;
goto free_wqe;
}
cptpf->vfpf_mbox_base = devm_ioremap_wc(dev, vfpf_mbox_base,
MBOX_SIZE * cptpf->max_vfs);
if (!cptpf->vfpf_mbox_base) {
dev_err(dev, "Mapping of VF-PF mailbox address failed\n");
err = -ENOMEM;
goto free_wqe;
}
err = otx2_mbox_init(&cptpf->vfpf_mbox, cptpf->vfpf_mbox_base,
cptpf->pdev, cptpf->reg_base, MBOX_DIR_PFVF,
num_vfs);
if (err)
goto free_wqe;
for (i = 0; i < num_vfs; i++) {
cptpf->vf[i].vf_id = i;
cptpf->vf[i].cptpf = cptpf;
cptpf->vf[i].intr_idx = i % 64;
INIT_WORK(&cptpf->vf[i].vfpf_mbox_work,
otx2_cptpf_vfpf_mbox_handler);
}
return 0;
free_wqe:
destroy_workqueue(cptpf->vfpf_mbox_wq);
return err;
}
static void cptpf_vfpf_mbox_destroy(struct otx2_cptpf_dev *cptpf)
{
destroy_workqueue(cptpf->vfpf_mbox_wq);
otx2_mbox_destroy(&cptpf->vfpf_mbox);
}
static void cptpf_disable_afpf_mbox_intr(struct otx2_cptpf_dev *cptpf)
{
/* Disable AF-PF interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1C,
0x1ULL);
/* Clear interrupt if any */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL);
}
static int cptpf_register_afpf_mbox_intr(struct otx2_cptpf_dev *cptpf)
{
struct pci_dev *pdev = cptpf->pdev;
struct device *dev = &pdev->dev;
int ret, irq;
irq = pci_irq_vector(pdev, RVU_PF_INT_VEC_AFPF_MBOX);
/* Register AF-PF mailbox interrupt handler */
ret = devm_request_irq(dev, irq, otx2_cptpf_afpf_mbox_intr, 0,
"CPTAFPF Mbox", cptpf);
if (ret) {
dev_err(dev,
"IRQ registration failed for PFAF mbox irq\n");
return ret;
}
/* Clear interrupt if any, to avoid spurious interrupts */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL);
/* Enable AF-PF interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1S,
0x1ULL);
ret = otx2_cpt_send_ready_msg(&cptpf->afpf_mbox, cptpf->pdev);
if (ret) {
dev_warn(dev,
"AF not responding to mailbox, deferring probe\n");
cptpf_disable_afpf_mbox_intr(cptpf);
return -EPROBE_DEFER;
}
return 0;
}
static int cptpf_afpf_mbox_init(struct otx2_cptpf_dev *cptpf)
{
struct pci_dev *pdev = cptpf->pdev;
resource_size_t offset;
int err;
cptpf->afpf_mbox_wq =
alloc_ordered_workqueue("cpt_afpf_mailbox",
WQ_HIGHPRI | WQ_MEM_RECLAIM);
if (!cptpf->afpf_mbox_wq)
return -ENOMEM;
offset = pci_resource_start(pdev, PCI_MBOX_BAR_NUM);
/* Map AF-PF mailbox memory */
cptpf->afpf_mbox_base = devm_ioremap_wc(&pdev->dev, offset, MBOX_SIZE);
if (!cptpf->afpf_mbox_base) {
dev_err(&pdev->dev, "Unable to map BAR4\n");
err = -ENOMEM;
goto error;
}
err = otx2_mbox_init(&cptpf->afpf_mbox, cptpf->afpf_mbox_base,
pdev, cptpf->reg_base, MBOX_DIR_PFAF, 1);
if (err)
goto error;
err = otx2_mbox_init(&cptpf->afpf_mbox_up, cptpf->afpf_mbox_base,
pdev, cptpf->reg_base, MBOX_DIR_PFAF_UP, 1);
if (err)
goto mbox_cleanup;
INIT_WORK(&cptpf->afpf_mbox_work, otx2_cptpf_afpf_mbox_handler);
INIT_WORK(&cptpf->afpf_mbox_up_work, otx2_cptpf_afpf_mbox_up_handler);
mutex_init(&cptpf->lock);
return 0;
mbox_cleanup:
otx2_mbox_destroy(&cptpf->afpf_mbox);
error:
destroy_workqueue(cptpf->afpf_mbox_wq);
return err;
}
static void cptpf_afpf_mbox_destroy(struct otx2_cptpf_dev *cptpf)
{
destroy_workqueue(cptpf->afpf_mbox_wq);
otx2_mbox_destroy(&cptpf->afpf_mbox);
otx2_mbox_destroy(&cptpf->afpf_mbox_up);
}
static ssize_t sso_pf_func_ovrd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", cptpf->sso_pf_func_ovrd);
}
static ssize_t sso_pf_func_ovrd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev);
u8 sso_pf_func_ovrd;
if (!(cptpf->pdev->revision == CPT_UC_RID_CN9K_B0))
return count;
if (kstrtou8(buf, 0, &sso_pf_func_ovrd))
return -EINVAL;
cptpf->sso_pf_func_ovrd = sso_pf_func_ovrd;
return count;
}
static ssize_t kvf_limits_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", cptpf->kvf_limits);
}
static ssize_t kvf_limits_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev);
int lfs_num;
int ret;
ret = kstrtoint(buf, 0, &lfs_num);
if (ret)
return ret;
if (lfs_num < 1 || lfs_num > num_online_cpus()) {
dev_err(dev, "lfs count %d must be in range [1 - %d]\n",
lfs_num, num_online_cpus());
return -EINVAL;
}
cptpf->kvf_limits = lfs_num;
return count;
}
static DEVICE_ATTR_RW(kvf_limits);
static DEVICE_ATTR_RW(sso_pf_func_ovrd);
static struct attribute *cptpf_attrs[] = {
&dev_attr_kvf_limits.attr,
&dev_attr_sso_pf_func_ovrd.attr,
NULL
};
static const struct attribute_group cptpf_sysfs_group = {
.attrs = cptpf_attrs,
};
static int cpt_is_pf_usable(struct otx2_cptpf_dev *cptpf)
{
u64 rev;
rev = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM));
rev = (rev >> 12) & 0xFF;
/*
* Check if AF has setup revision for RVUM block, otherwise
* driver probe should be deferred until AF driver comes up
*/
if (!rev) {
dev_warn(&cptpf->pdev->dev,
"AF is not initialized, deferring probe\n");
return -EPROBE_DEFER;
}
return 0;
}
static void cptpf_get_rid(struct pci_dev *pdev, struct otx2_cptpf_dev *cptpf)
{
struct otx2_cpt_eng_grps *eng_grps = &cptpf->eng_grps;
u64 reg_val = 0x0;
if (is_dev_otx2(pdev)) {
eng_grps->rid = pdev->revision;
return;
}
otx2_cpt_read_af_reg(&cptpf->afpf_mbox, pdev, CPT_AF_CTL, &reg_val,
BLKADDR_CPT0);
if ((cpt_feature_sgv2(pdev) && (reg_val & BIT_ULL(18))) ||
is_dev_cn10ka_ax(pdev))
eng_grps->rid = CPT_UC_RID_CN10K_A;
else if (cpt_feature_sgv2(pdev))
eng_grps->rid = CPT_UC_RID_CN10K_B;
}
static void cptpf_check_block_implemented(struct otx2_cptpf_dev *cptpf)
{
u64 cfg;
cfg = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_CPT1));
if (cfg & BIT_ULL(11))
cptpf->has_cpt1 = true;
}
static int cptpf_device_init(struct otx2_cptpf_dev *cptpf)
{
union otx2_cptx_af_constants1 af_cnsts1 = {0};
int ret = 0;
/* check if 'implemented' bit is set for block BLKADDR_CPT1 */
cptpf_check_block_implemented(cptpf);
/* Get number of SE, IE and AE engines */
ret = otx2_cpt_read_af_reg(&cptpf->afpf_mbox, cptpf->pdev,
CPT_AF_CONSTANTS1, &af_cnsts1.u,
BLKADDR_CPT0);
if (ret)
return ret;
cptpf->eng_grps.avail.max_se_cnt = af_cnsts1.s.se;
cptpf->eng_grps.avail.max_ie_cnt = af_cnsts1.s.ie;
cptpf->eng_grps.avail.max_ae_cnt = af_cnsts1.s.ae;
/* Disable all cores */
ret = otx2_cpt_disable_all_cores(cptpf);
otx2_cptlf_set_dev_info(&cptpf->lfs, cptpf->pdev, cptpf->reg_base,
&cptpf->afpf_mbox, BLKADDR_CPT0);
if (cptpf->has_cpt1)
otx2_cptlf_set_dev_info(&cptpf->cpt1_lfs, cptpf->pdev,
cptpf->reg_base, &cptpf->afpf_mbox,
BLKADDR_CPT1);
return ret;
}
static int cptpf_sriov_disable(struct pci_dev *pdev)
{
struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev);
int num_vfs = pci_num_vf(pdev);
if (!num_vfs)
return 0;
pci_disable_sriov(pdev);
cptpf_unregister_vfpf_intr(cptpf, num_vfs);
cptpf_flr_wq_destroy(cptpf);
cptpf_vfpf_mbox_destroy(cptpf);
module_put(THIS_MODULE);
cptpf->enabled_vfs = 0;
return 0;
}
static int cptpf_sriov_enable(struct pci_dev *pdev, int num_vfs)
{
struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev);
int ret;
/* Initialize VF<=>PF mailbox */
ret = cptpf_vfpf_mbox_init(cptpf, num_vfs);
if (ret)
return ret;
ret = cptpf_flr_wq_init(cptpf, num_vfs);
if (ret)
goto destroy_mbox;
/* Register VF<=>PF mailbox interrupt */
ret = cptpf_register_vfpf_intr(cptpf, num_vfs);
if (ret)
goto destroy_flr;
cptpf_get_rid(pdev, cptpf);
/* Get CPT HW capabilities using LOAD_FVC operation. */
ret = otx2_cpt_discover_eng_capabilities(cptpf);
if (ret)
goto disable_intr;
ret = otx2_cpt_create_eng_grps(cptpf, &cptpf->eng_grps);
if (ret)
goto disable_intr;
cptpf->enabled_vfs = num_vfs;
ret = pci_enable_sriov(pdev, num_vfs);
if (ret)
goto disable_intr;
dev_notice(&cptpf->pdev->dev, "VFs enabled: %d\n", num_vfs);
try_module_get(THIS_MODULE);
return num_vfs;
disable_intr:
cptpf_unregister_vfpf_intr(cptpf, num_vfs);
cptpf->enabled_vfs = 0;
destroy_flr:
cptpf_flr_wq_destroy(cptpf);
destroy_mbox:
cptpf_vfpf_mbox_destroy(cptpf);
return ret;
}
static int otx2_cptpf_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
if (num_vfs > 0) {
return cptpf_sriov_enable(pdev, num_vfs);
} else {
return cptpf_sriov_disable(pdev);
}
}
static int otx2_cptpf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct otx2_cptpf_dev *cptpf;
int err, num_vec;
cptpf = devm_kzalloc(dev, sizeof(*cptpf), GFP_KERNEL);
if (!cptpf)
return -ENOMEM;
err = pcim_enable_device(pdev);
if (err) {
dev_err(dev, "Failed to enable PCI device\n");
goto clear_drvdata;
}
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (err) {
dev_err(dev, "Unable to get usable DMA configuration\n");
goto clear_drvdata;
}
err = pcim_request_all_regions(pdev, OTX2_CPT_DRV_NAME);
if (err) {
dev_err(dev, "Couldn't request PCI resources 0x%x\n", err);
goto clear_drvdata;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, cptpf);
cptpf->pdev = pdev;
/* Map PF's configuration registers */
cptpf->reg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0);
if (!cptpf->reg_base) {
err = -ENOMEM;
dev_err(dev, "Couldn't ioremap PCI resource 0x%x\n", err);
goto clear_drvdata;
}
/* Check if AF driver is up, otherwise defer probe */
err = cpt_is_pf_usable(cptpf);
if (err)
goto clear_drvdata;
num_vec = pci_msix_vec_count(cptpf->pdev);
if (num_vec <= 0) {
err = -EINVAL;
goto clear_drvdata;
}
err = pci_alloc_irq_vectors(pdev, num_vec, num_vec, PCI_IRQ_MSIX);
if (err < 0) {
dev_err(dev, "Request for %d msix vectors failed\n",
RVU_PF_INT_VEC_CNT);
goto clear_drvdata;
}
otx2_cpt_set_hw_caps(pdev, &cptpf->cap_flag);
/* Initialize AF-PF mailbox */
err = cptpf_afpf_mbox_init(cptpf);
if (err)
goto clear_drvdata;
/* Register mailbox interrupt */
err = cptpf_register_afpf_mbox_intr(cptpf);
if (err)
goto destroy_afpf_mbox;
cptpf->max_vfs = pci_sriov_get_totalvfs(pdev);
cptpf->kvf_limits = 1;
/* Initialize CPT PF device */
err = cptpf_device_init(cptpf);
if (err)
goto unregister_intr;
err = cn10k_cptpf_lmtst_init(cptpf);
if (err)
goto unregister_intr;
/* Initialize engine groups */
err = otx2_cpt_init_eng_grps(pdev, &cptpf->eng_grps);
if (err)
goto free_lmtst;
err = sysfs_create_group(&dev->kobj, &cptpf_sysfs_group);
if (err)
goto cleanup_eng_grps;
err = otx2_cpt_register_dl(cptpf);
if (err)
goto sysfs_grp_del;
return 0;
sysfs_grp_del:
sysfs_remove_group(&dev->kobj, &cptpf_sysfs_group);
cleanup_eng_grps:
otx2_cpt_cleanup_eng_grps(pdev, &cptpf->eng_grps);
free_lmtst:
cn10k_cpt_lmtst_free(pdev, &cptpf->lfs);
unregister_intr:
cptpf_disable_afpf_mbox_intr(cptpf);
destroy_afpf_mbox:
cptpf_afpf_mbox_destroy(cptpf);
clear_drvdata:
pci_set_drvdata(pdev, NULL);
return err;
}
static void otx2_cptpf_remove(struct pci_dev *pdev)
{
struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev);
if (!cptpf)
return;
cptpf_sriov_disable(pdev);
otx2_cpt_unregister_dl(cptpf);
/* Cleanup Inline CPT LF's if attached */
if (cptpf->lfs.lfs_num)
otx2_inline_cptlf_cleanup(&cptpf->lfs);
if (cptpf->cpt1_lfs.lfs_num)
otx2_inline_cptlf_cleanup(&cptpf->cpt1_lfs);
/* Delete sysfs entry created for kernel VF limits */
sysfs_remove_group(&pdev->dev.kobj, &cptpf_sysfs_group);
/* Cleanup engine groups */
otx2_cpt_cleanup_eng_grps(pdev, &cptpf->eng_grps);
/* Disable AF-PF mailbox interrupt */
cptpf_disable_afpf_mbox_intr(cptpf);
/* Destroy AF-PF mbox */
cptpf_afpf_mbox_destroy(cptpf);
/* Free LMTST memory */
cn10k_cpt_lmtst_free(pdev, &cptpf->lfs);
pci_set_drvdata(pdev, NULL);
}
/* Supported devices */
static const struct pci_device_id otx2_cpt_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX2_CPT_PCI_PF_DEVICE_ID) },
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CN10K_CPT_PCI_PF_DEVICE_ID) },
{ 0, } /* end of table */
};
MODULE_DEVICE_TABLE(pci, otx2_cpt_id_table);
static struct pci_driver otx2_cpt_pci_driver = {
.name = OTX2_CPT_DRV_NAME,
.id_table = otx2_cpt_id_table,
.probe = otx2_cptpf_probe,
.remove = otx2_cptpf_remove,
.sriov_configure = otx2_cptpf_sriov_configure
};
module_pci_driver(otx2_cpt_pci_driver);
MODULE_IMPORT_NS("CRYPTO_DEV_OCTEONTX2_CPT");
MODULE_AUTHOR("Marvell");
MODULE_DESCRIPTION(OTX2_CPT_DRV_STRING);
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,661 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cpt_common.h"
#include "otx2_cptpf.h"
#include "rvu_reg.h"
/* Fastpath ipsec opcode with inplace processing */
#define CPT_INLINE_RX_OPCODE (0x26 | (1 << 6))
#define CN10K_CPT_INLINE_RX_OPCODE (0x29 | (1 << 6))
#define cpt_inline_rx_opcode(pdev) \
({ \
u8 opcode; \
if (is_dev_otx2(pdev)) \
opcode = CPT_INLINE_RX_OPCODE; \
else \
opcode = CN10K_CPT_INLINE_RX_OPCODE; \
(opcode); \
})
/*
* CPT PF driver version, It will be incremented by 1 for every feature
* addition in CPT mailbox messages.
*/
#define OTX2_CPT_PF_DRV_VERSION 0x1
static int forward_to_af(struct otx2_cptpf_dev *cptpf,
struct otx2_cptvf_info *vf,
struct mbox_msghdr *req, int size)
{
struct mbox_msghdr *msg;
int ret;
mutex_lock(&cptpf->lock);
msg = otx2_mbox_alloc_msg(&cptpf->afpf_mbox, 0, size);
if (msg == NULL) {
mutex_unlock(&cptpf->lock);
return -ENOMEM;
}
memcpy((uint8_t *)msg + sizeof(struct mbox_msghdr),
(uint8_t *)req + sizeof(struct mbox_msghdr), size);
msg->id = req->id;
msg->pcifunc = req->pcifunc;
msg->sig = req->sig;
msg->ver = req->ver;
ret = otx2_cpt_sync_mbox_msg(&cptpf->afpf_mbox);
/* Error code -EIO indicate there is a communication failure
* to the AF. Rest of the error codes indicate that AF processed
* VF messages and set the error codes in response messages
* (if any) so simply forward responses to VF.
*/
if (ret == -EIO) {
dev_warn(&cptpf->pdev->dev,
"AF not responding to VF%d messages\n", vf->vf_id);
mutex_unlock(&cptpf->lock);
return ret;
}
mutex_unlock(&cptpf->lock);
return 0;
}
static int handle_msg_get_caps(struct otx2_cptpf_dev *cptpf,
struct otx2_cptvf_info *vf,
struct mbox_msghdr *req)
{
struct otx2_cpt_caps_rsp *rsp;
rsp = (struct otx2_cpt_caps_rsp *)
otx2_mbox_alloc_msg(&cptpf->vfpf_mbox, vf->vf_id,
sizeof(*rsp));
if (!rsp)
return -ENOMEM;
rsp->hdr.id = MBOX_MSG_GET_CAPS;
rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
rsp->hdr.pcifunc = req->pcifunc;
rsp->cpt_pf_drv_version = OTX2_CPT_PF_DRV_VERSION;
rsp->cpt_revision = cptpf->eng_grps.rid;
memcpy(&rsp->eng_caps, &cptpf->eng_caps, sizeof(rsp->eng_caps));
return 0;
}
static int handle_msg_get_eng_grp_num(struct otx2_cptpf_dev *cptpf,
struct otx2_cptvf_info *vf,
struct mbox_msghdr *req)
{
struct otx2_cpt_egrp_num_msg *grp_req;
struct otx2_cpt_egrp_num_rsp *rsp;
grp_req = (struct otx2_cpt_egrp_num_msg *)req;
rsp = (struct otx2_cpt_egrp_num_rsp *)
otx2_mbox_alloc_msg(&cptpf->vfpf_mbox, vf->vf_id, sizeof(*rsp));
if (!rsp)
return -ENOMEM;
rsp->hdr.id = MBOX_MSG_GET_ENG_GRP_NUM;
rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
rsp->hdr.pcifunc = req->pcifunc;
rsp->eng_type = grp_req->eng_type;
rsp->eng_grp_num = otx2_cpt_get_eng_grp(&cptpf->eng_grps,
grp_req->eng_type);
return 0;
}
static int handle_msg_kvf_limits(struct otx2_cptpf_dev *cptpf,
struct otx2_cptvf_info *vf,
struct mbox_msghdr *req)
{
struct otx2_cpt_kvf_limits_rsp *rsp;
rsp = (struct otx2_cpt_kvf_limits_rsp *)
otx2_mbox_alloc_msg(&cptpf->vfpf_mbox, vf->vf_id, sizeof(*rsp));
if (!rsp)
return -ENOMEM;
rsp->hdr.id = MBOX_MSG_GET_KVF_LIMITS;
rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
rsp->hdr.pcifunc = req->pcifunc;
rsp->kvf_limits = cptpf->kvf_limits;
return 0;
}
static int send_inline_ipsec_inbound_msg(struct otx2_cptpf_dev *cptpf,
int sso_pf_func, u8 slot)
{
struct cpt_inline_ipsec_cfg_msg *req;
struct pci_dev *pdev = cptpf->pdev;
req = (struct cpt_inline_ipsec_cfg_msg *)
otx2_mbox_alloc_msg_rsp(&cptpf->afpf_mbox, 0,
sizeof(*req), sizeof(struct msg_rsp));
if (req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
memset(req, 0, sizeof(*req));
req->hdr.id = MBOX_MSG_CPT_INLINE_IPSEC_CFG;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptpf->pdev, cptpf->pf_id, 0);
req->dir = CPT_INLINE_INBOUND;
req->slot = slot;
req->sso_pf_func_ovrd = cptpf->sso_pf_func_ovrd;
req->sso_pf_func = sso_pf_func;
req->enable = 1;
return otx2_cpt_send_mbox_msg(&cptpf->afpf_mbox, pdev);
}
static int rx_inline_ipsec_lf_cfg(struct otx2_cptpf_dev *cptpf, u8 egrp,
struct otx2_cpt_rx_inline_lf_cfg *req)
{
struct nix_inline_ipsec_cfg *nix_req;
struct pci_dev *pdev = cptpf->pdev;
int ret;
nix_req = (struct nix_inline_ipsec_cfg *)
otx2_mbox_alloc_msg_rsp(&cptpf->afpf_mbox, 0,
sizeof(*nix_req),
sizeof(struct msg_rsp));
if (nix_req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
memset(nix_req, 0, sizeof(*nix_req));
nix_req->hdr.id = MBOX_MSG_NIX_INLINE_IPSEC_CFG;
nix_req->hdr.sig = OTX2_MBOX_REQ_SIG;
nix_req->enable = 1;
nix_req->credit_th = req->credit_th;
nix_req->bpid = req->bpid;
if (!req->credit || req->credit > OTX2_CPT_INST_QLEN_MSGS)
nix_req->cpt_credit = OTX2_CPT_INST_QLEN_MSGS - 1;
else
nix_req->cpt_credit = req->credit - 1;
nix_req->gen_cfg.egrp = egrp;
if (req->opcode)
nix_req->gen_cfg.opcode = req->opcode;
else
nix_req->gen_cfg.opcode = cpt_inline_rx_opcode(pdev);
nix_req->gen_cfg.param1 = req->param1;
nix_req->gen_cfg.param2 = req->param2;
nix_req->inst_qsel.cpt_pf_func =
OTX2_CPT_RVU_PFFUNC(cptpf->pdev, cptpf->pf_id, 0);
nix_req->inst_qsel.cpt_slot = 0;
ret = otx2_cpt_send_mbox_msg(&cptpf->afpf_mbox, pdev);
if (ret)
return ret;
if (cptpf->has_cpt1) {
ret = send_inline_ipsec_inbound_msg(cptpf, req->sso_pf_func, 1);
if (ret)
return ret;
}
return send_inline_ipsec_inbound_msg(cptpf, req->sso_pf_func, 0);
}
int
otx2_inline_cptlf_setup(struct otx2_cptpf_dev *cptpf,
struct otx2_cptlfs_info *lfs, u8 egrp, int num_lfs)
{
int ret;
ret = otx2_cptlf_init(lfs, 1 << egrp, OTX2_CPT_QUEUE_HI_PRIO, 1);
if (ret) {
dev_err(&cptpf->pdev->dev,
"LF configuration failed for RX inline ipsec.\n");
return ret;
}
/* Get msix offsets for attached LFs */
ret = otx2_cpt_msix_offset_msg(lfs);
if (ret)
goto cleanup_lf;
/* Register for CPT LF Misc interrupts */
ret = otx2_cptlf_register_misc_interrupts(lfs);
if (ret)
goto free_irq;
return 0;
free_irq:
otx2_cptlf_unregister_misc_interrupts(lfs);
cleanup_lf:
otx2_cptlf_shutdown(lfs);
return ret;
}
void
otx2_inline_cptlf_cleanup(struct otx2_cptlfs_info *lfs)
{
/* Unregister misc interrupt */
otx2_cptlf_unregister_misc_interrupts(lfs);
/* Cleanup LFs */
otx2_cptlf_shutdown(lfs);
}
static int handle_msg_rx_inline_ipsec_lf_cfg(struct otx2_cptpf_dev *cptpf,
struct mbox_msghdr *req)
{
struct otx2_cpt_rx_inline_lf_cfg *cfg_req;
int num_lfs = 1, ret;
u8 egrp;
cfg_req = (struct otx2_cpt_rx_inline_lf_cfg *)req;
if (cptpf->lfs.lfs_num) {
dev_err(&cptpf->pdev->dev,
"LF is already configured for RX inline ipsec.\n");
return -EEXIST;
}
/*
* Allow LFs to execute requests destined to only grp IE_TYPES and
* set queue priority of each LF to high
*/
egrp = otx2_cpt_get_eng_grp(&cptpf->eng_grps, OTX2_CPT_IE_TYPES);
if (egrp == OTX2_CPT_INVALID_CRYPTO_ENG_GRP) {
dev_err(&cptpf->pdev->dev,
"Engine group for inline ipsec is not available\n");
return -ENOENT;
}
cptpf->lfs.global_slot = 0;
cptpf->lfs.ctx_ilen_ovrd = cfg_req->ctx_ilen_valid;
cptpf->lfs.ctx_ilen = cfg_req->ctx_ilen;
ret = otx2_inline_cptlf_setup(cptpf, &cptpf->lfs, egrp, num_lfs);
if (ret) {
dev_err(&cptpf->pdev->dev, "Inline-Ipsec CPT0 LF setup failed.\n");
return ret;
}
if (cptpf->has_cpt1) {
cptpf->rsrc_req_blkaddr = BLKADDR_CPT1;
cptpf->cpt1_lfs.global_slot = num_lfs;
cptpf->cpt1_lfs.ctx_ilen_ovrd = cfg_req->ctx_ilen_valid;
cptpf->cpt1_lfs.ctx_ilen = cfg_req->ctx_ilen;
ret = otx2_inline_cptlf_setup(cptpf, &cptpf->cpt1_lfs, egrp,
num_lfs);
if (ret) {
dev_err(&cptpf->pdev->dev, "Inline CPT1 LF setup failed.\n");
goto lf_cleanup;
}
cptpf->rsrc_req_blkaddr = 0;
}
ret = rx_inline_ipsec_lf_cfg(cptpf, egrp, cfg_req);
if (ret)
goto lf1_cleanup;
return 0;
lf1_cleanup:
otx2_inline_cptlf_cleanup(&cptpf->cpt1_lfs);
lf_cleanup:
otx2_inline_cptlf_cleanup(&cptpf->lfs);
return ret;
}
static int cptpf_handle_vf_req(struct otx2_cptpf_dev *cptpf,
struct otx2_cptvf_info *vf,
struct mbox_msghdr *req, int size)
{
int err = 0;
/* Check if msg is valid, if not reply with an invalid msg */
if (req->sig != OTX2_MBOX_REQ_SIG)
goto inval_msg;
switch (req->id) {
case MBOX_MSG_GET_ENG_GRP_NUM:
err = handle_msg_get_eng_grp_num(cptpf, vf, req);
break;
case MBOX_MSG_GET_CAPS:
err = handle_msg_get_caps(cptpf, vf, req);
break;
case MBOX_MSG_GET_KVF_LIMITS:
err = handle_msg_kvf_limits(cptpf, vf, req);
break;
case MBOX_MSG_RX_INLINE_IPSEC_LF_CFG:
err = handle_msg_rx_inline_ipsec_lf_cfg(cptpf, req);
break;
default:
err = forward_to_af(cptpf, vf, req, size);
break;
}
return err;
inval_msg:
otx2_reply_invalid_msg(&cptpf->vfpf_mbox, vf->vf_id, 0, req->id);
otx2_mbox_msg_send(&cptpf->vfpf_mbox, vf->vf_id);
return err;
}
irqreturn_t otx2_cptpf_vfpf_mbox_intr(int __always_unused irq, void *arg)
{
struct otx2_cptpf_dev *cptpf = arg;
struct otx2_cptvf_info *vf;
int i, vf_idx;
u64 intr;
/*
* Check which VF has raised an interrupt and schedule
* corresponding work queue to process the messages
*/
for (i = 0; i < 2; i++) {
/* Read the interrupt bits */
intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0,
RVU_PF_VFPF_MBOX_INTX(i));
for (vf_idx = i * 64; vf_idx < cptpf->enabled_vfs; vf_idx++) {
vf = &cptpf->vf[vf_idx];
if (intr & (1ULL << vf->intr_idx)) {
queue_work(cptpf->vfpf_mbox_wq,
&vf->vfpf_mbox_work);
/* Clear the interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM,
0, RVU_PF_VFPF_MBOX_INTX(i),
BIT_ULL(vf->intr_idx));
}
}
}
return IRQ_HANDLED;
}
void otx2_cptpf_vfpf_mbox_handler(struct work_struct *work)
{
struct otx2_cptpf_dev *cptpf;
struct otx2_cptvf_info *vf;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *req_hdr;
struct mbox_msghdr *msg;
struct otx2_mbox *mbox;
int offset, i, err;
vf = container_of(work, struct otx2_cptvf_info, vfpf_mbox_work);
cptpf = vf->cptpf;
mbox = &cptpf->vfpf_mbox;
/* sync with mbox memory region */
smp_rmb();
mdev = &mbox->dev[vf->vf_id];
/* Process received mbox messages */
req_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
offset = mbox->rx_start + ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN);
for (i = 0; i < req_hdr->num_msgs; i++) {
msg = (struct mbox_msghdr *)(mdev->mbase + offset);
/* Set which VF sent this message based on mbox IRQ */
msg->pcifunc = rvu_make_pcifunc(cptpf->pdev, cptpf->pf_id,
(vf->vf_id + 1));
err = cptpf_handle_vf_req(cptpf, vf, msg,
msg->next_msgoff - offset);
/*
* Behave as the AF, drop the msg if there is
* no memory, timeout handling also goes here
*/
if (err == -ENOMEM || err == -EIO)
break;
offset = msg->next_msgoff;
/* Write barrier required for VF responses which are handled by
* PF driver and not forwarded to AF.
*/
smp_wmb();
}
/* Send mbox responses to VF */
if (mdev->num_msgs)
otx2_mbox_msg_send(mbox, vf->vf_id);
}
irqreturn_t otx2_cptpf_afpf_mbox_intr(int __always_unused irq, void *arg)
{
struct otx2_cptpf_dev *cptpf = arg;
struct otx2_mbox_dev *mdev;
struct otx2_mbox *mbox;
struct mbox_hdr *hdr;
u64 intr;
/* Read the interrupt bits */
intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT);
if (intr & 0x1ULL) {
mbox = &cptpf->afpf_mbox;
mdev = &mbox->dev[0];
hdr = mdev->mbase + mbox->rx_start;
if (hdr->num_msgs)
/* Schedule work queue function to process the MBOX request */
queue_work(cptpf->afpf_mbox_wq, &cptpf->afpf_mbox_work);
mbox = &cptpf->afpf_mbox_up;
mdev = &mbox->dev[0];
hdr = mdev->mbase + mbox->rx_start;
if (hdr->num_msgs)
/* Schedule work queue function to process the MBOX request */
queue_work(cptpf->afpf_mbox_wq, &cptpf->afpf_mbox_up_work);
/* Clear and ack the interrupt */
otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT,
0x1ULL);
}
return IRQ_HANDLED;
}
static void process_afpf_mbox_msg(struct otx2_cptpf_dev *cptpf,
struct mbox_msghdr *msg)
{
struct otx2_cptlfs_info *lfs = &cptpf->lfs;
struct device *dev = &cptpf->pdev->dev;
struct cpt_rd_wr_reg_msg *rsp_rd_wr;
struct msix_offset_rsp *rsp_msix;
int i;
if (msg->id >= MBOX_MSG_MAX) {
dev_err(dev, "MBOX msg with unknown ID %d\n", msg->id);
return;
}
if (msg->sig != OTX2_MBOX_RSP_SIG) {
dev_err(dev, "MBOX msg with wrong signature %x, ID %d\n",
msg->sig, msg->id);
return;
}
if (cptpf->rsrc_req_blkaddr == BLKADDR_CPT1)
lfs = &cptpf->cpt1_lfs;
switch (msg->id) {
case MBOX_MSG_READY:
cptpf->pf_id = rvu_get_pf(cptpf->pdev, msg->pcifunc);
break;
case MBOX_MSG_MSIX_OFFSET:
rsp_msix = (struct msix_offset_rsp *) msg;
for (i = 0; i < rsp_msix->cptlfs; i++)
lfs->lf[i].msix_offset = rsp_msix->cptlf_msixoff[i];
for (i = 0; i < rsp_msix->cpt1_lfs; i++)
lfs->lf[i].msix_offset = rsp_msix->cpt1_lf_msixoff[i];
break;
case MBOX_MSG_CPT_RD_WR_REGISTER:
rsp_rd_wr = (struct cpt_rd_wr_reg_msg *)msg;
if (msg->rc) {
dev_err(dev, "Reg %llx rd/wr(%d) failed %d\n",
rsp_rd_wr->reg_offset, rsp_rd_wr->is_write,
msg->rc);
return;
}
if (!rsp_rd_wr->is_write)
*rsp_rd_wr->ret_val = rsp_rd_wr->val;
break;
case MBOX_MSG_ATTACH_RESOURCES:
if (!msg->rc)
lfs->are_lfs_attached = 1;
break;
case MBOX_MSG_DETACH_RESOURCES:
if (!msg->rc)
lfs->are_lfs_attached = 0;
break;
case MBOX_MSG_CPT_INLINE_IPSEC_CFG:
case MBOX_MSG_NIX_INLINE_IPSEC_CFG:
case MBOX_MSG_CPT_LF_RESET:
case MBOX_MSG_LMTST_TBL_SETUP:
break;
default:
dev_err(dev,
"Unsupported msg %d received.\n", msg->id);
break;
}
}
static void forward_to_vf(struct otx2_cptpf_dev *cptpf, struct mbox_msghdr *msg,
int vf_id, int size)
{
struct otx2_mbox *vfpf_mbox;
struct mbox_msghdr *fwd;
if (msg->id >= MBOX_MSG_MAX) {
dev_err(&cptpf->pdev->dev,
"MBOX msg with unknown ID %d\n", msg->id);
return;
}
if (msg->sig != OTX2_MBOX_RSP_SIG) {
dev_err(&cptpf->pdev->dev,
"MBOX msg with wrong signature %x, ID %d\n",
msg->sig, msg->id);
return;
}
vfpf_mbox = &cptpf->vfpf_mbox;
vf_id--;
if (vf_id >= cptpf->enabled_vfs) {
dev_err(&cptpf->pdev->dev,
"MBOX msg to unknown VF: %d >= %d\n",
vf_id, cptpf->enabled_vfs);
return;
}
if (msg->id == MBOX_MSG_VF_FLR)
return;
fwd = otx2_mbox_alloc_msg(vfpf_mbox, vf_id, size);
if (!fwd) {
dev_err(&cptpf->pdev->dev,
"Forwarding to VF%d failed.\n", vf_id);
return;
}
memcpy((uint8_t *)fwd + sizeof(struct mbox_msghdr),
(uint8_t *)msg + sizeof(struct mbox_msghdr), size);
fwd->id = msg->id;
fwd->pcifunc = msg->pcifunc;
fwd->sig = msg->sig;
fwd->ver = msg->ver;
fwd->rc = msg->rc;
}
/* Handle mailbox messages received from AF */
void otx2_cptpf_afpf_mbox_handler(struct work_struct *work)
{
struct otx2_cptpf_dev *cptpf;
struct otx2_mbox *afpf_mbox;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
int offset, vf_id, i;
cptpf = container_of(work, struct otx2_cptpf_dev, afpf_mbox_work);
afpf_mbox = &cptpf->afpf_mbox;
mdev = &afpf_mbox->dev[0];
/* Sync mbox data into memory */
smp_wmb();
rsp_hdr = (struct mbox_hdr *)(mdev->mbase + afpf_mbox->rx_start);
offset = ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);
for (i = 0; i < rsp_hdr->num_msgs; i++) {
msg = (struct mbox_msghdr *)(mdev->mbase + afpf_mbox->rx_start +
offset);
vf_id = (msg->pcifunc >> RVU_PFVF_FUNC_SHIFT) &
RVU_PFVF_FUNC_MASK;
if (vf_id > 0)
forward_to_vf(cptpf, msg, vf_id,
msg->next_msgoff - offset);
else
process_afpf_mbox_msg(cptpf, msg);
offset = msg->next_msgoff;
/* Sync VF response ready to be sent */
smp_wmb();
mdev->msgs_acked++;
}
otx2_mbox_reset(afpf_mbox, 0);
}
static void handle_msg_cpt_inst_lmtst(struct otx2_cptpf_dev *cptpf,
struct mbox_msghdr *msg)
{
struct cpt_inst_lmtst_req *req = (struct cpt_inst_lmtst_req *)msg;
struct otx2_cptlfs_info *lfs = &cptpf->lfs;
struct msg_rsp *rsp;
if (cptpf->lfs.lfs_num)
lfs->ops->send_cmd((union otx2_cpt_inst_s *)req->inst, 1,
&lfs->lf[0]);
rsp = (struct msg_rsp *)otx2_mbox_alloc_msg(&cptpf->afpf_mbox_up, 0,
sizeof(*rsp));
if (!rsp)
return;
rsp->hdr.id = msg->id;
rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
rsp->hdr.pcifunc = 0;
rsp->hdr.rc = 0;
}
static void process_afpf_mbox_up_msg(struct otx2_cptpf_dev *cptpf,
struct mbox_msghdr *msg)
{
if (msg->id >= MBOX_MSG_MAX) {
dev_err(&cptpf->pdev->dev,
"MBOX msg with unknown ID %d\n", msg->id);
return;
}
switch (msg->id) {
case MBOX_MSG_CPT_INST_LMTST:
handle_msg_cpt_inst_lmtst(cptpf, msg);
break;
default:
otx2_reply_invalid_msg(&cptpf->afpf_mbox_up, 0, 0, msg->id);
}
}
void otx2_cptpf_afpf_mbox_up_handler(struct work_struct *work)
{
struct otx2_cptpf_dev *cptpf;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
struct otx2_mbox *mbox;
int offset, i;
cptpf = container_of(work, struct otx2_cptpf_dev, afpf_mbox_up_work);
mbox = &cptpf->afpf_mbox_up;
mdev = &mbox->dev[0];
/* Sync mbox data into memory */
smp_wmb();
rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);
for (i = 0; i < rsp_hdr->num_msgs; i++) {
msg = (struct mbox_msghdr *)(mdev->mbase + offset);
process_afpf_mbox_up_msg(cptpf, msg);
offset = mbox->rx_start + msg->next_msgoff;
}
otx2_mbox_msg_send(mbox, 0);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,172 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPTPF_UCODE_H
#define __OTX2_CPTPF_UCODE_H
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/module.h>
#include "otx2_cpt_hw_types.h"
#include "otx2_cpt_common.h"
/*
* On OcteonTX2 platform IPSec ucode can use both IE and SE engines therefore
* IE and SE engines can be attached to the same engine group.
*/
#define OTX2_CPT_MAX_ETYPES_PER_GRP 2
/* CPT ucode signature size */
#define OTX2_CPT_UCODE_SIGN_LEN 256
/* Microcode version string length */
#define OTX2_CPT_UCODE_VER_STR_SZ 44
/* Maximum number of supported engines/cores on OcteonTX2/CN10K platform */
#define OTX2_CPT_MAX_ENGINES 144
#define OTX2_CPT_ENGS_BITMASK_LEN BITS_TO_LONGS(OTX2_CPT_MAX_ENGINES)
#define OTX2_CPT_UCODE_SZ (64 * 1024)
/* Microcode types */
enum otx2_cpt_ucode_type {
OTX2_CPT_AE_UC_TYPE = 1, /* AE-MAIN */
OTX2_CPT_SE_UC_TYPE1 = 20,/* SE-MAIN - combination of 21 and 22 */
OTX2_CPT_SE_UC_TYPE2 = 21,/* Fast Path IPSec + AirCrypto */
OTX2_CPT_SE_UC_TYPE3 = 22,/*
* Hash + HMAC + FlexiCrypto + RNG +
* Full Feature IPSec + AirCrypto + Kasumi
*/
OTX2_CPT_IE_UC_TYPE1 = 30, /* IE-MAIN - combination of 31 and 32 */
OTX2_CPT_IE_UC_TYPE2 = 31, /* Fast Path IPSec */
OTX2_CPT_IE_UC_TYPE3 = 32, /*
* Hash + HMAC + FlexiCrypto + RNG +
* Full Future IPSec
*/
};
struct otx2_cpt_bitmap {
unsigned long bits[OTX2_CPT_ENGS_BITMASK_LEN];
int size;
};
struct otx2_cpt_engines {
int type;
int count;
};
/* Microcode version number */
struct otx2_cpt_ucode_ver_num {
u8 nn;
u8 xx;
u8 yy;
u8 zz;
};
struct otx2_cpt_ucode_hdr {
struct otx2_cpt_ucode_ver_num ver_num;
u8 ver_str[OTX2_CPT_UCODE_VER_STR_SZ];
__be32 code_length;
u32 padding[3];
};
struct otx2_cpt_ucode {
u8 ver_str[OTX2_CPT_UCODE_VER_STR_SZ + 1];/*
* ucode version in readable
* format
*/
struct otx2_cpt_ucode_ver_num ver_num;/* ucode version number */
char filename[OTX2_CPT_NAME_LENGTH];/* ucode filename */
dma_addr_t dma; /* phys address of ucode image */
void *va; /* virt address of ucode image */
u32 size; /* ucode image size */
int type; /* ucode image type SE, IE, AE or SE+IE */
};
struct otx2_cpt_uc_info_t {
struct list_head list;
struct otx2_cpt_ucode ucode;/* microcode information */
const struct firmware *fw;
};
/* Maximum and current number of engines available for all engine groups */
struct otx2_cpt_engs_available {
int max_se_cnt;
int max_ie_cnt;
int max_ae_cnt;
int se_cnt;
int ie_cnt;
int ae_cnt;
};
/* Engines reserved to an engine group */
struct otx2_cpt_engs_rsvd {
int type; /* engine type */
int count; /* number of engines attached */
int offset; /* constant offset of engine type in the bitmap */
unsigned long *bmap; /* attached engines bitmap */
struct otx2_cpt_ucode *ucode; /* ucode used by these engines */
};
struct otx2_cpt_mirror_info {
int is_ena; /*
* is mirroring enabled, it is set only for engine
* group which mirrors another engine group
*/
int idx; /*
* index of engine group which is mirrored by this
* group, set only for engine group which mirrors
* another group
*/
int ref_count; /*
* number of times this engine group is mirrored by
* other groups, this is set only for engine group
* which is mirrored by other group(s)
*/
};
struct otx2_cpt_eng_grp_info {
struct otx2_cpt_eng_grps *g; /* pointer to engine_groups structure */
/* engines attached */
struct otx2_cpt_engs_rsvd engs[OTX2_CPT_MAX_ETYPES_PER_GRP];
/* ucodes information */
struct otx2_cpt_ucode ucode[OTX2_CPT_MAX_ETYPES_PER_GRP];
/* engine group mirroring information */
struct otx2_cpt_mirror_info mirror;
int idx; /* engine group index */
bool is_enabled; /*
* is engine group enabled, engine group is enabled
* when it has engines attached and ucode loaded
*/
};
struct otx2_cpt_eng_grps {
struct mutex lock;
struct otx2_cpt_eng_grp_info grp[OTX2_CPT_MAX_ENGINE_GROUPS];
struct otx2_cpt_engs_available avail;
void *obj; /* device specific data */
int engs_num; /* total number of engines supported */
u8 eng_ref_cnt[OTX2_CPT_MAX_ENGINES];/* engines reference count */
bool is_grps_created; /* Is the engine groups are already created */
u16 rid;
};
struct otx2_cptpf_dev;
int otx2_cpt_init_eng_grps(struct pci_dev *pdev,
struct otx2_cpt_eng_grps *eng_grps);
void otx2_cpt_cleanup_eng_grps(struct pci_dev *pdev,
struct otx2_cpt_eng_grps *eng_grps);
int otx2_cpt_create_eng_grps(struct otx2_cptpf_dev *cptpf,
struct otx2_cpt_eng_grps *eng_grps);
int otx2_cpt_disable_all_cores(struct otx2_cptpf_dev *cptpf);
int otx2_cpt_get_eng_grp(struct otx2_cpt_eng_grps *eng_grps, int eng_type);
int otx2_cpt_discover_eng_capabilities(struct otx2_cptpf_dev *cptpf);
int otx2_cpt_dl_custom_egrp_create(struct otx2_cptpf_dev *cptpf,
struct devlink_param_gset_ctx *ctx);
int otx2_cpt_dl_custom_egrp_delete(struct otx2_cptpf_dev *cptpf,
struct devlink_param_gset_ctx *ctx);
struct otx2_cpt_engs_rsvd *find_engines_by_type(
struct otx2_cpt_eng_grp_info *eng_grp,
int eng_type);
#endif /* __OTX2_CPTPF_UCODE_H */
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPTVF_H
#define __OTX2_CPTVF_H
#include "mbox.h"
#include "otx2_cptlf.h"
struct otx2_cptvf_dev {
void __iomem *reg_base; /* Register start address */
void __iomem *pfvf_mbox_base; /* PF-VF mbox start address */
struct pci_dev *pdev; /* PCI device handle */
struct otx2_cptlfs_info lfs; /* CPT LFs attached to this VF */
u8 vf_id; /* Virtual function index */
/* PF <=> VF mbox */
struct otx2_mbox pfvf_mbox;
struct work_struct pfvf_mbox_work;
struct workqueue_struct *pfvf_mbox_wq;
int blkaddr;
void *bbuf_base;
unsigned long cap_flag;
u64 eng_caps[OTX2_CPT_MAX_ENG_TYPES];
};
irqreturn_t otx2_cptvf_pfvf_mbox_intr(int irq, void *arg);
void otx2_cptvf_pfvf_mbox_handler(struct work_struct *work);
int otx2_cptvf_send_eng_grp_num_msg(struct otx2_cptvf_dev *cptvf, int eng_type);
int otx2_cptvf_send_kvf_limits_msg(struct otx2_cptvf_dev *cptvf);
int otx2_cpt_mbox_bbuf_init(struct otx2_cptvf_dev *cptvf, struct pci_dev *pdev);
int otx2_cptvf_send_caps_msg(struct otx2_cptvf_dev *cptvf);
#endif /* __OTX2_CPTVF_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,183 @@
/* SPDX-License-Identifier: GPL-2.0-only
* Copyright (C) 2020 Marvell.
*/
#ifndef __OTX2_CPT_ALGS_H
#define __OTX2_CPT_ALGS_H
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <crypto/aead.h>
#include "otx2_cpt_common.h"
#include "cn10k_cpt.h"
#define OTX2_CPT_MAX_ENC_KEY_SIZE 32
#define OTX2_CPT_MAX_HASH_KEY_SIZE 64
#define OTX2_CPT_MAX_KEY_SIZE (OTX2_CPT_MAX_ENC_KEY_SIZE + \
OTX2_CPT_MAX_HASH_KEY_SIZE)
enum otx2_cpt_request_type {
OTX2_CPT_ENC_DEC_REQ = 0x1,
OTX2_CPT_AEAD_ENC_DEC_REQ = 0x2,
OTX2_CPT_AEAD_ENC_DEC_NULL_REQ = 0x3,
OTX2_CPT_PASSTHROUGH_REQ = 0x4
};
enum otx2_cpt_major_opcodes {
OTX2_CPT_MAJOR_OP_MISC = 0x01,
OTX2_CPT_MAJOR_OP_FC = 0x33,
OTX2_CPT_MAJOR_OP_HMAC = 0x35,
};
enum otx2_cpt_cipher_type {
OTX2_CPT_CIPHER_NULL = 0x0,
OTX2_CPT_DES3_CBC = 0x1,
OTX2_CPT_DES3_ECB = 0x2,
OTX2_CPT_AES_CBC = 0x3,
OTX2_CPT_AES_ECB = 0x4,
OTX2_CPT_AES_CFB = 0x5,
OTX2_CPT_AES_CTR = 0x6,
OTX2_CPT_AES_GCM = 0x7,
OTX2_CPT_AES_XTS = 0x8
};
enum otx2_cpt_mac_type {
OTX2_CPT_MAC_NULL = 0x0,
OTX2_CPT_MD5 = 0x1,
OTX2_CPT_SHA1 = 0x2,
OTX2_CPT_SHA224 = 0x3,
OTX2_CPT_SHA256 = 0x4,
OTX2_CPT_SHA384 = 0x5,
OTX2_CPT_SHA512 = 0x6,
OTX2_CPT_GMAC = 0x7
};
enum otx2_cpt_aes_key_len {
OTX2_CPT_AES_128_BIT = 0x1,
OTX2_CPT_AES_192_BIT = 0x2,
OTX2_CPT_AES_256_BIT = 0x3
};
union otx2_cpt_encr_ctrl {
u64 u;
struct {
#if defined(__BIG_ENDIAN_BITFIELD)
u64 enc_cipher:4;
u64 reserved_59:1;
u64 aes_key:2;
u64 iv_source:1;
u64 mac_type:4;
u64 reserved_49_51:3;
u64 auth_input_type:1;
u64 mac_len:8;
u64 reserved_32_39:8;
u64 encr_offset:16;
u64 iv_offset:8;
u64 auth_offset:8;
#else
u64 auth_offset:8;
u64 iv_offset:8;
u64 encr_offset:16;
u64 reserved_32_39:8;
u64 mac_len:8;
u64 auth_input_type:1;
u64 reserved_49_51:3;
u64 mac_type:4;
u64 iv_source:1;
u64 aes_key:2;
u64 reserved_59:1;
u64 enc_cipher:4;
#endif
} e;
};
struct otx2_cpt_cipher {
const char *name;
u8 value;
};
struct otx2_cpt_fc_enc_ctx {
union otx2_cpt_encr_ctrl enc_ctrl;
u8 encr_key[32];
u8 encr_iv[16];
};
union otx2_cpt_fc_hmac_ctx {
struct {
u8 ipad[64];
u8 opad[64];
} e;
struct {
u8 hmac_calc[64]; /* HMAC calculated */
u8 hmac_recv[64]; /* HMAC received */
} s;
};
struct otx2_cpt_fc_ctx {
struct otx2_cpt_fc_enc_ctx enc;
union otx2_cpt_fc_hmac_ctx hmac;
};
struct otx2_cpt_enc_ctx {
u32 key_len;
u8 enc_key[OTX2_CPT_MAX_KEY_SIZE];
u8 cipher_type;
u8 key_type;
u8 enc_align_len;
struct crypto_skcipher *fbk_cipher;
struct pci_dev *pdev;
struct cn10k_cpt_errata_ctx er_ctx;
};
union otx2_cpt_offset_ctrl {
u64 flags;
struct {
#if defined(__BIG_ENDIAN_BITFIELD)
u64 reserved:32;
u64 enc_data_offset:16;
u64 iv_offset:8;
u64 auth_offset:8;
#else
u64 auth_offset:8;
u64 iv_offset:8;
u64 enc_data_offset:16;
u64 reserved:32;
#endif
} e;
};
struct otx2_cpt_req_ctx {
struct otx2_cpt_req_info cpt_req;
union otx2_cpt_offset_ctrl ctrl_word;
struct otx2_cpt_fc_ctx fctx;
union {
struct skcipher_request sk_fbk_req;
struct aead_request fbk_req;
};
};
struct otx2_cpt_sdesc {
struct shash_desc shash;
};
struct otx2_cpt_aead_ctx {
u8 key[OTX2_CPT_MAX_KEY_SIZE];
struct crypto_shash *hashalg;
struct otx2_cpt_sdesc *sdesc;
struct crypto_aead *fbk_cipher;
struct cn10k_cpt_errata_ctx er_ctx;
struct pci_dev *pdev;
u8 *ipad;
u8 *opad;
u32 enc_key_len;
u32 auth_key_len;
u8 cipher_type;
u8 mac_type;
u8 key_type;
u8 is_trunc_hmac;
u8 enc_align_len;
};
int otx2_cpt_crypto_init(struct pci_dev *pdev, struct module *mod,
int num_queues, int num_devices);
void otx2_cpt_crypto_exit(struct pci_dev *pdev, struct module *mod);
#endif /* __OTX2_CPT_ALGS_H */
@@ -0,0 +1,482 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cpt_common.h"
#include "otx2_cptvf.h"
#include "otx2_cptlf.h"
#include "otx2_cptvf_algs.h"
#include "cn10k_cpt.h"
#include <rvu_reg.h>
#define OTX2_CPTVF_DRV_NAME "rvu_cptvf"
static void cptvf_enable_pfvf_mbox_intrs(struct otx2_cptvf_dev *cptvf)
{
/* Clear interrupt if any */
otx2_cpt_write64(cptvf->reg_base, BLKADDR_RVUM, 0, OTX2_RVU_VF_INT,
0x1ULL);
/* Enable PF-VF interrupt */
otx2_cpt_write64(cptvf->reg_base, BLKADDR_RVUM, 0,
OTX2_RVU_VF_INT_ENA_W1S, 0x1ULL);
}
static void cptvf_disable_pfvf_mbox_intrs(struct otx2_cptvf_dev *cptvf)
{
/* Disable PF-VF interrupt */
otx2_cpt_write64(cptvf->reg_base, BLKADDR_RVUM, 0,
OTX2_RVU_VF_INT_ENA_W1C, 0x1ULL);
/* Clear interrupt if any */
otx2_cpt_write64(cptvf->reg_base, BLKADDR_RVUM, 0, OTX2_RVU_VF_INT,
0x1ULL);
}
static int cptvf_register_interrupts(struct otx2_cptvf_dev *cptvf)
{
int ret, irq;
int num_vec;
num_vec = pci_msix_vec_count(cptvf->pdev);
if (num_vec <= 0)
return -EINVAL;
/* Enable MSI-X */
ret = pci_alloc_irq_vectors(cptvf->pdev, num_vec, num_vec,
PCI_IRQ_MSIX);
if (ret < 0) {
dev_err(&cptvf->pdev->dev,
"Request for %d msix vectors failed\n", num_vec);
return ret;
}
irq = pci_irq_vector(cptvf->pdev, OTX2_CPT_VF_INT_VEC_E_MBOX);
/* Register VF<=>PF mailbox interrupt handler */
ret = devm_request_irq(&cptvf->pdev->dev, irq,
otx2_cptvf_pfvf_mbox_intr, 0,
"CPTPFVF Mbox", cptvf);
if (ret)
return ret;
/* Enable PF-VF mailbox interrupts */
cptvf_enable_pfvf_mbox_intrs(cptvf);
ret = otx2_cpt_send_ready_msg(&cptvf->pfvf_mbox, cptvf->pdev);
if (ret) {
dev_warn(&cptvf->pdev->dev,
"PF not responding to mailbox, deferring probe\n");
cptvf_disable_pfvf_mbox_intrs(cptvf);
return -EPROBE_DEFER;
}
return 0;
}
static int cptvf_pfvf_mbox_init(struct otx2_cptvf_dev *cptvf)
{
struct pci_dev *pdev = cptvf->pdev;
resource_size_t offset, size;
int ret;
cptvf->pfvf_mbox_wq =
alloc_ordered_workqueue("cpt_pfvf_mailbox",
WQ_HIGHPRI | WQ_MEM_RECLAIM);
if (!cptvf->pfvf_mbox_wq)
return -ENOMEM;
if (test_bit(CN10K_MBOX, &cptvf->cap_flag)) {
/* For cn10k platform, VF mailbox region is in its BAR2
* register space
*/
cptvf->pfvf_mbox_base = cptvf->reg_base +
CN10K_CPT_VF_MBOX_REGION;
} else {
offset = pci_resource_start(pdev, PCI_MBOX_BAR_NUM);
size = pci_resource_len(pdev, PCI_MBOX_BAR_NUM);
/* Map PF-VF mailbox memory */
cptvf->pfvf_mbox_base = devm_ioremap_wc(&pdev->dev, offset,
size);
if (!cptvf->pfvf_mbox_base) {
dev_err(&pdev->dev, "Unable to map BAR4\n");
ret = -ENOMEM;
goto free_wqe;
}
}
ret = otx2_mbox_init(&cptvf->pfvf_mbox, cptvf->pfvf_mbox_base,
pdev, cptvf->reg_base, MBOX_DIR_VFPF, 1);
if (ret)
goto free_wqe;
ret = otx2_cpt_mbox_bbuf_init(cptvf, pdev);
if (ret)
goto destroy_mbox;
INIT_WORK(&cptvf->pfvf_mbox_work, otx2_cptvf_pfvf_mbox_handler);
return 0;
destroy_mbox:
otx2_mbox_destroy(&cptvf->pfvf_mbox);
free_wqe:
destroy_workqueue(cptvf->pfvf_mbox_wq);
return ret;
}
static void cptvf_pfvf_mbox_destroy(struct otx2_cptvf_dev *cptvf)
{
destroy_workqueue(cptvf->pfvf_mbox_wq);
otx2_mbox_destroy(&cptvf->pfvf_mbox);
}
static void cptlf_work_handler(unsigned long data)
{
otx2_cpt_post_process((struct otx2_cptlf_wqe *) data);
}
static void cleanup_tasklet_work(struct otx2_cptlfs_info *lfs)
{
int i;
for (i = 0; i < lfs->lfs_num; i++) {
if (!lfs->lf[i].wqe)
continue;
tasklet_kill(&lfs->lf[i].wqe->work);
kfree(lfs->lf[i].wqe);
lfs->lf[i].wqe = NULL;
}
}
static int init_tasklet_work(struct otx2_cptlfs_info *lfs)
{
struct otx2_cptlf_wqe *wqe;
int i, ret = 0;
for (i = 0; i < lfs->lfs_num; i++) {
wqe = kzalloc_obj(struct otx2_cptlf_wqe);
if (!wqe) {
ret = -ENOMEM;
goto cleanup_tasklet;
}
tasklet_init(&wqe->work, cptlf_work_handler, (u64) wqe);
wqe->lfs = lfs;
wqe->lf_num = i;
lfs->lf[i].wqe = wqe;
}
return 0;
cleanup_tasklet:
cleanup_tasklet_work(lfs);
return ret;
}
static void free_pending_queues(struct otx2_cptlfs_info *lfs)
{
int i;
for (i = 0; i < lfs->lfs_num; i++) {
kfree(lfs->lf[i].pqueue.head);
lfs->lf[i].pqueue.head = NULL;
}
}
static int alloc_pending_queues(struct otx2_cptlfs_info *lfs)
{
int size, ret, i;
if (!lfs->lfs_num)
return -EINVAL;
for (i = 0; i < lfs->lfs_num; i++) {
lfs->lf[i].pqueue.qlen = OTX2_CPT_INST_QLEN_MSGS;
size = lfs->lf[i].pqueue.qlen *
sizeof(struct otx2_cpt_pending_entry);
lfs->lf[i].pqueue.head = kzalloc(size, GFP_KERNEL);
if (!lfs->lf[i].pqueue.head) {
ret = -ENOMEM;
goto error;
}
/* Initialize spin lock */
spin_lock_init(&lfs->lf[i].pqueue.lock);
}
return 0;
error:
free_pending_queues(lfs);
return ret;
}
static void lf_sw_cleanup(struct otx2_cptlfs_info *lfs)
{
cleanup_tasklet_work(lfs);
free_pending_queues(lfs);
}
static int lf_sw_init(struct otx2_cptlfs_info *lfs)
{
int ret;
ret = alloc_pending_queues(lfs);
if (ret) {
dev_err(&lfs->pdev->dev,
"Allocating pending queues failed\n");
return ret;
}
ret = init_tasklet_work(lfs);
if (ret) {
dev_err(&lfs->pdev->dev,
"Tasklet work init failed\n");
goto pending_queues_free;
}
return 0;
pending_queues_free:
free_pending_queues(lfs);
return ret;
}
static void cptvf_lf_shutdown(struct otx2_cptlfs_info *lfs)
{
atomic_set(&lfs->state, OTX2_CPTLF_IN_RESET);
/* Remove interrupts affinity */
otx2_cptlf_free_irqs_affinity(lfs);
/* Disable instruction queue */
otx2_cptlf_disable_iqueues(lfs);
/* Unregister crypto algorithms */
otx2_cpt_crypto_exit(lfs->pdev, THIS_MODULE);
/* Unregister LFs interrupts */
otx2_cptlf_unregister_misc_interrupts(lfs);
otx2_cptlf_unregister_done_interrupts(lfs);
/* Cleanup LFs software side */
lf_sw_cleanup(lfs);
/* Free instruction queues */
otx2_cpt_free_instruction_queues(lfs);
/* Send request to detach LFs */
otx2_cpt_detach_rsrcs_msg(lfs);
lfs->lfs_num = 0;
}
static int cptvf_lf_init(struct otx2_cptvf_dev *cptvf)
{
struct otx2_cptlfs_info *lfs = &cptvf->lfs;
struct device *dev = &cptvf->pdev->dev;
int ret, lfs_num;
u8 eng_grp_msk;
/* Get engine group number for symmetric crypto */
cptvf->lfs.kcrypto_se_eng_grp_num = OTX2_CPT_INVALID_CRYPTO_ENG_GRP;
ret = otx2_cptvf_send_eng_grp_num_msg(cptvf, OTX2_CPT_SE_TYPES);
if (ret)
return ret;
if (cptvf->lfs.kcrypto_se_eng_grp_num ==
OTX2_CPT_INVALID_CRYPTO_ENG_GRP) {
dev_err(dev,
"Symmetric Engine group for crypto not available\n");
return -ENOENT;
}
/* Get engine group number for asymmetric crypto */
cptvf->lfs.kcrypto_ae_eng_grp_num = OTX2_CPT_INVALID_CRYPTO_ENG_GRP;
ret = otx2_cptvf_send_eng_grp_num_msg(cptvf, OTX2_CPT_AE_TYPES);
if (ret)
return ret;
if (cptvf->lfs.kcrypto_ae_eng_grp_num ==
OTX2_CPT_INVALID_CRYPTO_ENG_GRP) {
dev_err(dev,
"Asymmetric Engine group for crypto not available\n");
return -ENOENT;
}
eng_grp_msk = BIT(cptvf->lfs.kcrypto_se_eng_grp_num) |
BIT(cptvf->lfs.kcrypto_ae_eng_grp_num);
ret = otx2_cptvf_send_kvf_limits_msg(cptvf);
if (ret)
return ret;
lfs_num = cptvf->lfs.kvf_limits;
ret = otx2_cptlf_init(lfs, eng_grp_msk, OTX2_CPT_QUEUE_HI_PRIO,
lfs_num);
if (ret)
return ret;
/* Get msix offsets for attached LFs */
ret = otx2_cpt_msix_offset_msg(lfs);
if (ret)
goto cleanup_lf;
/* Initialize LFs software side */
ret = lf_sw_init(lfs);
if (ret)
goto cleanup_lf;
/* Register LFs interrupts */
ret = otx2_cptlf_register_misc_interrupts(lfs);
if (ret)
goto cleanup_lf_sw;
ret = otx2_cptlf_register_done_interrupts(lfs);
if (ret)
goto cleanup_lf_sw;
/* Set interrupts affinity */
ret = otx2_cptlf_set_irqs_affinity(lfs);
if (ret)
goto unregister_intr;
atomic_set(&lfs->state, OTX2_CPTLF_STARTED);
/* Register crypto algorithms */
ret = otx2_cpt_crypto_init(lfs->pdev, THIS_MODULE, lfs_num, 1);
if (ret) {
dev_err(&lfs->pdev->dev, "algorithms registration failed\n");
goto disable_irqs;
}
return 0;
disable_irqs:
otx2_cptlf_free_irqs_affinity(lfs);
unregister_intr:
otx2_cptlf_unregister_misc_interrupts(lfs);
otx2_cptlf_unregister_done_interrupts(lfs);
cleanup_lf_sw:
lf_sw_cleanup(lfs);
cleanup_lf:
otx2_cptlf_shutdown(lfs);
return ret;
}
static int otx2_cptvf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct otx2_cptvf_dev *cptvf;
int ret;
cptvf = devm_kzalloc(dev, sizeof(*cptvf), GFP_KERNEL);
if (!cptvf)
return -ENOMEM;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(dev, "Failed to enable PCI device\n");
goto clear_drvdata;
}
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(dev, "Unable to get usable DMA configuration\n");
goto clear_drvdata;
}
ret = pcim_request_all_regions(pdev, OTX2_CPTVF_DRV_NAME);
if (ret) {
dev_err(dev, "Couldn't get PCI resources 0x%x\n", ret);
goto clear_drvdata;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, cptvf);
cptvf->pdev = pdev;
/* Map VF's configuration registers */
cptvf->reg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0);
if (!cptvf->reg_base) {
ret = -ENOMEM;
dev_err(dev, "Couldn't ioremap PCI resource 0x%x\n", ret);
goto clear_drvdata;
}
otx2_cpt_set_hw_caps(pdev, &cptvf->cap_flag);
/* Initialize PF<=>VF mailbox */
ret = cptvf_pfvf_mbox_init(cptvf);
if (ret)
goto clear_drvdata;
/* Register interrupts */
ret = cptvf_register_interrupts(cptvf);
if (ret)
goto destroy_pfvf_mbox;
cptvf->blkaddr = BLKADDR_CPT0;
cptvf_hw_ops_get(cptvf);
otx2_cptlf_set_dev_info(&cptvf->lfs, cptvf->pdev, cptvf->reg_base,
&cptvf->pfvf_mbox, cptvf->blkaddr);
ret = otx2_cptvf_send_caps_msg(cptvf);
if (ret) {
dev_err(&pdev->dev, "Couldn't get CPT engine capabilities.\n");
goto unregister_interrupts;
}
if (cptvf->eng_caps[OTX2_CPT_SE_TYPES] & BIT_ULL(35))
cptvf->lfs.ops->cpt_sg_info_create = cn10k_sgv2_info_create;
ret = cn10k_cptvf_lmtst_init(cptvf);
if (ret)
goto unregister_interrupts;
/* Initialize CPT LFs */
ret = cptvf_lf_init(cptvf);
if (ret)
goto free_lmtst;
return 0;
free_lmtst:
cn10k_cpt_lmtst_free(pdev, &cptvf->lfs);
unregister_interrupts:
cptvf_disable_pfvf_mbox_intrs(cptvf);
destroy_pfvf_mbox:
cptvf_pfvf_mbox_destroy(cptvf);
clear_drvdata:
pci_set_drvdata(pdev, NULL);
return ret;
}
static void otx2_cptvf_remove(struct pci_dev *pdev)
{
struct otx2_cptvf_dev *cptvf = pci_get_drvdata(pdev);
if (!cptvf) {
dev_err(&pdev->dev, "Invalid CPT VF device.\n");
return;
}
cptvf_lf_shutdown(&cptvf->lfs);
/* Disable PF-VF mailbox interrupt */
cptvf_disable_pfvf_mbox_intrs(cptvf);
/* Destroy PF-VF mbox */
cptvf_pfvf_mbox_destroy(cptvf);
/* Free LMTST memory */
cn10k_cpt_lmtst_free(pdev, &cptvf->lfs);
pci_set_drvdata(pdev, NULL);
}
/* Supported devices */
static const struct pci_device_id otx2_cptvf_id_table[] = {
{ PCI_VDEVICE(CAVIUM, OTX2_CPT_PCI_VF_DEVICE_ID) },
{ PCI_VDEVICE(CAVIUM, CN10K_CPT_PCI_VF_DEVICE_ID) },
{ } /* end of table */
};
MODULE_DEVICE_TABLE(pci, otx2_cptvf_id_table);
static struct pci_driver otx2_cptvf_pci_driver = {
.name = OTX2_CPTVF_DRV_NAME,
.id_table = otx2_cptvf_id_table,
.probe = otx2_cptvf_probe,
.remove = otx2_cptvf_remove,
};
module_pci_driver(otx2_cptvf_pci_driver);
MODULE_IMPORT_NS("CRYPTO_DEV_OCTEONTX2_CPT");
MODULE_AUTHOR("Marvell");
MODULE_DESCRIPTION("Marvell RVU CPT Virtual Function Driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,241 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cpt_common.h"
#include "otx2_cptvf.h"
#include <rvu_reg.h>
int otx2_cpt_mbox_bbuf_init(struct otx2_cptvf_dev *cptvf, struct pci_dev *pdev)
{
struct otx2_mbox_dev *mdev;
struct otx2_mbox *otx2_mbox;
cptvf->bbuf_base = devm_kmalloc(&pdev->dev, MBOX_SIZE, GFP_KERNEL);
if (!cptvf->bbuf_base)
return -ENOMEM;
/*
* Overwrite mbox mbase to point to bounce buffer, so that PF/VF
* prepare all mbox messages in bounce buffer instead of directly
* in hw mbox memory.
*/
otx2_mbox = &cptvf->pfvf_mbox;
mdev = &otx2_mbox->dev[0];
mdev->mbase = cptvf->bbuf_base;
return 0;
}
static void otx2_cpt_sync_mbox_bbuf(struct otx2_mbox *mbox, int devid)
{
u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE);
struct otx2_mbox_dev *mdev = &mbox->dev[devid];
struct mbox_hdr *hdr;
u64 msg_size;
if (mdev->mbase == hw_mbase)
return;
hdr = hw_mbase + mbox->rx_start;
msg_size = hdr->msg_size;
if (msg_size > mbox->rx_size - msgs_offset)
msg_size = mbox->rx_size - msgs_offset;
/* Copy mbox messages from mbox memory to bounce buffer */
memcpy(mdev->mbase + mbox->rx_start,
hw_mbase + mbox->rx_start, msg_size + msgs_offset);
}
irqreturn_t otx2_cptvf_pfvf_mbox_intr(int __always_unused irq, void *arg)
{
struct otx2_cptvf_dev *cptvf = arg;
u64 intr;
/* Read the interrupt bits */
intr = otx2_cpt_read64(cptvf->reg_base, BLKADDR_RVUM, 0,
OTX2_RVU_VF_INT);
if (intr & 0x1ULL) {
/* Schedule work queue function to process the MBOX request */
queue_work(cptvf->pfvf_mbox_wq, &cptvf->pfvf_mbox_work);
/* Clear and ack the interrupt */
otx2_cpt_write64(cptvf->reg_base, BLKADDR_RVUM, 0,
OTX2_RVU_VF_INT, 0x1ULL);
}
return IRQ_HANDLED;
}
static void process_pfvf_mbox_mbox_msg(struct otx2_cptvf_dev *cptvf,
struct mbox_msghdr *msg)
{
struct otx2_cptlfs_info *lfs = &cptvf->lfs;
struct otx2_cpt_kvf_limits_rsp *rsp_limits;
struct otx2_cpt_egrp_num_rsp *rsp_grp;
struct otx2_cpt_caps_rsp *eng_caps;
struct cpt_rd_wr_reg_msg *rsp_reg;
struct msix_offset_rsp *rsp_msix;
u8 grp_num;
int i;
if (msg->id >= MBOX_MSG_MAX) {
dev_err(&cptvf->pdev->dev,
"MBOX msg with unknown ID %d\n", msg->id);
return;
}
if (msg->sig != OTX2_MBOX_RSP_SIG) {
dev_err(&cptvf->pdev->dev,
"MBOX msg with wrong signature %x, ID %d\n",
msg->sig, msg->id);
return;
}
switch (msg->id) {
case MBOX_MSG_READY:
cptvf->vf_id = ((msg->pcifunc >> RVU_PFVF_FUNC_SHIFT)
& RVU_PFVF_FUNC_MASK) - 1;
break;
case MBOX_MSG_ATTACH_RESOURCES:
/* Check if resources were successfully attached */
if (!msg->rc)
lfs->are_lfs_attached = 1;
break;
case MBOX_MSG_DETACH_RESOURCES:
/* Check if resources were successfully detached */
if (!msg->rc)
lfs->are_lfs_attached = 0;
break;
case MBOX_MSG_MSIX_OFFSET:
rsp_msix = (struct msix_offset_rsp *) msg;
for (i = 0; i < rsp_msix->cptlfs; i++)
lfs->lf[i].msix_offset = rsp_msix->cptlf_msixoff[i];
break;
case MBOX_MSG_CPT_RD_WR_REGISTER:
rsp_reg = (struct cpt_rd_wr_reg_msg *) msg;
if (msg->rc) {
dev_err(&cptvf->pdev->dev,
"Reg %llx rd/wr(%d) failed %d\n",
rsp_reg->reg_offset, rsp_reg->is_write,
msg->rc);
return;
}
if (!rsp_reg->is_write)
*rsp_reg->ret_val = rsp_reg->val;
break;
case MBOX_MSG_GET_ENG_GRP_NUM:
rsp_grp = (struct otx2_cpt_egrp_num_rsp *) msg;
grp_num = rsp_grp->eng_grp_num;
if (rsp_grp->eng_type == OTX2_CPT_SE_TYPES)
cptvf->lfs.kcrypto_se_eng_grp_num = grp_num;
else if (rsp_grp->eng_type == OTX2_CPT_AE_TYPES)
cptvf->lfs.kcrypto_ae_eng_grp_num = grp_num;
break;
case MBOX_MSG_GET_KVF_LIMITS:
rsp_limits = (struct otx2_cpt_kvf_limits_rsp *) msg;
cptvf->lfs.kvf_limits = rsp_limits->kvf_limits;
break;
case MBOX_MSG_GET_CAPS:
eng_caps = (struct otx2_cpt_caps_rsp *)msg;
memcpy(cptvf->eng_caps, eng_caps->eng_caps,
sizeof(cptvf->eng_caps));
break;
case MBOX_MSG_CPT_LF_RESET:
case MBOX_MSG_LMTST_TBL_SETUP:
break;
default:
dev_err(&cptvf->pdev->dev, "Unsupported msg %d received.\n",
msg->id);
break;
}
}
void otx2_cptvf_pfvf_mbox_handler(struct work_struct *work)
{
struct otx2_cptvf_dev *cptvf;
struct otx2_mbox *pfvf_mbox;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
int offset, i;
/* sync with mbox memory region */
smp_rmb();
cptvf = container_of(work, struct otx2_cptvf_dev, pfvf_mbox_work);
pfvf_mbox = &cptvf->pfvf_mbox;
otx2_cpt_sync_mbox_bbuf(pfvf_mbox, 0);
mdev = &pfvf_mbox->dev[0];
rsp_hdr = (struct mbox_hdr *)(mdev->mbase + pfvf_mbox->rx_start);
if (rsp_hdr->num_msgs == 0)
return;
offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
for (i = 0; i < rsp_hdr->num_msgs; i++) {
msg = (struct mbox_msghdr *)(mdev->mbase + pfvf_mbox->rx_start +
offset);
process_pfvf_mbox_mbox_msg(cptvf, msg);
offset = msg->next_msgoff;
mdev->msgs_acked++;
}
otx2_mbox_reset(pfvf_mbox, 0);
}
int otx2_cptvf_send_eng_grp_num_msg(struct otx2_cptvf_dev *cptvf, int eng_type)
{
struct otx2_mbox *mbox = &cptvf->pfvf_mbox;
struct pci_dev *pdev = cptvf->pdev;
struct otx2_cpt_egrp_num_msg *req;
req = (struct otx2_cpt_egrp_num_msg *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct otx2_cpt_egrp_num_rsp));
if (req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->hdr.id = MBOX_MSG_GET_ENG_GRP_NUM;
req->hdr.sig = OTX2_MBOX_REQ_SIG;
req->hdr.pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0);
req->eng_type = eng_type;
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
int otx2_cptvf_send_kvf_limits_msg(struct otx2_cptvf_dev *cptvf)
{
struct otx2_mbox *mbox = &cptvf->pfvf_mbox;
struct pci_dev *pdev = cptvf->pdev;
struct mbox_msghdr *req;
req = (struct mbox_msghdr *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct otx2_cpt_kvf_limits_rsp));
if (req == NULL) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->id = MBOX_MSG_GET_KVF_LIMITS;
req->sig = OTX2_MBOX_REQ_SIG;
req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0);
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
int otx2_cptvf_send_caps_msg(struct otx2_cptvf_dev *cptvf)
{
struct otx2_mbox *mbox = &cptvf->pfvf_mbox;
struct pci_dev *pdev = cptvf->pdev;
struct mbox_msghdr *req;
req = (struct mbox_msghdr *)
otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req),
sizeof(struct otx2_cpt_caps_rsp));
if (!req) {
dev_err(&pdev->dev, "RVU MBOX failed to get message.\n");
return -EFAULT;
}
req->id = MBOX_MSG_GET_CAPS;
req->sig = OTX2_MBOX_REQ_SIG;
req->pcifunc = OTX2_CPT_RVU_PFFUNC(cptvf->pdev, cptvf->vf_id, 0);
return otx2_cpt_send_mbox_msg(mbox, pdev);
}
@@ -0,0 +1,409 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2020 Marvell. */
#include "otx2_cptvf.h"
#include "otx2_cpt_common.h"
/* Default timeout when waiting for free pending entry in us */
#define CPT_PENTRY_TIMEOUT 1000
#define CPT_PENTRY_STEP 50
/* Default threshold for stopping and resuming sender requests */
#define CPT_IQ_STOP_MARGIN 128
#define CPT_IQ_RESUME_MARGIN 512
/* Default command timeout in seconds */
#define CPT_COMMAND_TIMEOUT 4
#define CPT_TIME_IN_RESET_COUNT 5
static void otx2_cpt_dump_sg_list(struct pci_dev *pdev,
struct otx2_cpt_req_info *req)
{
int i;
pr_debug("Gather list size %d\n", req->in_cnt);
for (i = 0; i < req->in_cnt; i++) {
pr_debug("Buffer %d size %d, vptr 0x%p, dmaptr 0x%llx\n", i,
req->in[i].size, req->in[i].vptr,
req->in[i].dma_addr);
pr_debug("Buffer hexdump (%d bytes)\n",
req->in[i].size);
print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1,
req->in[i].vptr, req->in[i].size, false);
}
pr_debug("Scatter list size %d\n", req->out_cnt);
for (i = 0; i < req->out_cnt; i++) {
pr_debug("Buffer %d size %d, vptr 0x%p, dmaptr 0x%llx\n", i,
req->out[i].size, req->out[i].vptr,
req->out[i].dma_addr);
pr_debug("Buffer hexdump (%d bytes)\n", req->out[i].size);
print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1,
req->out[i].vptr, req->out[i].size, false);
}
}
static inline struct otx2_cpt_pending_entry *get_free_pending_entry(
struct otx2_cpt_pending_queue *q,
int qlen)
{
struct otx2_cpt_pending_entry *ent = NULL;
ent = &q->head[q->rear];
if (unlikely(ent->busy))
return NULL;
q->rear++;
if (unlikely(q->rear == qlen))
q->rear = 0;
return ent;
}
static inline u32 modulo_inc(u32 index, u32 length, u32 inc)
{
if (WARN_ON(inc > length))
inc = length;
index += inc;
if (unlikely(index >= length))
index -= length;
return index;
}
static inline void free_pentry(struct otx2_cpt_pending_entry *pentry)
{
pentry->completion_addr = NULL;
pentry->info = NULL;
pentry->callback = NULL;
pentry->areq = NULL;
pentry->resume_sender = false;
pentry->busy = false;
}
static int process_request(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
struct otx2_cpt_pending_queue *pqueue,
struct otx2_cptlf_info *lf)
{
struct otx2_cptvf_request *cpt_req = &req->req;
struct otx2_cpt_pending_entry *pentry = NULL;
union otx2_cpt_ctrl_info *ctrl = &req->ctrl;
struct otx2_cpt_inst_info *info = NULL;
union otx2_cpt_res_s *result = NULL;
struct otx2_cpt_iq_command iq_cmd;
union otx2_cpt_inst_s cptinst;
int retry, ret = 0;
u8 resume_sender;
gfp_t gfp;
gfp = (req->areq->flags & CRYPTO_TFM_REQ_MAY_SLEEP) ? GFP_KERNEL :
GFP_ATOMIC;
if (unlikely(!otx2_cptlf_started(lf->lfs)))
return -ENODEV;
info = lf->lfs->ops->cpt_sg_info_create(pdev, req, gfp);
if (unlikely(!info)) {
dev_err(&pdev->dev, "Setting up cpt inst info failed");
return -ENOMEM;
}
cpt_req->dlen = info->dlen;
result = info->completion_addr;
result->s.compcode = OTX2_CPT_COMPLETION_CODE_INIT;
spin_lock_bh(&pqueue->lock);
pentry = get_free_pending_entry(pqueue, pqueue->qlen);
retry = CPT_PENTRY_TIMEOUT / CPT_PENTRY_STEP;
while (unlikely(!pentry) && retry--) {
spin_unlock_bh(&pqueue->lock);
udelay(CPT_PENTRY_STEP);
spin_lock_bh(&pqueue->lock);
pentry = get_free_pending_entry(pqueue, pqueue->qlen);
}
if (unlikely(!pentry)) {
ret = -ENOSPC;
goto destroy_info;
}
/*
* Check if we are close to filling in entire pending queue,
* if so then tell the sender to stop/sleep by returning -EBUSY
* We do it only for context which can sleep (GFP_KERNEL)
*/
if (gfp == GFP_KERNEL &&
pqueue->pending_count > (pqueue->qlen - CPT_IQ_STOP_MARGIN)) {
pentry->resume_sender = true;
} else
pentry->resume_sender = false;
resume_sender = pentry->resume_sender;
pqueue->pending_count++;
pentry->completion_addr = info->completion_addr;
pentry->info = info;
pentry->callback = req->callback;
pentry->areq = req->areq;
pentry->busy = true;
info->pentry = pentry;
info->time_in = jiffies;
info->req = req;
/* Fill in the command */
iq_cmd.cmd.u = 0;
iq_cmd.cmd.s.opcode = cpu_to_be16(cpt_req->opcode.flags);
iq_cmd.cmd.s.param1 = cpu_to_be16(cpt_req->param1);
iq_cmd.cmd.s.param2 = cpu_to_be16(cpt_req->param2);
iq_cmd.cmd.s.dlen = cpu_to_be16(cpt_req->dlen);
/* 64-bit swap for microcode data reads, not needed for addresses*/
cpu_to_be64s(&iq_cmd.cmd.u);
iq_cmd.dptr = info->dptr_baddr | info->gthr_sz << 60;
iq_cmd.rptr = info->rptr_baddr | info->sctr_sz << 60;
iq_cmd.cptr.s.cptr = cpt_req->cptr_dma;
iq_cmd.cptr.s.grp = ctrl->s.grp;
/* Fill in the CPT_INST_S type command for HW interpretation */
otx2_cpt_fill_inst(&cptinst, &iq_cmd, info->comp_baddr);
/* Print debug info if enabled */
otx2_cpt_dump_sg_list(pdev, req);
pr_debug("Cpt_inst_s hexdump (%d bytes)\n", OTX2_CPT_INST_SIZE);
print_hex_dump_debug("", 0, 16, 1, &cptinst, OTX2_CPT_INST_SIZE, false);
pr_debug("Dptr hexdump (%d bytes)\n", cpt_req->dlen);
print_hex_dump_debug("", 0, 16, 1, info->in_buffer,
cpt_req->dlen, false);
/* Send CPT command */
lf->lfs->ops->send_cmd(&cptinst, 1, lf);
/*
* We allocate and prepare pending queue entry in critical section
* together with submitting CPT instruction to CPT instruction queue
* to make sure that order of CPT requests is the same in both
* pending and instruction queues
*/
spin_unlock_bh(&pqueue->lock);
ret = resume_sender ? -EBUSY : -EINPROGRESS;
return ret;
destroy_info:
spin_unlock_bh(&pqueue->lock);
otx2_cpt_info_destroy(pdev, info);
return ret;
}
int otx2_cpt_do_request(struct pci_dev *pdev, struct otx2_cpt_req_info *req,
int cpu_num)
{
struct otx2_cptvf_dev *cptvf = pci_get_drvdata(pdev);
struct otx2_cptlfs_info *lfs = &cptvf->lfs;
return process_request(lfs->pdev, req, &lfs->lf[cpu_num].pqueue,
&lfs->lf[cpu_num]);
}
static int cpt_process_ccode(struct otx2_cptlfs_info *lfs,
union otx2_cpt_res_s *cpt_status,
struct otx2_cpt_inst_info *info,
u32 *res_code)
{
u8 uc_ccode = lfs->ops->cpt_get_uc_compcode(cpt_status);
u8 ccode = lfs->ops->cpt_get_compcode(cpt_status);
struct pci_dev *pdev = lfs->pdev;
switch (ccode) {
case OTX2_CPT_COMP_E_FAULT:
dev_err(&pdev->dev,
"Request failed with DMA fault\n");
otx2_cpt_dump_sg_list(pdev, info->req);
break;
case OTX2_CPT_COMP_E_HWERR:
dev_err(&pdev->dev,
"Request failed with hardware error\n");
otx2_cpt_dump_sg_list(pdev, info->req);
break;
case OTX2_CPT_COMP_E_INSTERR:
dev_err(&pdev->dev,
"Request failed with instruction error\n");
otx2_cpt_dump_sg_list(pdev, info->req);
break;
case OTX2_CPT_COMP_E_NOTDONE:
/* check for timeout */
if (time_after_eq(jiffies, info->time_in +
CPT_COMMAND_TIMEOUT * HZ))
dev_warn(&pdev->dev,
"Request timed out 0x%p", info->req);
else if (info->extra_time < CPT_TIME_IN_RESET_COUNT) {
info->time_in = jiffies;
info->extra_time++;
}
return 1;
case OTX2_CPT_COMP_E_GOOD:
case OTX2_CPT_COMP_E_WARN:
/*
* Check microcode completion code, it is only valid
* when completion code is CPT_COMP_E::GOOD
*/
if (uc_ccode != OTX2_CPT_UCC_SUCCESS) {
/*
* If requested hmac is truncated and ucode returns
* s/g write length error then we report success
* because ucode writes as many bytes of calculated
* hmac as available in gather buffer and reports
* s/g write length error if number of bytes in gather
* buffer is less than full hmac size.
*/
if (info->req->is_trunc_hmac &&
uc_ccode == OTX2_CPT_UCC_SG_WRITE_LENGTH) {
*res_code = 0;
break;
}
pr_debug("Request failed with software error code 0x%x: algo = %s driver = %s\n",
cpt_status->s.uc_compcode,
info->req->areq->tfm->__crt_alg->cra_name,
info->req->areq->tfm->__crt_alg->cra_driver_name);
otx2_cpt_dump_sg_list(pdev, info->req);
break;
}
/* Request has been processed with success */
*res_code = 0;
break;
default:
dev_err(&pdev->dev,
"Request returned invalid status %d\n", ccode);
break;
}
return 0;
}
static inline void process_pending_queue(struct otx2_cptlfs_info *lfs,
struct otx2_cpt_pending_queue *pqueue)
{
struct otx2_cpt_pending_entry *resume_pentry = NULL;
void (*callback)(int status, void *arg, void *req);
struct otx2_cpt_pending_entry *pentry = NULL;
union otx2_cpt_res_s *cpt_status = NULL;
struct otx2_cpt_inst_info *info = NULL;
struct otx2_cpt_req_info *req = NULL;
struct crypto_async_request *areq;
struct pci_dev *pdev = lfs->pdev;
u32 res_code, resume_index;
while (1) {
spin_lock_bh(&pqueue->lock);
pentry = &pqueue->head[pqueue->front];
if (WARN_ON(!pentry)) {
spin_unlock_bh(&pqueue->lock);
break;
}
res_code = -EINVAL;
if (unlikely(!pentry->busy)) {
spin_unlock_bh(&pqueue->lock);
break;
}
if (unlikely(!pentry->callback)) {
dev_err(&pdev->dev, "Callback NULL\n");
goto process_pentry;
}
info = pentry->info;
if (unlikely(!info)) {
dev_err(&pdev->dev, "Pending entry post arg NULL\n");
goto process_pentry;
}
req = info->req;
if (unlikely(!req)) {
dev_err(&pdev->dev, "Request NULL\n");
goto process_pentry;
}
cpt_status = pentry->completion_addr;
if (unlikely(!cpt_status)) {
dev_err(&pdev->dev, "Completion address NULL\n");
goto process_pentry;
}
if (cpt_process_ccode(lfs, cpt_status, info, &res_code)) {
spin_unlock_bh(&pqueue->lock);
return;
}
info->pdev = pdev;
process_pentry:
/*
* Check if we should inform sending side to resume
* We do it CPT_IQ_RESUME_MARGIN elements in advance before
* pending queue becomes empty
*/
resume_index = modulo_inc(pqueue->front, pqueue->qlen,
CPT_IQ_RESUME_MARGIN);
resume_pentry = &pqueue->head[resume_index];
if (resume_pentry &&
resume_pentry->resume_sender) {
resume_pentry->resume_sender = false;
callback = resume_pentry->callback;
areq = resume_pentry->areq;
if (callback) {
spin_unlock_bh(&pqueue->lock);
/*
* EINPROGRESS is an indication for sending
* side that it can resume sending requests
*/
callback(-EINPROGRESS, areq, info);
spin_lock_bh(&pqueue->lock);
}
}
callback = pentry->callback;
areq = pentry->areq;
free_pentry(pentry);
pqueue->pending_count--;
pqueue->front = modulo_inc(pqueue->front, pqueue->qlen, 1);
spin_unlock_bh(&pqueue->lock);
/*
* Call callback after current pending entry has been
* processed, we don't do it if the callback pointer is
* invalid.
*/
if (callback)
callback(res_code, areq, info);
}
}
void otx2_cpt_post_process(struct otx2_cptlf_wqe *wqe)
{
process_pending_queue(wqe->lfs,
&wqe->lfs->lf[wqe->lf_num].pqueue);
}
int otx2_cpt_get_eng_grp_num(struct pci_dev *pdev,
enum otx2_cpt_eng_type eng_type)
{
struct otx2_cptvf_dev *cptvf = pci_get_drvdata(pdev);
switch (eng_type) {
case OTX2_CPT_SE_TYPES:
return cptvf->lfs.kcrypto_se_eng_grp_num;
case OTX2_CPT_AE_TYPES:
return cptvf->lfs.kcrypto_ae_eng_grp_num;
default:
dev_err(&cptvf->pdev->dev, "Unsupported engine type");
break;
}
return -ENXIO;
}