root/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Admin Function driver
 *
 * Copyright (C) 2026 Marvell.
 *
 */
#include <linux/xarray.h>
#include <linux/bitfield.h>

#include "rvu.h"
#include "npc.h"
#include "npc_profile.h"
#include "rvu_npc_hash.h"
#include "rvu_npc.h"
#include "cn20k/npc.h"
#include "cn20k/reg.h"
#include "rvu_npc_fs.h"

static struct npc_priv_t *npc_priv;

static const char *npc_kw_name[NPC_MCAM_KEY_MAX] = {
        [NPC_MCAM_KEY_DYN] = "DYNAMIC",
        [NPC_MCAM_KEY_X2] = "X2",
        [NPC_MCAM_KEY_X4] = "X4",
};

static const char *npc_dft_rule_name[NPC_DFT_RULE_MAX_ID] = {
        [NPC_DFT_RULE_PROMISC_ID] = "Promisc",
        [NPC_DFT_RULE_MCAST_ID] = "Mcast",
        [NPC_DFT_RULE_BCAST_ID] = "Bcast",
        [NPC_DFT_RULE_UCAST_ID] = "Ucast",
};

#define KEX_EXTR_CFG(bytesm1, hdr_ofs, ena, key_ofs)            \
                     (((bytesm1) << 16) | ((hdr_ofs) << 8) | ((ena) << 7) | \
                     ((key_ofs) & 0x3F))

#define NPC_DFT_RULE_ID_MK(pcifunc, id) \
        ((pcifunc) | FIELD_PREP(GENMASK_ULL(31, 16), id))

#define NPC_DFT_RULE_ID_2_PCIFUNC(rid) \
        FIELD_GET(GENMASK_ULL(15, 0), rid)

#define NPC_DFT_RULE_ID_2_ID(rid) \
        FIELD_GET(GENMASK_ULL(31, 16), rid)

#define NPC_DFT_RULE_PRIO 127

static const char cn20k_def_pfl_name[] = "default";

static struct npc_mcam_kex_extr npc_mkex_extr_default = {
        .mkex_sign = MKEX_CN20K_SIGN,
        .name = "default",
        .kpu_version = NPC_KPU_PROFILE_VER,
        .keyx_cfg = {
                /* nibble: LA..LE (ltype only) + Error code + Channel */
                [NIX_INTF_RX] = ((u64)NPC_MCAM_KEY_DYN << 32) |
                        NPC_CN20K_PARSE_NIBBLE_INTF_RX |
                                 NPC_CN20K_PARSE_NIBBLE_ERRCODE,

                /* nibble: LA..LE (ltype only) */
                [NIX_INTF_TX] = ((u64)NPC_MCAM_KEY_X2 << 32) |
                        NPC_CN20K_PARSE_NIBBLE_INTF_TX,
        },
        .intf_extr_lid = {
        /* Default RX MCAM KEX profile */
        [NIX_INTF_RX] = { NPC_LID_LA, NPC_LID_LA, NPC_LID_LB, NPC_LID_LB,
                          NPC_LID_LC, NPC_LID_LC, NPC_LID_LD },
        [NIX_INTF_TX] = { NPC_LID_LA, NPC_LID_LA, NPC_LID_LB, NPC_LID_LB,
                          NPC_LID_LC, NPC_LID_LD },
        },
        .intf_extr_lt = {
        /* Default RX MCAM KEX profile */
        [NIX_INTF_RX] = {
                [0] = {
                        /* Layer A: Ethernet: */
                        [NPC_LT_LA_ETHER] =
                                /* DMAC: 6 bytes, KW1[63:15] */
                                KEX_EXTR_CFG(0x05, 0x0, 0x1,
                                             NPC_KEXOF_DMAC + 1),
                        [NPC_LT_LA_CPT_HDR] =
                                /* DMAC: 6 bytes, KW1[63:15] */
                                KEX_EXTR_CFG(0x05, 0x0, 0x1,
                                             NPC_KEXOF_DMAC + 1),
                },
                [1] = {
                        /* Layer A: Ethernet: */
                        [NPC_LT_LA_ETHER] =
                                /* Ethertype: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0xc, 0x1, 0x6),
                        [NPC_LT_LA_CPT_HDR] =
                                /* Ethertype: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0xc, 0x1, 0x6),
                },
                [2] = {
                        /* Layer B: Single VLAN (CTAG) */
                        [NPC_LT_LB_CTAG] =
                                /* CTAG VLAN: 2 bytes, KW1[15:0] */
                                KEX_EXTR_CFG(0x01, 0x2, 0x1, 0x8),
                        /* Layer B: Stacked VLAN (STAG|QinQ) */
                        [NPC_LT_LB_STAG_QINQ] =
                                /* Outer VLAN: 2 bytes, KW1[15:0] */
                                KEX_EXTR_CFG(0x01, 0x2, 0x1, 0x8),
                        [NPC_LT_LB_FDSA] =
                                /* SWITCH PORT: 1 byte, KW1[7:0] */
                                KEX_EXTR_CFG(0x0, 0x1, 0x1, 0x8),
                },
                [3] = {
                        [NPC_LT_LB_CTAG] =
                                /* Ethertype: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0x4, 0x1, 0x6),
                        [NPC_LT_LB_STAG_QINQ] =
                                /* Ethertype: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0x8, 0x1, 0x6),
                        [NPC_LT_LB_FDSA] =
                                /* Ethertype: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0x4, 0x1, 0x6),
                },
                [4] = {
                        /* Layer C: IPv4 */
                        [NPC_LT_LC_IP] =
                                /* SIP+DIP: 8 bytes, KW3[7:0], KW2[63:8] */
                                KEX_EXTR_CFG(0x07, 0xc, 0x1, 0x11),
                        /* Layer C: IPv6 */
                        [NPC_LT_LC_IP6] =
                                /* Everything up to SADDR: 8 bytes, KW3[7:0],
                                 * KW2[63:8]
                                 */
                                KEX_EXTR_CFG(0x07, 0x0, 0x1, 0x11),
                },
                [5] = {
                        [NPC_LT_LC_IP] =
                                /* TOS: 1 byte, KW2[7:0] */
                                KEX_EXTR_CFG(0x0, 0x1, 0x1, 0x10),
                },
                [6] = {
                        /* Layer D:UDP */
                        [NPC_LT_LD_UDP] =
                                /* SPORT+DPORT: 4 bytes, KW3[39:8] */
                                KEX_EXTR_CFG(0x3, 0x0, 0x1, 0x19),
                        /* Layer D:TCP */
                        [NPC_LT_LD_TCP] =
                                /* SPORT+DPORT: 4 bytes, KW3[39:8] */
                                KEX_EXTR_CFG(0x3, 0x0, 0x1, 0x19),
                },
        },
        /* Default TX MCAM KEX profile */
        [NIX_INTF_TX] = {
                [0] = {
                        /* Layer A: NIX_INST_HDR_S + Ethernet */
                        /* NIX appends 8 bytes of NIX_INST_HDR_S at the
                         * start of each TX packet supplied to NPC.
                         */
                        [NPC_LT_LA_IH_NIX_ETHER] =
                                /* PF_FUNC: 2B , KW0 [47:32] */
                                KEX_EXTR_CFG(0x01, 0x0, 0x1, 0x4),
                        /* Layer A: HiGig2: */
                        [NPC_LT_LA_IH_NIX_HIGIG2_ETHER] =
                                /* PF_FUNC: 2B , KW0 [47:32] */
                                KEX_EXTR_CFG(0x01, 0x0, 0x1, 0x4),
                },
                [1] = {
                        [NPC_LT_LA_IH_NIX_ETHER] =
                                /* SQ_ID 3 bytes, KW1[63:16] */
                                KEX_EXTR_CFG(0x02, 0x02, 0x1, 0xa),
                        [NPC_LT_LA_IH_NIX_HIGIG2_ETHER] =
                                /* VID: 2 bytes, KW1[31:16] */
                                KEX_EXTR_CFG(0x01, 0x10, 0x1, 0xa),
                },
                [2] = {
                        /* Layer B: Single VLAN (CTAG) */
                        [NPC_LT_LB_CTAG] =
                                /* CTAG VLAN[2..3] KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0x2, 0x1, 0x6),
                        /* Layer B: Stacked VLAN (STAG|QinQ) */
                        [NPC_LT_LB_STAG_QINQ] =
                                /* Outer VLAN: 2 bytes, KW0[63:48] */
                                KEX_EXTR_CFG(0x01, 0x2, 0x1, 0x6),
                },
                [3] = {
                        [NPC_LT_LB_CTAG] =
                                /* CTAG VLAN[2..3] KW1[15:0] */
                                KEX_EXTR_CFG(0x01, 0x4, 0x1, 0x8),
                        [NPC_LT_LB_STAG_QINQ] =
                                /* Outer VLAN: 2 Bytes, KW1[15:0] */
                                KEX_EXTR_CFG(0x01, 0x8, 0x1, 0x8),
                },
                [4] = {
                        /* Layer C: IPv4 */
                        [NPC_LT_LC_IP] =
                                /* SIP+DIP: 8 bytes, KW2[63:0] */
                                KEX_EXTR_CFG(0x07, 0xc, 0x1, 0x10),
                        /* Layer C: IPv6 */
                        [NPC_LT_LC_IP6] =
                                /* Everything up to SADDR: 8 bytes, KW2[63:0] */
                                KEX_EXTR_CFG(0x07, 0x0, 0x1, 0x10),
                },
                [5] = {
                        /* Layer D:UDP */
                        [NPC_LT_LD_UDP] =
                                /* SPORT+DPORT: 4 bytes, KW3[31:0] */
                                KEX_EXTR_CFG(0x3, 0x0, 0x1, 0x18),
                        /* Layer D:TCP */
                        [NPC_LT_LD_TCP] =
                                /* SPORT+DPORT: 4 bytes, KW3[31:0] */
                                KEX_EXTR_CFG(0x3, 0x0, 0x1, 0x18),
                },
        },
        },
};

struct npc_mcam_kex_extr *npc_mkex_extr_default_get(void)
{
        return &npc_mkex_extr_default;
}

static u16 npc_idx2vidx(u16 idx)
{
        unsigned long index;
        void *map;
        u16 vidx;
        int val;

        vidx = idx;
        index = idx;

        map = xa_load(&npc_priv->xa_idx2vidx_map, index);
        if (!map)
                goto done;

        val = xa_to_value(map);
        if (val == -1)
                goto done;

        vidx = val;

done:
        return vidx;
}

static bool npc_is_vidx(u16 vidx)
{
        return vidx >= npc_priv->bank_depth * 2;
}

static u16 npc_vidx2idx(u16 vidx)
{
        unsigned long sentinel = (unsigned long)-1;
        unsigned long index;
        void *map;
        int val;
        u16 idx;

        idx = vidx;
        index = vidx;

        map = xa_load(&npc_priv->xa_vidx2idx_map, index);
        if (!map)
                goto done;

        val = xa_to_value(map);
        if (val == sentinel)
                goto done;

        idx = val;

done:
        return idx;
}

u16 npc_cn20k_vidx2idx(u16 idx)
{
        if (!npc_priv)
                return idx;

        if (!npc_is_vidx(idx))
                return idx;

        return npc_vidx2idx(idx);
}

u16 npc_cn20k_idx2vidx(u16 idx)
{
        if (!npc_priv)
                return idx;

        if (npc_is_vidx(idx))
                return idx;

        return npc_idx2vidx(idx);
}

static int npc_vidx_maps_del_entry(struct rvu *rvu, u16 vidx, u16 *old_midx)
{
        u16 mcam_idx;
        void *map;

        if (!npc_is_vidx(vidx)) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) does not map to proper mcam idx\n",
                        __func__, vidx);
                return -ESRCH;
        }

        mcam_idx = npc_vidx2idx(vidx);

        map = xa_erase(&npc_priv->xa_vidx2idx_map, vidx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) does not map to proper mcam idx\n",
                        __func__, vidx);
                return -ESRCH;
        }

        map = xa_erase(&npc_priv->xa_idx2vidx_map, mcam_idx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) is not valid\n",
                        __func__, vidx);
                return -ESRCH;
        }

        if (old_midx)
                *old_midx = mcam_idx;

        return 0;
}

static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx)
{
        u16 old_midx;
        void *map;
        int rc;

        if (!npc_is_vidx(vidx)) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) does not map to proper mcam idx\n",
                        __func__, vidx);
                return -ESRCH;
        }

        map = xa_erase(&npc_priv->xa_vidx2idx_map, vidx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) could not be deleted from vidx2idx map\n",
                        __func__, vidx);
                return -ESRCH;
        }

        old_midx = xa_to_value(map);

        rc = xa_insert(&npc_priv->xa_vidx2idx_map, vidx,
                       xa_mk_value(new_midx), GFP_KERNEL);
        if (rc) {
                dev_err(rvu->dev,
                        "%s: vidx(%u) cannot be added to vidx2idx map\n",
                        __func__, vidx);
                goto fail1;
        }

        map = xa_erase(&npc_priv->xa_idx2vidx_map, old_midx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: old_midx(%u, vidx(%u)) cannot be added to idx2vidx map\n",
                        __func__, old_midx, vidx);
                rc = -ESRCH;
                goto fail2;
        }

        rc = xa_insert(&npc_priv->xa_idx2vidx_map, new_midx,
                       xa_mk_value(vidx), GFP_KERNEL);
        if (rc) {
                dev_err(rvu->dev,
                        "%s: new_midx(%u, vidx(%u)) cannot be added to idx2vidx map\n",
                        __func__, new_midx, vidx);
                goto fail3;
        }

        return 0;

fail3:
        /* Restore vidx at old_midx location */
        if (xa_insert(&npc_priv->xa_idx2vidx_map, old_midx,
                      xa_mk_value(vidx), GFP_KERNEL))
                dev_err(rvu->dev,
                        "%s: Error to roll back idx2vidx old_midx=%u vidx=%u\n",
                        __func__, old_midx, vidx);
fail2:
        /* Erase new_midx inserted at vidx */
        if (!xa_erase(&npc_priv->xa_vidx2idx_map, vidx))
                dev_err(rvu->dev,
                        "%s: Failed to roll back vidx2idx vidx=%u\n",
                        __func__, vidx);

fail1:
        /* Restore old_midx at vidx location */
        if (xa_insert(&npc_priv->xa_vidx2idx_map, vidx,
                      xa_mk_value(old_midx), GFP_KERNEL))
                dev_err(rvu->dev,
                        "%s: Failed to roll back vidx2idx to old_midx=%u, vidx=%u\n",
                        __func__, old_midx, vidx);

        return rc;
}

static int npc_vidx_maps_add_entry(struct rvu *rvu, u16 mcam_idx, int pcifunc,
                                   u16 *vidx)
{
        int rc, max, min;
        u32 id;

        /* Virtual index start from maximum mcam index + 1 */
        max = npc_priv->bank_depth * 2 * 2 - 1;
        min = npc_priv->bank_depth * 2;

        rc = xa_alloc(&npc_priv->xa_vidx2idx_map, &id,
                      xa_mk_value(mcam_idx),
                      XA_LIMIT(min, max), GFP_KERNEL);
        if (rc) {
                dev_err(rvu->dev,
                        "%s: Failed to add to vidx2idx map (%u)\n",
                        __func__, mcam_idx);
                goto fail1;
        }

        rc = xa_insert(&npc_priv->xa_idx2vidx_map, mcam_idx,
                       xa_mk_value(id), GFP_KERNEL);
        if (rc) {
                dev_err(rvu->dev,
                        "%s: Failed to add to idx2vidx map (%u)\n",
                        __func__, mcam_idx);
                goto fail2;
        }

        if (vidx)
                *vidx = id;

        return 0;

fail2:
        xa_erase(&npc_priv->xa_vidx2idx_map, id);
fail1:
        return rc;
}

static void npc_config_kpmcam(struct rvu *rvu, int blkaddr,
                              const struct npc_kpu_profile_cam *kpucam,
                              int kpm, int entry)
{
        struct npc_kpu_cam cam0 = {0};
        struct npc_kpu_cam cam1 = {0};

        cam1.state = kpucam->state & kpucam->state_mask;
        cam1.dp0_data = kpucam->dp0 & kpucam->dp0_mask;
        cam1.dp1_data = kpucam->dp1 & kpucam->dp1_mask;
        cam1.dp2_data = kpucam->dp2 & kpucam->dp2_mask;

        cam0.state = ~kpucam->state & kpucam->state_mask;
        cam0.dp0_data = ~kpucam->dp0 & kpucam->dp0_mask;
        cam0.dp1_data = ~kpucam->dp1 & kpucam->dp1_mask;
        cam0.dp2_data = ~kpucam->dp2 & kpucam->dp2_mask;

        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRYX_CAMX(kpm, entry, 0), *(u64 *)&cam0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRYX_CAMX(kpm, entry, 1), *(u64 *)&cam1);
}

static void
npc_config_kpmaction(struct rvu *rvu, int blkaddr,
                     const struct npc_kpu_profile_action *kpuaction,
                     int kpm, int entry, bool pkind)
{
        struct npc_kpm_action0 action0 = {0};
        struct npc_kpu_action1 action1 = {0};
        u64 reg;

        action1.errlev = kpuaction->errlev;
        action1.errcode = kpuaction->errcode;
        action1.dp0_offset = kpuaction->dp0_offset;
        action1.dp1_offset = kpuaction->dp1_offset;
        action1.dp2_offset = kpuaction->dp2_offset;

        if (pkind)
                reg = NPC_AF_PKINDX_ACTION1(entry);
        else
                reg = NPC_AF_KPMX_ENTRYX_ACTION1(kpm, entry);

        rvu_write64(rvu, blkaddr, reg, *(u64 *)&action1);

        action0.byp_count = kpuaction->bypass_count & 0x7;
        action0.capture_ena = kpuaction->cap_ena & 1;
        action0.parse_done = kpuaction->parse_done & 1;
        action0.next_state = kpuaction->next_state & 0xf;
        action0.capture_lid = kpuaction->lid & 0x7;

        /* Parser functionality will work correctly even though
         * upper flag bits are silently discarded
         */
        action0.capture_ltype = kpuaction->ltype & 0xf;
        action0.capture_flags = kpuaction->flags & 0xf;
        action0.ptr_advance = kpuaction->ptr_advance;

        action0.var_len_offset = kpuaction->offset;
        action0.var_len_mask = kpuaction->mask;
        action0.var_len_right = kpuaction->right & 1;
        action0.var_len_shift = kpuaction->shift & 1;

        if (pkind)
                reg = NPC_AF_PKINDX_ACTION0(entry);
        else
                reg = NPC_AF_KPMX_ENTRYX_ACTION0(kpm, entry);

        rvu_write64(rvu, blkaddr, reg, *(u64 *)&action0);
}

static void
npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
                               int kpm, int start_entry,
                               const struct npc_kpu_profile *profile)
{
        int num_cam_entries, num_action_entries;
        int entry, num_entries, max_entries;
        u64 idx;

        num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile);
        num_action_entries = npc_get_num_kpu_action_entries(rvu, profile);

        if (num_cam_entries != num_action_entries) {
                dev_err(rvu->dev,
                        "kpm%d: CAM and action entries [%d != %d] not equal\n",
                        kpm, num_cam_entries, num_action_entries);

                WARN(1, "Fatal error\n");
                return;
        }

        max_entries = rvu->hw->npc_kpu_entries / 2;
        entry = start_entry;
        /* Program CAM match entries for previous kpm extracted data */
        num_entries = min_t(int, num_cam_entries, max_entries);
        for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
                npc_config_kpmcam(rvu, blkaddr,
                                  npc_get_kpu_cam_nth_entry(rvu, profile, idx),
                                  kpm, entry);

        entry = start_entry;
        /* Program this kpm's actions */
        num_entries = min_t(int, num_action_entries, max_entries);
        for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
                npc_config_kpmaction(rvu, blkaddr,
                                     npc_get_kpu_action_nth_entry(rvu, profile, idx),
                                     kpm, entry, false);
}

static void
npc_enable_kpm_entry(struct rvu *rvu, int blkaddr, int kpm, int num_entries)
{
        u64 entry_mask;

        entry_mask = npc_enable_mask(num_entries);
        /* Disable first KPU_CN20K_MAX_CST_ENT entries for built-in profile */
        if (!rvu->kpu.custom)
                entry_mask |= GENMASK_ULL(KPU_CN20K_MAX_CST_ENT - 1, 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRY_DISX(kpm, 0), entry_mask);
        if (num_entries <= 64) {
                /* Disable all the entries in W1, W2 and W3 */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 1),
                            npc_enable_mask(0));
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 2),
                            npc_enable_mask(0));
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 3),
                            npc_enable_mask(0));
                return;
        }

        num_entries = num_entries - 64;
        entry_mask = npc_enable_mask(num_entries);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRY_DISX(kpm, 1), entry_mask);
        if (num_entries <= 64) {
                /* Disable all the entries in W2 and W3 */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 2),
                            npc_enable_mask(0));
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 3),
                            npc_enable_mask(0));
                return;
        }

        num_entries = num_entries - 64;
        entry_mask = npc_enable_mask(num_entries);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRY_DISX(kpm, 2), entry_mask);
        if (num_entries <= 64) {
                /* Disable all the entries in W3 */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(kpm, 3),
                            npc_enable_mask(0));
                return;
        }

        num_entries = num_entries - 64;
        entry_mask = npc_enable_mask(num_entries);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_KPMX_ENTRY_DISX(kpm, 3), entry_mask);
}

#define KPU_OFFSET      8
static void npc_program_kpm_profile(struct rvu *rvu, int blkaddr, int num_kpms)
{
        const struct npc_kpu_profile *profile1, *profile2;
        int pfl1_num_cam_entries, pfl2_num_cam_entries;
        int idx, total_cam_entries;

        for (idx = 0; idx < num_kpms; idx++) {
                profile1 = &rvu->kpu.kpu[idx];
                pfl1_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile1);
                npc_program_single_kpm_profile(rvu, blkaddr, idx, 0, profile1);
                profile2 = &rvu->kpu.kpu[idx + KPU_OFFSET];
                pfl2_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile2);

                npc_program_single_kpm_profile(rvu, blkaddr, idx,
                                               pfl1_num_cam_entries,
                                               profile2);
                total_cam_entries = pfl1_num_cam_entries + pfl2_num_cam_entries;
                npc_enable_kpm_entry(rvu, blkaddr, idx, total_cam_entries);
                rvu_write64(rvu, blkaddr, NPC_AF_KPMX_PASS2_OFFSET(idx),
                            pfl1_num_cam_entries);
                /* Enable the KPUs associated with this KPM */
                rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx), 0x01);
                rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx + KPU_OFFSET),
                            0x01);
        }
}

void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr)
{
        struct npc_kpu_profile_action *act;
        struct rvu_hwinfo *hw = rvu->hw;
        int num_pkinds, idx;

        /* Disable all KPMs and their entries */
        for (idx = 0; idx < hw->npc_kpms; idx++) {
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(idx, 0), ~0ULL);
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(idx, 1), ~0ULL);
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(idx, 2), ~0ULL);
                rvu_write64(rvu, blkaddr,
                            NPC_AF_KPMX_ENTRY_DISX(idx, 3), ~0ULL);
        }

        for (idx = 0; idx < hw->npc_kpus; idx++)
                rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx), 0x00);

        /* Load and customize KPU profile. */
        npc_load_kpu_profile(rvu);

        /* Configure KPU and KPM mapping for second pass */
        rvu_write64(rvu, blkaddr, NPC_AF_KPM_PASS2_CFG, 0x76543210);

        /* First program IKPU profile i.e PKIND configs.
         * Check HW max count to avoid configuring junk or
         * writing to unsupported CSR addresses.
         */
        num_pkinds = rvu->kpu.pkinds;
        num_pkinds = min_t(int, hw->npc_pkinds, num_pkinds);

        /* Cn20k does not support Custom profile from filesystem */
        for (idx = 0; idx < num_pkinds; idx++) {
                act = npc_get_ikpu_nth_entry(rvu, idx);
                if (!act)
                        continue;

                npc_config_kpmaction(rvu, blkaddr, act,
                                     0, idx, true);
        }

        /* Program KPM CAM and Action profiles */
        npc_program_kpm_profile(rvu, blkaddr, hw->npc_kpms);
}

struct npc_priv_t *npc_priv_get(void)
{
        return npc_priv;
}

static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr,
                                const struct npc_mcam_kex_extr *mkex_extr,
                                u8 intf)
{
        u8 num_extr = rvu->hw->npc_kex_extr;
        int extr, lt;
        u64 val;

        if (is_npc_intf_tx(intf))
                return;

        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(intf),
                    mkex_extr->keyx_cfg[NIX_INTF_RX]);

        /* Program EXTRACTOR */
        for (extr = 0; extr < num_extr; extr++)
                rvu_write64(rvu, blkaddr,
                            NPC_AF_INTFX_EXTRACTORX_CFG(intf, extr),
                            mkex_extr->intf_extr_lid[intf][extr]);

        /* Program EXTRACTOR_LTYPE */
        for (extr = 0; extr < num_extr; extr++) {
                for (lt = 0; lt < NPC_MAX_LT; lt++) {
                        val = mkex_extr->intf_extr_lt[intf][extr][lt];
                        CN20K_SET_EXTR_LT(intf, extr, lt, val);
                }
        }
}

static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr,
                                const struct npc_mcam_kex_extr *mkex_extr,
                                u8 intf)
{
        u8 num_extr = rvu->hw->npc_kex_extr;
        int extr, lt;
        u64 val;

        if (is_npc_intf_rx(intf))
                return;

        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(intf),
                    mkex_extr->keyx_cfg[NIX_INTF_TX]);

        /* Program EXTRACTOR */
        for (extr = 0; extr < num_extr; extr++)
                rvu_write64(rvu, blkaddr,
                            NPC_AF_INTFX_EXTRACTORX_CFG(intf, extr),
                            mkex_extr->intf_extr_lid[intf][extr]);

        /* Program EXTRACTOR_LTYPE */
        for (extr = 0; extr < num_extr; extr++) {
                for (lt = 0; lt < NPC_MAX_LT; lt++) {
                        val = mkex_extr->intf_extr_lt[intf][extr][lt];
                        CN20K_SET_EXTR_LT(intf, extr, lt, val);
                }
        }
}

static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr,
                                     const struct npc_mcam_kex_extr *mkex_extr)
{
        struct rvu_hwinfo *hw = rvu->hw;
        u8 intf;

        for (intf = 0; intf < hw->npc_intfs; intf++) {
                npc_program_mkex_rx(rvu, blkaddr, mkex_extr, intf);
                npc_program_mkex_tx(rvu, blkaddr, mkex_extr, intf);
        }

        /* Programme mkex hash profile */
        npc_program_mkex_hash(rvu, blkaddr);
}

void npc_cn20k_load_mkex_profile(struct rvu *rvu, int blkaddr,
                                 const char *mkex_profile)
{
        struct npc_mcam_kex_extr *mcam_kex_extr;
        struct device *dev = &rvu->pdev->dev;
        void __iomem *mkex_prfl_addr = NULL;
        u64 prfl_sz;
        int ret;

        /* If user not selected mkex profile */
        if (rvu->kpu_fwdata_sz ||
            !strncmp(mkex_profile, cn20k_def_pfl_name, MKEX_NAME_LEN))
                goto program_mkex_extr;

        /* Setting up the mapping for mkex profile image */
        ret = npc_fwdb_prfl_img_map(rvu, &mkex_prfl_addr, &prfl_sz);
        if (ret < 0)
                goto program_mkex_extr;

        mcam_kex_extr = (struct npc_mcam_kex_extr __force *)mkex_prfl_addr;

        while (((s64)prfl_sz > 0) &&
               (mcam_kex_extr->mkex_sign != MKEX_END_SIGN)) {
                /* Compare with mkex mod_param name string */
                if (mcam_kex_extr->mkex_sign == MKEX_CN20K_SIGN &&
                    !strncmp(mcam_kex_extr->name, mkex_profile,
                             MKEX_NAME_LEN)) {
                        rvu->kpu.mcam_kex_prfl.mkex_extr = mcam_kex_extr;
                        goto program_mkex_extr;
                }

                mcam_kex_extr++;
                prfl_sz -= sizeof(struct npc_mcam_kex_extr);
        }
        dev_warn(dev, "Failed to load requested profile: %s\n", mkex_profile);
        rvu->kpu.mcam_kex_prfl.mkex_extr = npc_mkex_extr_default_get();

program_mkex_extr:
        dev_info(rvu->dev, "Using %s mkex profile\n",
                 rvu->kpu.mcam_kex_prfl.mkex_extr->name);
        /* Program selected mkex profile */
        npc_program_mkex_profile(rvu, blkaddr,
                                 rvu->kpu.mcam_kex_prfl.mkex_extr);
        if (mkex_prfl_addr)
                iounmap(mkex_prfl_addr);
}

int
npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
                            int index, bool enable)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int mcam_idx = index % mcam->banksize;
        int bank = index / mcam->banksize;
        u64 cfg, hw_prio;
        u8 kw_type;

        if (index < 0 || index >= mcam->total_entries)
                return -EINVAL;

        if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
                return -EINVAL;

        if (kw_type == NPC_MCAM_KEY_X2) {
                cfg = rvu_read64(rvu, blkaddr,
                                 NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx,
                                                                   bank));
                hw_prio = cfg & GENMASK_ULL(30, 24);
                cfg = enable ? 1 : 0;
                cfg |= hw_prio;
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
                            cfg);
                goto update_en_map;
        }

        /* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks
         * need to be programmed with the same value.
         */
        for (bank = 0; bank < mcam->banks_per_entry; bank++) {
                cfg = rvu_read64(rvu, blkaddr,
                                 NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx,
                                                                   bank));
                hw_prio = cfg & GENMASK_ULL(30, 24);
                cfg = enable ? 1 : 0;
                cfg |= hw_prio;
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
                            cfg);
        }

update_en_map:
        if (enable)
                set_bit(index, npc_priv->en_map);
        else
                clear_bit(index, npc_priv->en_map);

        return 0;
}

static void
npc_clear_x2_entry(struct rvu *rvu, int blkaddr, int bank, int index)
{
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1),
                    0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 0),
                    0);

        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index, bank, 1), 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index, bank, 0), 0);

        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index, bank, 1), 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index, bank, 0), 0);

        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index, bank, 1), 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index, bank, 0), 0);

        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index, bank, 1), 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index, bank, 0), 0);

        /* Clear corresponding stats register */
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, bank), 0);
}

int
npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int mcam_idx)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int bank = npc_get_bank(mcam, mcam_idx);
        u8 kw_type;
        int index;

        if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &kw_type))
                return -EINVAL;

        index = mcam_idx & (mcam->banksize - 1);

        if (kw_type == NPC_MCAM_KEY_X2) {
                npc_clear_x2_entry(rvu, blkaddr, bank, index);
                return 0;
        }

        /* For NPC_MCAM_KEY_X4 keys, both the banks
         * need to be programmed with the same value.
         */
        for (bank = 0; bank < mcam->banks_per_entry; bank++)
                npc_clear_x2_entry(rvu, blkaddr, bank, index);

        return 0;
}

static void npc_cn20k_get_keyword(struct cn20k_mcam_entry *entry, int idx,
                                  u64 *cam0, u64 *cam1)
{
        u64 kw_mask;

        /* The two banks of every MCAM entry are used as a single double-wide
         * entry that is compared with the search key as follows:
         *
         * NPC_AF_MCAME()_BANK(0)_CAM(0..1)_W0_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW0]
         * NPC_AF_MCAME()_BANK(0)_CAM(0..1)_W1_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW1]
         * NPC_AF_MCAME()_BANK(0)_CAM(0..1)_W2_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW2]
         * NPC_AF_MCAME()_BANK(0)_CAM(0..1)_W3_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW3]
         * NPC_AF_MCAME()_BANK(1)_CAM(0..1)_W0_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW4]
         * NPC_AF_MCAME()_BANK(1)_CAM(0..1)_W1_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW5]
         * NPC_AF_MCAME()_BANK(1)_CAM(0..1)_W2_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW6]
         * NPC_AF_MCAME()_BANK(1)_CAM(0..1)_W3_EXT[MD] ->NPC_MCAM_KEY_X4_S[KW7]
         */
        *cam1 = entry->kw[idx];
        kw_mask = entry->kw_mask[idx];
        *cam1 &= kw_mask;
        *cam0 = ~*cam1 & kw_mask;
}

/*-------------------------------------------------------------------------
 *Kex type|  mcam       |  cam1 |cam0   | req_kwtype||<----- output >     |
 * in     |             |       |       |           ||          |         |
 * profile|  len        |       |       |           ||len       | type    |
 *-------------------------------------------------------------------------
 *X2    |  256 (X2)     |  001b |110b   | 0         ||X2        | X2      |
 *------------------------------------------------------------------------|
 *X4    |  256 (X2)     |  000b |000b   | 0         ||X2        | DYN     |
 *------------------------------------------------------------------------|
 *X4    |  512 (X4)     |  010b |101b   | 0         ||X4        | X4      |
 *------------------------------------------------------------------------|
 *DYN   |  256 (X2)     |  000b |000b   | 0         ||X2        | DYN     |
 *------------------------------------------------------------------------|
 *DYN   |  512 (X4)     |  010b |101b   | 0         ||X4        | X4      |
 *------------------------------------------------------------------------|
 *X4    |  256 (X2)     |  000b |000b   | X2        ||DYN       | DYN     |
 *------------------------------------------------------------------------|
 *DYNC  |  256 (X2)     |  000b |000b   | X2        ||DYN       | DYN     |
 *------------------------------------------------------------------------|
 * X2   |  512 (X4)     |  xxxb |xxxb   | X4        ||INVAL     | INVAL   |
 *------------------------------------------------------------------------|
 */
static void npc_cn20k_config_kw_x2(struct rvu *rvu, struct npc_mcam *mcam,
                                   int blkaddr, int index, u8 intf,
                                   struct cn20k_mcam_entry *entry,
                                   int bank, u8 kw_type, int kw,
                                   u8 req_kw_type)
{
        u64 intf_ext = 0, intf_ext_mask = 0;
        u8 tx_intf_mask = ~intf & 0x3;
        u8 tx_intf = intf, kex_type;
        u8 kw_type_mask = ~kw_type;
        u64 cam0, cam1, kex_cfg;

        if (is_npc_intf_tx(intf)) {
                /* Last bit must be set and rest don't care
                 * for TX interfaces
                 */
                tx_intf_mask = 0x1;
                tx_intf = intf & tx_intf_mask;
                tx_intf_mask = ~tx_intf & tx_intf_mask;
        }

        kex_cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(intf));
        kex_type = (kex_cfg & GENMASK_ULL(34, 32)) >> 32;
        if ((kex_type == NPC_MCAM_KEY_DYN || kex_type == NPC_MCAM_KEY_X4) &&
            kw_type == NPC_MCAM_KEY_X2) {
                kw_type = 0;
                kw_type_mask = 0;
        }

        /* Say, we need to write x2 keyword in an x4 subbank.
         * req_kw_type will be x2, and kw_type will be x4.
         * So in the case ignore kw bits in mcam.
         */
        if (kw_type == NPC_MCAM_KEY_X4 && req_kw_type == NPC_MCAM_KEY_X2) {
                kw_type = 0;
                kw_type_mask = 0;
        }

        intf_ext = ((u64)kw_type << 16) | tx_intf;
        intf_ext_mask = (((u64)kw_type_mask  << 16) & GENMASK_ULL(18, 16)) |
                tx_intf_mask;
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1),
                    intf_ext);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 0),
                    intf_ext_mask);

        /* Set the match key */
        npc_cn20k_get_keyword(entry, kw, &cam0, &cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index, bank, 1),
                    cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index, bank, 0),
                    cam0);

        npc_cn20k_get_keyword(entry, kw + 1, &cam0, &cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index, bank, 1),
                    cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index, bank, 0),
                    cam0);

        npc_cn20k_get_keyword(entry, kw + 2, &cam0, &cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index, bank, 1),
                    cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index, bank, 0),
                    cam0);

        npc_cn20k_get_keyword(entry, kw + 3, &cam0, &cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index, bank, 1),
                    cam1);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index, bank, 0),
                    cam0);
}

static void npc_cn20k_config_kw_x4(struct rvu *rvu, struct npc_mcam *mcam,
                                   int blkaddr, int index, u8 intf,
                                   struct cn20k_mcam_entry *entry,
                                   u8 kw_type, u8 req_kw_type)
{
        int kw = 0, bank;

        for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4)
                npc_cn20k_config_kw_x2(rvu, mcam, blkaddr,
                                       index, intf,
                                       entry, bank, kw_type,
                                       kw, req_kw_type);
}

int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
                                u8 intf, struct cn20k_mcam_entry *entry,
                                bool enable, u8 hw_prio, u8 req_kw_type)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int mcam_idx = index % mcam->banksize;
        int bank = index / mcam->banksize;
        u64 bank_cfg = (u64)hw_prio << 24;
        int kw = 0;
        u8 kw_type;

        if (index < 0 || index >= mcam->total_entries)
                return -EINVAL;

        if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
                return -EINVAL;

        /* Disable before mcam entry update */
        if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false))
                return -EINVAL;

        /* CAM1 takes the comparison value and
         * CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'.
         * CAM1<n> = 0 & CAM0<n> = 1 => match if key<n> = 0
         * CAM1<n> = 1 & CAM0<n> = 0 => match if key<n> = 1
         * CAM1<n> = 0 & CAM0<n> = 0 => always match i.e dontcare.
         */
        if (kw_type == NPC_MCAM_KEY_X2) {
                /* Clear mcam entry to avoid writes being suppressed by NPC */
                npc_clear_x2_entry(rvu, blkaddr, bank, mcam_idx);
                npc_cn20k_config_kw_x2(rvu, mcam, blkaddr,
                                       mcam_idx, intf, entry,
                                       bank, kw_type, kw, req_kw_type);
                /* Set 'action' */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                  bank, 0),
                            entry->action);

                /* Set 'action2' for inline receive */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                  bank, 2),
                            entry->action2);

                /* Set TAG 'action' */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                  bank, 1),
                            entry->vtag_action);

                /* Set HW priority */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
                            bank_cfg);

        } else {
                /* Clear mcam entry to avoid writes being suppressed by NPC */
                npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx);
                npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx);

                npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
                                       mcam_idx, intf, entry,
                                       kw_type, req_kw_type);
                for (bank = 0; bank < mcam->banks_per_entry; bank++) {
                        /* Set 'action' */
                        rvu_write64(rvu, blkaddr,
                                    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                          bank, 0),
                                    entry->action);

                        /* Set TAG 'action' */
                        rvu_write64(rvu, blkaddr,
                                    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                          bank, 1),
                                    entry->vtag_action);

                        /* Set 'action2' for inline receive */
                        rvu_write64(rvu, blkaddr,
                                    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
                                                                          bank, 2),
                                    entry->action2);

                        /* Set HW priority */
                        rvu_write64(rvu, blkaddr,
                                    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
                                    bank_cfg);
                }
        }

        /* TODO: */
        /* PF installing VF rule */
        if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable))
                return -EINVAL;

        return 0;
}

int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u64 cfg, sreg, dreg, soff, doff;
        u8 src_kwtype, dest_kwtype;
        int bank, i, sb, db;
        int dbank, sbank;

        if (src >= mcam->total_entries || dest >= mcam->total_entries)
                return -EINVAL;

        dbank = npc_get_bank(mcam, dest);
        sbank = npc_get_bank(mcam, src);

        if (npc_mcam_idx_2_key_type(rvu, src, &src_kwtype))
                return -EINVAL;

        if (npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype))
                return -EINVAL;

        if (src_kwtype != dest_kwtype)
                return -EINVAL;

        src &= (mcam->banksize - 1);
        dest &= (mcam->banksize - 1);

        /* Copy INTF's, W0's, W1's, W2's, W3s CAM0 and CAM1 configuration */
        for (bank = 0; bank < mcam->banks_per_entry; bank++) {
                sb = sbank + bank;
                sreg = NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(src, sb, 0);
                db = dbank + bank;
                dreg = NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(dest, db, 0);
                for (i = 0; i < 10; i++) {
                        cfg = rvu_read64(rvu, blkaddr, sreg + (i * 8));
                        rvu_write64(rvu, blkaddr, dreg + (i * 8), cfg);
                }

                /* Copy action */
                for (i = 0; i < 3; i++) {
                        soff = NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(src,
                                                                     sb, i);
                        cfg = rvu_read64(rvu, blkaddr, soff);

                        doff = NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(dest, db,
                                                                     i);
                        rvu_write64(rvu, blkaddr, doff, cfg);
                }

                /* Copy bank configuration */
                cfg = rvu_read64(rvu, blkaddr,
                                 NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(src, sb));
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(dest, db), cfg);
                if (src_kwtype == NPC_MCAM_KEY_X2)
                        break;
        }

        return 0;
}

static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx,
                                     u64 cam0, u64 cam1)
{
        entry->kw[idx] = cam1;
        entry->kw_mask[idx] = cam1 ^ cam0;
}

int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
                              struct cn20k_mcam_entry *entry,
                              u8 *intf, u8 *ena, u8 *hw_prio)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u64 cam0, cam1, bank_cfg, cfg;
        int kw = 0, bank;
        u8 kw_type;

        if (index >= mcam->total_entries)
                return -EINVAL;

        if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
                return -EINVAL;

        bank = npc_get_bank(mcam, index);
        index &= (mcam->banksize - 1);

        cfg = rvu_read64(rvu, blkaddr,
                         NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 0));
        entry->action = cfg;

        cfg = rvu_read64(rvu, blkaddr,
                         NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 1));
        entry->vtag_action = cfg;

        cfg = rvu_read64(rvu, blkaddr,
                         NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 2));
        entry->action2 = cfg;

        cfg = rvu_read64(rvu, blkaddr,
                         NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index,
                                                                 bank, 1)) & 3;
        *intf = cfg;

        bank_cfg = rvu_read64(rvu, blkaddr,
                              NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(index, bank));
        *ena = bank_cfg & 0x1;
        *hw_prio = (bank_cfg & GENMASK_ULL(30, 24)) >> 24;
        if (kw_type == NPC_MCAM_KEY_X2) {
                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 1, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 2, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
                return 0;
        }

        for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4) {
                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W0_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W1_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 1, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W2_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 2, cam0, cam1);

                cam1 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index,
                                                                        bank,
                                                                        1));
                cam0 = rvu_read64(rvu, blkaddr,
                                  NPC_AF_CN20K_MCAMEX_BANKX_CAMX_W3_EXT(index,
                                                                        bank,
                                                                        0));
                npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
        }

        return 0;
}

int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu,
                                                struct npc_cn20k_mcam_write_entry_req *req,
                                                struct msg_rsp *rsp)
{
        struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc);
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u16 pcifunc = req->hdr.pcifunc;
        int blkaddr, rc;
        u8 nix_intf;

        req->entry = npc_cn20k_vidx2idx(req->entry);

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return NPC_MCAM_INVALID_REQ;

        mutex_lock(&mcam->lock);
        rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
        if (rc)
                goto exit;

        if (!is_npc_interface_valid(rvu, req->intf)) {
                rc = NPC_MCAM_INVALID_REQ;
                goto exit;
        }

        if (is_npc_intf_tx(req->intf))
                nix_intf = pfvf->nix_tx_intf;
        else
                nix_intf = pfvf->nix_rx_intf;

        /* For AF installed rules, the nix_intf should be set to target NIX */
        if (is_pffunc_af(req->hdr.pcifunc))
                nix_intf = req->intf;

        rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf,
                                         &req->entry_data, req->enable_entry,
                                         req->hw_prio, req->req_kw_type);

exit:
        mutex_unlock(&mcam->lock);
        return rc;
}

int rvu_mbox_handler_npc_cn20k_mcam_read_entry(struct rvu *rvu,
                                               struct npc_mcam_read_entry_req *req,
                                               struct npc_cn20k_mcam_read_entry_rsp *rsp)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u16 pcifunc = req->hdr.pcifunc;
        int blkaddr, rc;

        req->entry = npc_cn20k_vidx2idx(req->entry);

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return NPC_MCAM_INVALID_REQ;

        mutex_lock(&mcam->lock);
        rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
        if (rc)
                goto fail;

        rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry,
                                       &rsp->entry_data, &rsp->intf,
                                       &rsp->enable, &rsp->hw_prio);
fail:
        mutex_unlock(&mcam->lock);
        return rc;
}

int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu,
                                                          struct npc_cn20k_mcam_alloc_and_write_entry_req *req,
                                                          struct npc_mcam_alloc_and_write_entry_rsp *rsp)
{
        struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc);
        struct npc_mcam_free_entry_req free_req = { 0 };
        struct npc_mcam_alloc_entry_req entry_req;
        struct npc_mcam_alloc_entry_rsp entry_rsp;
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u16 entry = NPC_MCAM_ENTRY_INVALID;
        struct msg_rsp free_rsp;
        int blkaddr, rc, err;
        u8 nix_intf;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return NPC_MCAM_INVALID_REQ;

        if (!is_npc_interface_valid(rvu, req->intf))
                return NPC_MCAM_INVALID_REQ;

        /* Try to allocate a MCAM entry */
        entry_req.hdr.pcifunc = req->hdr.pcifunc;
        entry_req.contig = true;
        entry_req.ref_prio = req->ref_prio;
        entry_req.ref_entry = req->ref_entry;
        entry_req.count = 1;
        entry_req.virt = req->virt;

        rc = rvu_mbox_handler_npc_mcam_alloc_entry(rvu,
                                                   &entry_req, &entry_rsp);
        if (rc)
                return rc;

        if (!entry_rsp.count)
                return NPC_MCAM_ALLOC_FAILED;

        /* entry_req.count is 1, so single entry is allocated */
        entry = npc_cn20k_vidx2idx(entry_rsp.entry);

        mutex_lock(&mcam->lock);

        if (is_npc_intf_tx(req->intf))
                nix_intf = pfvf->nix_tx_intf;
        else
                nix_intf = pfvf->nix_rx_intf;

        rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
                                         &req->entry_data, req->enable_entry,
                                         req->hw_prio, req->req_kw_type);

        mutex_unlock(&mcam->lock);

        if (rc) {
                free_req.hdr.pcifunc = req->hdr.pcifunc;
                free_req.entry = entry_rsp.entry;
                err = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &free_rsp);
                if (err)
                        dev_err(rvu->dev,
                                "%s: Error to free mcam idx %u\n",
                                __func__, entry_rsp.entry);
                return rc;
        }

        rsp->entry = entry_rsp.entry;
        return 0;
}

static int rvu_npc_get_base_steer_rule_type(struct rvu *rvu, u16 pcifunc)
{
        if (is_lbk_vf(rvu, pcifunc))
                return NIXLF_PROMISC_ENTRY;

        return NIXLF_UCAST_ENTRY;
}

int rvu_mbox_handler_npc_cn20k_read_base_steer_rule(struct rvu *rvu,
                                                    struct msg_req *req,
                                                    struct npc_cn20k_mcam_read_base_rule_rsp *rsp)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int index, blkaddr, nixlf, rc = 0;
        u16 pcifunc = req->hdr.pcifunc;
        u8 intf, enable, hw_prio;
        struct rvu_pfvf *pfvf;
        int rl_type;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return NPC_MCAM_INVALID_REQ;

        /* Return the channel number in case of PF */
        if (!(pcifunc & RVU_PFVF_FUNC_MASK)) {
                pfvf = rvu_get_pfvf(rvu, pcifunc);
                rsp->entry.kw[0] = pfvf->rx_chan_base;
                rsp->entry.kw_mask[0] = 0xFFFULL;
                goto out;
        }

        /* Find the pkt steering rule installed by PF to this VF */
        mutex_lock(&mcam->lock);
        for (index = 0; index < mcam->bmap_entries; index++) {
                if (mcam->entry2target_pffunc[index] == pcifunc)
                        goto read_entry;
        }

        rc = nix_get_nixlf(rvu, pcifunc, &nixlf, NULL);
        if (rc < 0) {
                mutex_unlock(&mcam->lock);
                goto out;
        }

        rl_type = rvu_npc_get_base_steer_rule_type(rvu, pcifunc);

        /* Read the default ucast entry if there is no pkt steering rule */
        index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, rl_type);
        if (index < 0) {
                mutex_unlock(&mcam->lock);
                goto out;
        }

read_entry:
        /* Read the mcam entry */
        rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, index,
                                       &rsp->entry, &intf,
                                       &enable, &hw_prio);
        mutex_unlock(&mcam->lock);
out:
        return rc;
}

static u8 npc_map2cn20k_flag(u8 flag)
{
        switch (flag) {
        case NPC_F_LC_U_IP_FRAG:
                return NPC_CN20K_F_LC_L_IP_FRAG;

        case NPC_F_LC_U_IP6_FRAG:
                return NPC_CN20K_F_LC_L_IP6_FRAG;

        case NPC_F_LC_L_6TO4:
                return NPC_CN20K_F_LC_L_6TO4;

        case NPC_F_LC_L_MPLS_IN_IP:
                return NPC_CN20K_F_LC_U_MPLS_IN_IP;

        case NPC_F_LC_L_IP6_TUN_IP6:
                return NPC_CN20K_F_LC_U_IP6_TUN_IP6;

        case NPC_F_LC_L_IP6_MPLS_IN_IP:
                return NPC_CN20K_F_LC_U_IP6_MPLS_IN_IP;

        default:
                break;
        }

        WARN(1, "%s: Invalid flag=%u\n", __func__, flag);
        return 0xff;
}

static void npc_cn20k_translate_action_flags(struct npc_kpu_profile_action *act)
{
        u8 ltype, val;

        if (act->lid != NPC_LID_LC)
                return;

        ltype = act->ltype;
        if (ltype != NPC_LT_LC_IP &&
            ltype != NPC_LT_LC_IP6 &&
            ltype != NPC_LT_LC_IP_OPT &&
            ltype != NPC_LT_LC_IP6_EXT)
                return;

        switch (act->flags) {
        case NPC_F_LC_U_IP_FRAG:
        case NPC_F_LC_U_IP6_FRAG:
        case NPC_F_LC_L_6TO4:
        case NPC_F_LC_L_MPLS_IN_IP:
        case NPC_F_LC_L_IP6_TUN_IP6:
        case NPC_F_LC_L_IP6_MPLS_IN_IP:
                val = npc_map2cn20k_flag(act->flags);
                if (val != 0xFF)
                        act->flags = val;
                break;
        default:
                break;
        }
}

void
npc_cn20k_update_action_entries_n_flags(struct rvu *rvu,
                                        struct npc_kpu_profile_adapter *pfl)
{
        struct npc_kpu_profile_action *action;
        int entries;

        for (int i = 0; i < pfl->kpus; i++) {
                action = pfl->kpu[i].action;
                entries = pfl->kpu[i].action_entries;

                for (int j = 0; j < entries; j++)
                        npc_cn20k_translate_action_flags(&action[j]);
        }
}

int npc_cn20k_apply_custom_kpu(struct rvu *rvu,
                               struct npc_kpu_profile_adapter *profile)
{
        const struct npc_cn20k_kpu_profile_fwdata *fw = rvu->kpu_fwdata;
        size_t hdr_sz = sizeof(struct npc_cn20k_kpu_profile_fwdata);
        struct npc_kpu_profile_action *action;
        struct npc_kpu_profile_cam *cam;
        struct npc_kpu_fwdata *fw_kpu;
        size_t offset = 0;
        u16 kpu, entry;
        int entries;

        hdr_sz = sizeof(struct npc_cn20k_kpu_profile_fwdata);

        if (rvu->kpu_fwdata_sz < hdr_sz) {
                dev_warn(rvu->dev, "Invalid KPU profile size\n");
                return -EINVAL;
        }

        if (le64_to_cpu(fw->signature) != KPU_SIGN) {
                dev_warn(rvu->dev, "Invalid KPU profile signature %llx\n",
                         fw->signature);
                return -EINVAL;
        }

        /* Verify if the using known profile structure */
        if (NPC_KPU_VER_MAJ(profile->version) >
            NPC_KPU_VER_MAJ(NPC_KPU_PROFILE_VER)) {
                dev_warn(rvu->dev, "Not supported Major version: %d > %d\n",
                         NPC_KPU_VER_MAJ(profile->version),
                         NPC_KPU_VER_MAJ(NPC_KPU_PROFILE_VER));
                return -EINVAL;
        }

        /* Verify if profile is aligned with the required kernel changes */
        if (NPC_KPU_VER_MIN(profile->version) <
            NPC_KPU_VER_MIN(NPC_KPU_PROFILE_VER)) {
                dev_warn(rvu->dev,
                         "Invalid KPU profile version: %d.%d.%d expected version <= %d.%d.%d\n",
                         NPC_KPU_VER_MAJ(profile->version),
                         NPC_KPU_VER_MIN(profile->version),
                         NPC_KPU_VER_PATCH(profile->version),
                         NPC_KPU_VER_MAJ(NPC_KPU_PROFILE_VER),
                         NPC_KPU_VER_MIN(NPC_KPU_PROFILE_VER),
                         NPC_KPU_VER_PATCH(NPC_KPU_PROFILE_VER));
                return -EINVAL;
        }

        /* Verify if profile fits the HW */
        if (fw->kpus > rvu->hw->npc_kpus) {
                dev_warn(rvu->dev, "Not enough KPUs: %d > %d\n", fw->kpus,
                         rvu->hw->npc_kpus);
                return -EINVAL;
        }

        /* Check if there is enough memory */
        if (fw->kpus > profile->kpus) {
                dev_warn(rvu->dev, "Not enough KPUs: %d > %zu\n", fw->kpus,
                         profile->kpus);
                return -EINVAL;
        }

        profile->mcam_kex_prfl.mkex_extr = &fw->mkex;
        if (profile->mcam_kex_prfl.mkex_extr->mkex_sign != MKEX_CN20K_SIGN) {
                dev_warn(rvu->dev, "Invalid MKEX profile signature:%llx\n",
                         profile->mcam_kex_prfl.mkex_extr->mkex_sign);
                return -EINVAL;
        }

        profile->custom = 1;
        profile->name = fw->name;
        profile->version = le64_to_cpu(fw->version);
        profile->lt_def = &fw->lt_def;

        for (kpu = 0; kpu < fw->kpus; kpu++) {
                fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset);
                if (fw_kpu->entries > KPU_CN20K_MAX_CST_ENT)
                        dev_warn(rvu->dev,
                                 "Too many custom entries on KPU%d: %d > %d\n",
                                 kpu, fw_kpu->entries, KPU_CN20K_MAX_CST_ENT);
                entries = min(fw_kpu->entries, KPU_CN20K_MAX_CST_ENT);
                cam = (struct npc_kpu_profile_cam *)fw_kpu->data;
                offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam);
                action = (struct npc_kpu_profile_action *)(fw->data + offset);
                offset += fw_kpu->entries * sizeof(*action);
                if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
                        dev_warn(rvu->dev,
                                 "Profile size mismatch on KPU%i parsing.\n",
                                 kpu + 1);
                        return -EINVAL;
                }

                for (entry = 0; entry < entries; entry++) {
                        profile->kpu[kpu].cam[entry] = cam[entry];
                        profile->kpu[kpu].action[entry] = action[entry];
                        npc_cn20k_translate_action_flags(&profile->kpu[kpu].action[entry]);
                }
        }

        return 0;
}

int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type)
{
        struct npc_subbank *sb;
        int bank_off, sb_id;

        /* mcam_idx should be less than (2 * bank depth) */
        if (mcam_idx >= npc_priv->bank_depth * 2) {
                dev_err(rvu->dev, "%s: bad params\n",
                        __func__);
                return -EINVAL;
        }

        /* find mcam offset per bank */
        bank_off = mcam_idx & (npc_priv->bank_depth - 1);

        /* Find subbank id */
        sb_id = bank_off / npc_priv->subbank_depth;

        /* Check if subbank id is more than maximum
         * number of subbanks available
         */
        if (sb_id >= npc_priv->num_subbanks) {
                dev_err(rvu->dev, "%s: invalid subbank %d\n",
                        __func__, sb_id);
                return -EINVAL;
        }

        sb = &npc_priv->sb[sb_id];

        *key_type = sb->key_type;

        return 0;
}

static int npc_subbank_idx_2_mcam_idx(struct rvu *rvu, struct npc_subbank *sb,
                                      u16 sub_off, u16 *mcam_idx)
{
        int off, bot;

        /* for x4 section, maximum allowed subbank index =
         * subsection depth - 1
         */
        if (sb->key_type == NPC_MCAM_KEY_X4 &&
            sub_off >= npc_priv->subbank_depth) {
                dev_err(rvu->dev,
                        "%s: Failed to get mcam idx (x4) sb->idx=%u sub_off=%u",
                        __func__, sb->idx, sub_off);
                return -EINVAL;
        }

        /* for x2 section, maximum allowed subbank index =
         * 2 * subsection depth - 1
         */
        if (sb->key_type == NPC_MCAM_KEY_X2 &&
            sub_off >= npc_priv->subbank_depth * 2) {
                dev_err(rvu->dev,
                        "%s: Failed to get mcam idx (x2) sb->idx=%u sub_off=%u",
                        __func__, sb->idx, sub_off);
                return -EINVAL;
        }

        /* Find subbank offset from respective subbank (w.r.t bank) */
        off = sub_off & (npc_priv->subbank_depth - 1);

        /* if subsection idx is in bank1, add bank depth,
         * which is part of sb->b1b
         */
        bot = sub_off >= npc_priv->subbank_depth ? sb->b1b : sb->b0b;

        *mcam_idx = bot + off;
        return 0;
}

int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx,
                               struct npc_subbank **sb,
                               int *sb_off)
{
        int bank_off, sb_id;

        /* mcam_idx should be less than (2 * bank depth) */
        if (mcam_idx >= npc_priv->bank_depth * 2) {
                dev_err(rvu->dev, "%s: Invalid mcam idx %u\n",
                        __func__, mcam_idx);
                return -EINVAL;
        }

        /* find mcam offset per bank */
        bank_off = mcam_idx & (npc_priv->bank_depth - 1);

        /* Find subbank id */
        sb_id = bank_off / npc_priv->subbank_depth;

        /* Check if subbank id is more than maximum
         * number of subbanks available
         */
        if (sb_id >= npc_priv->num_subbanks) {
                dev_err(rvu->dev, "%s: invalid subbank %d\n",
                        __func__, sb_id);
                return -EINVAL;
        }

        *sb = &npc_priv->sb[sb_id];

        /* Subbank offset per bank */
        *sb_off = bank_off % npc_priv->subbank_depth;

        /* Index in a subbank should add subbank depth
         * if it is in bank1
         */
        if (mcam_idx >= npc_priv->bank_depth)
                *sb_off += npc_priv->subbank_depth;

        return 0;
}

static int __npc_subbank_contig_alloc(struct rvu *rvu,
                                      struct npc_subbank *sb,
                                      int key_type, int sidx,
                                      int eidx, int prio,
                                      int count, int t, int b,
                                      unsigned long *bmap,
                                      u16 *save)
{
        int k, offset, delta = 0;
        int cnt = 0, sbd;

        sbd = npc_priv->subbank_depth;

        if (sidx >= npc_priv->bank_depth)
                delta = sbd;

        switch (prio) {
        case NPC_MCAM_LOWER_PRIO:
        case NPC_MCAM_ANY_PRIO:
                /* Find an area of size 'count' from sidx to eidx */
                offset = bitmap_find_next_zero_area(bmap, sbd, sidx - b,
                                                    count, 0);

                if (offset >= sbd) {
                        dev_err(rvu->dev,
                                "%s: Could not find contiguous(%d) entries\n",
                                __func__, count);
                        return -EFAULT;
                }

                dev_dbg(rvu->dev,
                        "%s: sidx=%d eidx=%d t=%d b=%d offset=%d count=%d delta=%d\n",
                        __func__, sidx, eidx, t, b, offset,
                        count, delta);

                for (cnt = 0; cnt < count; cnt++)
                        save[cnt] = offset + cnt + delta;

                break;

        case NPC_MCAM_HIGHER_PRIO:
                /* Find an area of 'count' from eidx to sidx */
                for (k = eidx - b; cnt < count && k >= (sidx - b); k--) {
                        /* If an intermediate slot is not free,
                         * reset the counter (cnt) to zero as
                         * request is for contiguous.
                         */
                        if (test_bit(k, bmap)) {
                                cnt = 0;
                                continue;
                        }

                        save[cnt++] = k + delta;
                }
                break;
        }

        /* Found 'count' number of free slots */
        if (cnt == count)
                return 0;

        dev_dbg(rvu->dev,
                "%s: Could not find contiguous(%d) entries in subbank=%u\n",
                __func__, count, sb->idx);
        return -EFAULT;
}

static int __npc_subbank_non_contig_alloc(struct rvu *rvu,
                                          struct npc_subbank *sb,
                                          int key_type, int sidx,
                                          int eidx, int prio,
                                          int t, int b,
                                          unsigned long *bmap,
                                          int count, u16 *save,
                                          bool max_alloc, int *alloc_cnt)
{
        unsigned long index;
        int cnt = 0, delta;
        int k, sbd;

        sbd = npc_priv->subbank_depth;
        delta = sidx >= npc_priv->bank_depth ? sbd : 0;

        switch (prio) {
                /* Find an area of size 'count' from sidx to eidx */
        case NPC_MCAM_LOWER_PRIO:
        case NPC_MCAM_ANY_PRIO:
                index = find_next_zero_bit(bmap, sbd, sidx - b);
                if (index >= sbd) {
                        dev_err(rvu->dev,
                                "%s: Error happened to alloc %u, bitmap_weight=%u, sb->idx=%u\n",
                                __func__, count,
                                bitmap_weight(bmap, sbd),
                                sb->idx);
                        break;
                }

                for (k = index; cnt < count && k <= (eidx - b); k++) {
                        /* Skip used slots */
                        if (test_bit(k, bmap))
                                continue;

                        save[cnt++] = k + delta;
                }
                break;

                /* Find an area of 'count' from eidx to sidx */
        case NPC_MCAM_HIGHER_PRIO:
                for (k = eidx - b; cnt < count && k >= (sidx - b); k--) {
                        /* Skip used slots */
                        if (test_bit(k, bmap))
                                continue;

                        save[cnt++] = k + delta;
                }
                break;
        }

        /* Update allocated 'cnt' to alloc_cnt */
        *alloc_cnt = cnt;

        /* Successfully allocated requested count slots */
        if (cnt == count)
                return 0;

        /* Allocation successful for cnt < count */
        if (max_alloc && cnt > 0)
                return 0;

        dev_dbg(rvu->dev,
                "%s: Could not find non contiguous entries(%u) in subbank(%u) cnt=%d max_alloc=%d\n",
                __func__, count, sb->idx, cnt, max_alloc);

        return -EFAULT;
}

static void __npc_subbank_sboff_2_off(struct rvu *rvu, struct npc_subbank *sb,
                                      int sb_off, unsigned long **bmap,
                                      int *off)
{
        int sbd;

        sbd = npc_priv->subbank_depth;

        *off = sb_off & (sbd - 1);
        *bmap = (sb_off >= sbd) ? sb->b1map : sb->b0map;
}

/* set/clear bitmap */
static bool __npc_subbank_mark_slot(struct rvu *rvu,
                                    struct npc_subbank *sb,
                                    int sb_off, bool set)
{
        unsigned long *bmap;
        int off;

        /* if sb_off >= subbank.depth, then slots are in
         * bank1
         */
        __npc_subbank_sboff_2_off(rvu, sb, sb_off, &bmap, &off);

        dev_dbg(rvu->dev,
                "%s: Marking set=%d sb_off=%d sb->idx=%d off=%d\n",
                __func__, set, sb_off, sb->idx, off);

        if (set) {
                /* Slot is already used */
                if (test_bit(off, bmap))
                        return false;

                sb->free_cnt--;
                set_bit(off, bmap);
                return true;
        }

        /* Slot is already free */
        if (!test_bit(off, bmap))
                return false;

        sb->free_cnt++;
        clear_bit(off, bmap);
        return true;
}

static int __npc_subbank_mark_free(struct rvu *rvu, struct npc_subbank *sb)
{
        int rc, blkaddr;

        sb->flags = NPC_SUBBANK_FLAG_FREE;
        sb->key_type = 0;

        bitmap_clear(sb->b0map, 0, npc_priv->subbank_depth);
        bitmap_clear(sb->b1map, 0, npc_priv->subbank_depth);

        if (!xa_erase(&npc_priv->xa_sb_used, sb->arr_idx)) {
                dev_err(rvu->dev,
                        "%s: Error to delete from xa_sb_used array\n",
                        __func__);
                return -EFAULT;
        }

        rc = xa_insert(&npc_priv->xa_sb_free, sb->arr_idx,
                       xa_mk_value(sb->idx), GFP_KERNEL);
        if (rc) {
                rc = xa_insert(&npc_priv->xa_sb_used, sb->arr_idx,
                               xa_mk_value(sb->idx), GFP_KERNEL);
                if (rc)
                        dev_err(rvu->dev,
                                "%s: Failed to roll back sb(%u) arr_idx=%d\n",
                                __func__, sb->idx, sb->arr_idx);

                dev_err(rvu->dev,
                        "%s: Error to add sb(%u) to xa_sb_free array at arr_idx=%d\n",
                        __func__, sb->idx, sb->arr_idx);
                return rc;
        }

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        rvu_write64(rvu, blkaddr,
                    NPC_AF_MCAM_SECTIONX_CFG_EXT(sb->idx),
                    NPC_MCAM_KEY_X2);

        return rc;
}

static int __npc_subbank_mark_used(struct rvu *rvu, struct npc_subbank *sb,
                                   int key_type)
{
        int rc;

        sb->flags = NPC_SUBBANK_FLAG_USED;
        sb->key_type = key_type;
        if (key_type == NPC_MCAM_KEY_X4)
                sb->free_cnt = npc_priv->subbank_depth;
        else
                sb->free_cnt = 2 * npc_priv->subbank_depth;

        bitmap_clear(sb->b0map, 0, npc_priv->subbank_depth);
        bitmap_clear(sb->b1map, 0, npc_priv->subbank_depth);

        if (!xa_erase(&npc_priv->xa_sb_free, sb->arr_idx)) {
                dev_err(rvu->dev,
                        "%s: Error to delete from xa_sb_free array\n",
                        __func__);
                return -EFAULT;
        }

        rc = xa_insert(&npc_priv->xa_sb_used, sb->arr_idx,
                       xa_mk_value(sb->idx), GFP_KERNEL);
        if (rc)
                dev_err(rvu->dev,
                        "%s: Error to add to xa_sb_used array\n", __func__);

        return rc;
}

static bool __npc_subbank_free(struct rvu *rvu, struct npc_subbank *sb,
                               u16 sb_off)
{
        bool deleted = false;
        unsigned long *bmap;
        int rc, off;

        deleted = __npc_subbank_mark_slot(rvu, sb, sb_off, false);
        if (!deleted)
                goto done;

        __npc_subbank_sboff_2_off(rvu, sb, sb_off, &bmap, &off);

        /* Check whether we can mark whole subbank as free */
        if (sb->key_type == NPC_MCAM_KEY_X4) {
                if (sb->free_cnt < npc_priv->subbank_depth)
                        goto done;
        } else {
                if (sb->free_cnt < 2 * npc_priv->subbank_depth)
                        goto done;
        }

        /* All slots in subbank are unused. Mark the subbank as free
         * and add to free pool
         */
        rc = __npc_subbank_mark_free(rvu, sb);
        if (rc)
                dev_err(rvu->dev, "%s: Error to free subbank\n", __func__);

done:
        return deleted;
}

static int
npc_subbank_free(struct rvu *rvu, struct npc_subbank *sb, u16 sb_off)
{
        bool deleted;

        mutex_lock(&sb->lock);
        deleted = __npc_subbank_free(rvu, sb, sb_off);
        mutex_unlock(&sb->lock);

        return deleted ? 0 : -EFAULT;
}

static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb,
                               int key_type, int ref, int limit, int prio,
                               bool contig, int count, u16 *mcam_idx,
                               int idx_sz, bool max_alloc, int *alloc_cnt)
{
        int cnt, t, b, i, blkaddr;
        bool new_sub_bank = false;
        unsigned long *bmap;
        u16 *save = NULL;
        int sidx, eidx;
        bool diffbank;
        int bw, bfree;
        int rc = 0;
        bool ret;

        /* Check if enough space is there to return requested number of
         * mcam indexes in case of contiguous allocation
         */
        if (!max_alloc && count > idx_sz) {
                dev_err(rvu->dev,
                        "%s: Less space, count=%d idx_sz=%d sb_id=%d\n",
                        __func__, count, idx_sz, sb->idx);
                return -ENOSPC;
        }

        /* Allocation on multiple subbank is not supported by this function.
         * it means that ref and limit should be on same subbank.
         *
         * ref and limit values should be validated w.r.t prio as below.
         * say ref = 100, limit = 200,
         * if NPC_MCAM_LOWER_PRIO, allocate index 100
         * if NPC_MCAM_HIGHER_PRIO, below sanity test returns error.
         * if NPC_MCAM_ANY_PRIO, allocate index 100
         *
         * say ref = 200, limit = 100
         * if NPC_MCAM_LOWER_PRIO, below sanity test returns error.
         * if NPC_MCAM_HIGHER_PRIO, allocate index 200
         * if NPC_MCAM_ANY_PRIO, allocate index 100
         *
         * Please note that NPC_MCAM_ANY_PRIO does not have any restriction
         * on "ref" and "limit" values. ie, ref > limit and limit > ref
         * are valid cases.
         */
        if ((prio == NPC_MCAM_LOWER_PRIO && ref > limit) ||
            (prio == NPC_MCAM_HIGHER_PRIO && ref < limit)) {
                dev_err(rvu->dev, "%s: Wrong ref_enty(%d) or limit(%d)\n",
                        __func__, ref, limit);
                return -EINVAL;
        }

        /* x4 indexes are from 0 to bank size as it combines two x2 banks */
        if (key_type == NPC_MCAM_KEY_X4 &&
            (ref >= npc_priv->bank_depth || limit >= npc_priv->bank_depth)) {
                dev_err(rvu->dev,
                        "%s: Wrong ref_enty(%d) or limit(%d) for x4\n",
                        __func__, ref, limit);
                return -EINVAL;
        }

        /* This function is called either bank0 or bank1 portion of a subbank.
         * so ref and limit should be on same bank.
         */
        diffbank = !!((ref & npc_priv->bank_depth) ^
                      (limit & npc_priv->bank_depth));
        if (diffbank) {
                dev_err(rvu->dev,
                        "%s: request ref and limit should be from same bank\n",
                        __func__);
                return -EINVAL;
        }

        sidx = min_t(int, limit, ref);
        eidx = max_t(int, limit, ref);

        /* Find total number of slots available; both used and free */
        cnt = eidx - sidx + 1;
        if (contig && cnt < count) {
                dev_err(rvu->dev,
                        "%s: Wrong ref_enty(%d) or limit(%d) for count(%d)\n",
                        __func__, ref, limit, count);
                return -EINVAL;
        }

        /* If subbank is free, check if requested number of indexes is less than
         * or equal to mcam entries available in the subbank if contig.
         */
        if (sb->flags & NPC_SUBBANK_FLAG_FREE) {
                if (contig && count > npc_priv->subbank_depth) {
                        dev_err(rvu->dev, "%s: Less number of entries\n",
                                __func__);
                        return -ENOSPC;
                }

                new_sub_bank = true;
                goto process;
        }

        /* Flag should be set for all used subbanks */
        WARN_ONCE(!(sb->flags & NPC_SUBBANK_FLAG_USED),
                  "Used flag is not set(%#x)\n", sb->flags);

        /* If subbank key type does not match with requested key_type,
         * return error
         */
        if (sb->key_type != key_type) {
                dev_dbg(rvu->dev, "%s: subbank key_type mismatch\n", __func__);
                return -EINVAL;
        }

process:
        /* if ref or limit >= npc_priv->bank_depth, index are in bank1.
         * else bank0.
         */
        if (ref >= npc_priv->bank_depth) {
                bmap = sb->b1map;
                t = sb->b1t;
                b = sb->b1b;
        } else {
                bmap = sb->b0map;
                t = sb->b0t;
                b = sb->b0b;
        }

        /* Calculate free slots */
        bw = bitmap_weight(bmap, npc_priv->subbank_depth);
        bfree = npc_priv->subbank_depth - bw;

        if (!bfree) {
                dev_dbg(rvu->dev, "%s: subbank is full\n", __func__);
                return -ENOSPC;
        }

        /* If request is for contiguous , then max we can allocate is
         * equal to subbank_depth
         */
        if (contig && bfree < count) {
                dev_dbg(rvu->dev, "%s: no space for entry\n", __func__);
                return -ENOSPC;
        }

        /* 'save' array stores available indexes temporarily before
         * marking it as allocated
         */
        save = kcalloc(count, sizeof(u16), GFP_KERNEL);
        if (!save) {
                rc = -ENOMEM;
                goto err1;
        }

        if (contig) {
                rc =  __npc_subbank_contig_alloc(rvu, sb, key_type,
                                                 sidx, eidx, prio,
                                                 count, t, b,
                                                 bmap, save);
                /* contiguous allocation success means that
                 * requested number of free slots got
                 * allocated
                 */
                if (!rc)
                        *alloc_cnt = count;

        } else {
                rc =  __npc_subbank_non_contig_alloc(rvu, sb, key_type,
                                                     sidx, eidx, prio,
                                                     t, b, bmap,
                                                     count, save,
                                                     max_alloc, alloc_cnt);
        }

        if (rc)
                goto err1;

        /* Mark new subbank bank as used */
        if (new_sub_bank) {
                blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
                if (blkaddr < 0) {
                        dev_err(rvu->dev,
                                "%s: NPC block not implemented\n", __func__);
                        rc = -EFAULT;
                        goto err1;
                }

                rc =  __npc_subbank_mark_used(rvu, sb, key_type);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: Error to mark subbank as used\n",
                                __func__);
                        goto err2;
                }

                /* Configure section type to key_type */
                rvu_write64(rvu, blkaddr,
                            NPC_AF_MCAM_SECTIONX_CFG_EXT(sb->idx),
                            key_type);
        }

        for (i = 0; i < *alloc_cnt; i++) {
                rc = npc_subbank_idx_2_mcam_idx(rvu, sb, save[i],
                                                &mcam_idx[i]);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: Error to find mcam idx for %u\n",
                                __func__, save[i]);
                        /* TODO: handle err case gracefully */
                        goto err3;
                }

                /* Mark all slots as used */
                ret = __npc_subbank_mark_slot(rvu, sb, save[i], true);
                if (!ret) {
                        dev_err(rvu->dev, "%s: Error to mark mcam_idx %u\n",
                                __func__, mcam_idx[i]);
                        rc = -EFAULT;
                        goto err3;
                }
        }
        kfree(save);
        return 0;

err3:
        for (int j = 0; j < i; j++)
                __npc_subbank_mark_slot(rvu, sb, save[j], false);
err2:
        if (new_sub_bank)
                __npc_subbank_mark_free(rvu, sb);
err1:
        kfree(save);
        *alloc_cnt = 0;
        return rc;
}

static int
npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb,
                  int key_type, int ref, int limit, int prio,
                  bool contig, int count, u16 *mcam_idx,
                  int idx_sz, bool max_alloc, int *alloc_cnt)
{
        int rc;

        mutex_lock(&sb->lock);
        rc = __npc_subbank_alloc(rvu, sb, key_type, ref, limit, prio,
                                 contig, count, mcam_idx, idx_sz,
                                 max_alloc, alloc_cnt);
        mutex_unlock(&sb->lock);

        return rc;
}

static int
npc_del_from_pf_maps(struct rvu *rvu, u16 mcam_idx)
{
        int pcifunc, idx;
        void *map;

        map = xa_erase(&npc_priv->xa_idx2pf_map, mcam_idx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: failed to erase mcam_idx(%u) from xa_idx2pf map\n",
                        __func__, mcam_idx);
                return -EFAULT;
        }

        pcifunc = xa_to_value(map);
        map = xa_load(&npc_priv->xa_pf_map, pcifunc);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: failed to find entry for (%u) from xa_pf_map, mcam=%u\n",
                        __func__, pcifunc, mcam_idx);
                return -ESRCH;
        }

        idx = xa_to_value(map);

        map = xa_erase(&npc_priv->xa_pf2idx_map[idx], mcam_idx);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: failed to erase mcam_idx(%u) from xa_pf2idx_map map\n",
                        __func__, mcam_idx);
                return -EFAULT;
        }
        return 0;
}

static int
npc_add_to_pf_maps(struct rvu *rvu, u16 mcam_idx, int pcifunc)
{
        int rc, idx;
        void *map;

        dev_dbg(rvu->dev,
                "%s: add2maps mcam_idx(%u) to xa_idx2pf map pcifunc=%#x\n",
                __func__, mcam_idx, pcifunc);

        rc = xa_insert(&npc_priv->xa_idx2pf_map, mcam_idx,
                       xa_mk_value(pcifunc), GFP_KERNEL);

        if (rc) {
                map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx);
                dev_err(rvu->dev,
                        "%s: failed to insert mcam_idx(%u) to xa_idx2pf map, existing value=%lu\n",
                        __func__, mcam_idx, xa_to_value(map));
                return -EFAULT;
        }

        map = xa_load(&npc_priv->xa_pf_map, pcifunc);
        if (!map) {
                dev_err(rvu->dev,
                        "%s: failed to find pf map entry for pcifunc=%#x, mcam=%u\n",
                        __func__, pcifunc, mcam_idx);
                return -ESRCH;
        }

        idx = xa_to_value(map);

        rc = xa_insert(&npc_priv->xa_pf2idx_map[idx], mcam_idx,
                       xa_mk_value(pcifunc), GFP_KERNEL);

        if (rc) {
                map = xa_load(&npc_priv->xa_pf2idx_map[idx], mcam_idx);
                xa_erase(&npc_priv->xa_idx2pf_map, mcam_idx);
                dev_err(rvu->dev,
                        "%s: failed to insert mcam_idx(%u) to xa_pf2idx_map map, earlier value=%lu idx=%u\n",
                        __func__, mcam_idx, xa_to_value(map), idx);

                return -EFAULT;
        }

        return 0;
}

static bool
npc_subbank_suits(struct npc_subbank *sb, int key_type)
{
        mutex_lock(&sb->lock);

        if (!sb->key_type) {
                mutex_unlock(&sb->lock);
                return true;
        }

        if (sb->key_type == key_type) {
                mutex_unlock(&sb->lock);
                return true;
        }

        mutex_unlock(&sb->lock);
        return false;
}

#define SB_ALIGN_UP(val)   (((val) + npc_priv->subbank_depth) & \
                            ~((npc_priv->subbank_depth) - 1))
#define SB_ALIGN_DOWN(val) ALIGN_DOWN((val), npc_priv->subbank_depth)

static void npc_subbank_iter_down(struct rvu *rvu,
                                  int ref, int limit,
                                  int *cur_ref, int *cur_limit,
                                  bool *start, bool *stop)
{
        int align;

        *stop = false;

        /* ALIGN_DOWN the limit to current subbank boundary bottom index */
        if (*start) {
                *start = false;
                *cur_ref = ref;
                align = SB_ALIGN_DOWN(ref);
                if (align < limit) {
                        *stop = true;
                        *cur_limit = limit;
                        return;
                }
                *cur_limit = align;
                return;
        }

        *cur_ref = *cur_limit - 1;
        align = *cur_ref - npc_priv->subbank_depth + 1;
        if (align <= limit) {
                *stop = true;
                *cur_limit = limit;
                return;
        }

        *cur_limit = align;
}

static void npc_subbank_iter_up(struct rvu *rvu,
                                int ref, int limit,
                                int *cur_ref, int *cur_limit,
                                bool *start, bool *stop)
{
        int align;

        *stop = false;

        /* ALIGN_UP the limit to current subbank boundary top index */
        if (*start) {
                *start = false;
                *cur_ref = ref;

                /* Find next lower prio subbank's bottom index */
                align = SB_ALIGN_UP(ref);

                /* Crosses limit ? */
                if (align - 1 > limit) {
                        *stop = true;
                        *cur_limit = limit;
                        return;
                }

                /* Current subbank's top index */
                *cur_limit = align - 1;
                return;
        }

        *cur_ref = *cur_limit + 1;
        align = *cur_ref + npc_priv->subbank_depth - 1;

        if (align >= limit) {
                *stop = true;
                *cur_limit = limit;
                return;
        }

        *cur_limit = align;
}

static int
npc_subbank_iter(struct rvu *rvu, int key_type,
                 int ref, int limit, int prio,
                 int *cur_ref, int *cur_limit,
                 bool *start, bool *stop)
{
        if (prio != NPC_MCAM_HIGHER_PRIO)
                npc_subbank_iter_up(rvu, ref, limit,
                                    cur_ref, cur_limit,
                                    start, stop);
        else
                npc_subbank_iter_down(rvu, ref, limit,
                                      cur_ref, cur_limit,
                                      start, stop);

        /* limit and ref should < bank_depth for x4 */
        if (key_type == NPC_MCAM_KEY_X4) {
                if (*cur_ref >= npc_priv->bank_depth)
                        return -EINVAL;

                if (*cur_limit >= npc_priv->bank_depth)
                        return -EINVAL;
        }
        /* limit and ref should < 2 * bank_depth, for x2 */
        if (*cur_ref >= 2 * npc_priv->bank_depth)
                return -EINVAL;

        if (*cur_limit >= 2 * npc_priv->bank_depth)
                return -EINVAL;

        return 0;
}

static int npc_idx_free(struct rvu *rvu, u16 *mcam_idx, int count,
                        bool maps_del)
{
        struct npc_subbank *sb;
        u16 vidx, midx;
        int sb_off, i;
        bool ret;
        int rc;

        /* Check if we can dealloc indexes properly ? */
        for (i = 0; i < count; i++) {
                rc =  npc_mcam_idx_2_subbank_idx(rvu, npc_vidx2idx(mcam_idx[i]),
                                                 &sb, &sb_off);
                if (rc) {
                        dev_err(rvu->dev,
                                "Failed to free mcam idx=%u\n", mcam_idx[i]);
                        return rc;
                }
        }

        for (i = 0; i < count; i++) {
                if (npc_is_vidx(mcam_idx[i])) {
                        vidx = mcam_idx[i];
                        midx = npc_vidx2idx(vidx);
                } else {
                        midx = mcam_idx[i];
                        vidx = npc_idx2vidx(midx);
                }

                if (midx >= npc_priv->bank_depth * npc_priv->num_banks) {
                        dev_err(rvu->dev,
                                "%s: Invalid mcam_idx=%u cannot be deleted\n",
                                __func__, mcam_idx[i]);
                        return -EINVAL;
                }

                rc =  npc_mcam_idx_2_subbank_idx(rvu, midx,
                                                 &sb, &sb_off);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: Failed to find subbank info for vidx=%u\n",
                                __func__, vidx);
                        return rc;
                }

                ret = npc_subbank_free(rvu, sb, sb_off);
                if (ret) {
                        dev_err(rvu->dev,
                                "%s: Failed to find subbank info for vidx=%u\n",
                                __func__, vidx);
                        return -EINVAL;
                }

                if (!maps_del)
                        continue;

                rc = npc_del_from_pf_maps(rvu, midx);
                if (rc)
                        return rc;

                /* If there is no vidx mapping; continue */
                if (vidx == midx)
                        continue;

                rc = npc_vidx_maps_del_entry(rvu, vidx, NULL);
                if (rc)
                        return rc;
        }

        return 0;
}

static int npc_multi_subbank_ref_alloc(struct rvu *rvu, int key_type,
                                       int ref, int limit, int prio,
                                       bool contig, int count,
                                       u16 *mcam_idx)
{
        struct npc_subbank *sb;
        unsigned long *bmap;
        int sb_off, off, rc;
        int cnt = 0;
        bool bitset;

        if (prio != NPC_MCAM_HIGHER_PRIO) {
                while (ref <= limit) {
                        /* Calculate subbank and subbank index */
                        rc =  npc_mcam_idx_2_subbank_idx(rvu, ref,
                                                         &sb, &sb_off);
                        if (rc)
                                goto err;

                        /* If subbank is not suitable for requested key type
                         * restart search from next subbank
                         */
                        if (!npc_subbank_suits(sb, key_type)) {
                                ref = SB_ALIGN_UP(ref);
                                if (contig) {
                                        rc = npc_idx_free(rvu, mcam_idx,
                                                          cnt, false);
                                        if (rc)
                                                return rc;
                                        cnt = 0;
                                }
                                continue;
                        }

                        mutex_lock(&sb->lock);

                        /* If subbank is free; mark it as used */
                        if (sb->flags & NPC_SUBBANK_FLAG_FREE) {
                                rc =  __npc_subbank_mark_used(rvu, sb,
                                                              key_type);
                                if (rc) {
                                        mutex_unlock(&sb->lock);
                                        dev_err(rvu->dev,
                                                "%s:Error to add to use array\n",
                                                __func__);
                                        goto err;
                                }
                        }

                        /* Find correct bmap */
                        __npc_subbank_sboff_2_off(rvu, sb, sb_off, &bmap, &off);

                        /* if bit is already set, reset 'cnt' */
                        bitset = test_bit(off, bmap);
                        if (bitset) {
                                mutex_unlock(&sb->lock);
                                if (contig) {
                                        rc = npc_idx_free(rvu, mcam_idx,
                                                          cnt, false);
                                        if (rc)
                                                return rc;
                                        cnt = 0;
                                }

                                ref++;
                                continue;
                        }

                        set_bit(off, bmap);
                        sb->free_cnt--;
                        mcam_idx[cnt++] = ref;
                        mutex_unlock(&sb->lock);

                        if (cnt == count)
                                return 0;
                        ref++;
                }

                /* Could not allocate request count slots */
                goto err;
        }
        while (ref >= limit) {
                rc =  npc_mcam_idx_2_subbank_idx(rvu, ref,
                                                 &sb, &sb_off);
                if (rc)
                        goto err;

                if (!npc_subbank_suits(sb, key_type)) {
                        ref = SB_ALIGN_DOWN(ref) - 1;
                        if (contig) {
                                rc = npc_idx_free(rvu, mcam_idx, cnt, false);
                                if (rc)
                                        return rc;

                                cnt = 0;
                        }
                        continue;
                }

                mutex_lock(&sb->lock);

                if (sb->flags & NPC_SUBBANK_FLAG_FREE) {
                        rc =  __npc_subbank_mark_used(rvu, sb, key_type);
                        if (rc) {
                                mutex_unlock(&sb->lock);
                                dev_err(rvu->dev,
                                        "%s:Error to add to use array\n",
                                        __func__);
                                goto err;
                        }
                }

                __npc_subbank_sboff_2_off(rvu, sb, sb_off, &bmap, &off);
                bitset = test_bit(off, bmap);
                if (bitset) {
                        mutex_unlock(&sb->lock);
                        if (contig) {
                                rc = npc_idx_free(rvu, mcam_idx, cnt, false);
                                if (rc)
                                        return rc;

                                cnt = 0;
                        }
                        ref--;
                        continue;
                }

                mcam_idx[cnt++] = ref;
                sb->free_cnt--;
                set_bit(off, bmap);
                mutex_unlock(&sb->lock);

                if (cnt == count)
                        return 0;
                ref--;
        }

err:
        rc = npc_idx_free(rvu, mcam_idx, cnt, false);
        if (rc)
                dev_err(rvu->dev,
                        "%s: Error happened while freeing cnt=%u indexes\n",
                        __func__, cnt);

        return -ENOSPC;
}

static int npc_subbank_free_cnt(struct rvu *rvu, struct npc_subbank *sb,
                                int key_type)
{
        int cnt, spd;

        spd = npc_priv->subbank_depth;
        mutex_lock(&sb->lock);

        if (sb->flags & NPC_SUBBANK_FLAG_FREE)
                cnt = key_type == NPC_MCAM_KEY_X4 ? spd : 2 * spd;
        else
                cnt = sb->free_cnt;

        mutex_unlock(&sb->lock);
        return cnt;
}

static int npc_subbank_ref_alloc(struct rvu *rvu, int key_type,
                                 int ref, int limit, int prio,
                                 bool contig, int count,
                                 u16 *mcam_idx)
{
        struct npc_subbank *sb1, *sb2;
        bool max_alloc, start, stop;
        int r, l, sb_idx1, sb_idx2;
        int tot = 0, rc;
        int alloc_cnt;

        max_alloc = !contig;

        start = true;
        stop = false;

        /* Loop until we cross the ref/limit boundary */
        while (!stop) {
                rc = npc_subbank_iter(rvu, key_type, ref, limit, prio,
                                      &r, &l, &start, &stop);

                dev_dbg(rvu->dev,
                        "%s: ref=%d limit=%d r=%d l=%d start=%d stop=%d tot=%d count=%d rc=%d\n",
                        __func__, ref, limit, r, l,
                        start, stop, tot, count, rc);

                if (rc)
                        goto err;

                /* Find subbank and subbank index for ref */
                rc = npc_mcam_idx_2_subbank_idx(rvu, r, &sb1,
                                                &sb_idx1);
                if (rc)
                        goto err;

                dev_dbg(rvu->dev,
                        "%s: ref subbank=%d off=%d\n",
                        __func__, sb1->idx, sb_idx1);

                /* Skip subbank if it is not available for the keytype */
                if (!npc_subbank_suits(sb1, key_type)) {
                        dev_dbg(rvu->dev,
                                "%s: not suitable sb=%d key_type=%d\n",
                                __func__, sb1->idx, key_type);
                        continue;
                }

                /* Find subbank and subbank index for limit */
                rc = npc_mcam_idx_2_subbank_idx(rvu, l, &sb2,
                                                &sb_idx2);
                if (rc)
                        goto err;

                dev_dbg(rvu->dev,
                        "%s: limit subbank=%d off=%d\n",
                        __func__, sb_idx1, sb_idx2);

                /* subbank of ref and limit should be same */
                if (sb1 != sb2) {
                        dev_err(rvu->dev,
                                "%s: l(%d) and r(%d) are not in same subbank\n",
                                __func__, r, l);
                        goto err;
                }

                if (contig &&
                    npc_subbank_free_cnt(rvu, sb1, key_type) < count) {
                        dev_dbg(rvu->dev, "%s: less count =%d\n",
                                __func__,
                                npc_subbank_free_cnt(rvu, sb1, key_type));
                        continue;
                }

                /* Try in one bank of a subbank */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb1, key_type,
                                        r, l, prio, contig,
                                        count - tot, mcam_idx + tot,
                                        count - tot, max_alloc,
                                        &alloc_cnt);

                tot += alloc_cnt;

                dev_dbg(rvu->dev, "%s: Allocated tot=%d alloc_cnt=%d\n",
                        __func__, tot, alloc_cnt);

                if (!rc && count == tot)
                        return 0;
        }
err:
        dev_dbg(rvu->dev, "%s: Error to allocate\n",
                __func__);

        /* non contiguous allocation fails. We need to do clean up */
        if (max_alloc) {
                rc = npc_idx_free(rvu, mcam_idx, tot, false);
                if (rc)
                        dev_err(rvu->dev,
                                "%s: failed to free %u indexes\n",
                                __func__, tot);
        }

        return -EFAULT;
}

/* Minimize allocation from bottom and top subbanks for noref allocations.
 * Default allocations are ref based, and will be allocated from top
 * subbanks (least priority subbanks). Since default allocation is at very
 * early stage of kernel netdev probes, this subbanks will be moved to
 * used subbanks list. This will pave a way for noref allocation from these
 * used subbanks. Skip allocation for these top and bottom, and try free
 * bank next. If none slot is available, come back and search in these
 * subbanks.
 */

static int npc_subbank_restricted_idxs[2];
static bool restrict_valid = true;

static bool npc_subbank_restrict_usage(struct rvu *rvu, int index)
{
        int i;

        if (!restrict_valid)
                return false;

        for (i = 0; i < ARRAY_SIZE(npc_subbank_restricted_idxs); i++) {
                if (index == npc_subbank_restricted_idxs[i])
                        return true;
        }

        return false;
}

static int npc_subbank_noref_alloc(struct rvu *rvu, int key_type, bool contig,
                                   int count, u16 *mcam_idx)
{
        struct npc_subbank *sb;
        unsigned long index;
        int tot = 0, rc;
        bool max_alloc;
        int alloc_cnt;
        int idx, i;
        void *val;

        max_alloc = !contig;

        /* Check used subbanks for free slots */
        xa_for_each(&npc_priv->xa_sb_used, index, val) {
                idx = xa_to_value(val);

                /* Minimize allocation from restricted subbanks
                 * in noref allocations.
                 */
                if (npc_subbank_restrict_usage(rvu, idx))
                        continue;

                sb = &npc_priv->sb[idx];

                /* Skip if not suitable subbank */
                if (!npc_subbank_suits(sb, key_type))
                        continue;

                if (contig && npc_subbank_free_cnt(rvu, sb, key_type) < count)
                        continue;

                /* try in bank 0. Try passing ref and limit equal to
                 * subbank boundaries
                 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb, key_type,
                                        sb->b0b, sb->b0t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot,
                                        count - tot,
                                        max_alloc, &alloc_cnt);

                /* Non contiguous allocation may allocate less than
                 * requested 'count'.
                 */
                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                /* Successfully allocated */
                if (!rc && count == tot)
                        return 0;

                /* x4 entries can be allocated from bank 0 only */
                if (key_type == NPC_MCAM_KEY_X4)
                        continue;

                /* try in bank 1 for x2 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb, key_type,
                                        sb->b1b, sb->b1t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot,
                                        count - tot, max_alloc,
                                        &alloc_cnt);

                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                if (!rc && count == tot)
                        return 0;
        }

        /* Allocate in free subbanks */
        xa_for_each(&npc_priv->xa_sb_free, index, val) {
                idx = xa_to_value(val);
                sb = &npc_priv->sb[idx];

                /* Minimize allocation from restricted subbanks
                 * in noref allocations.
                 */
                if (npc_subbank_restrict_usage(rvu, idx))
                        continue;

                if (!npc_subbank_suits(sb, key_type))
                        continue;

                /* try in bank 0 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb, key_type,
                                        sb->b0b, sb->b0t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot,
                                        count - tot,
                                        max_alloc, &alloc_cnt);

                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                /* Successfully allocated */
                if (!rc && count == tot)
                        return 0;

                /* x4 entries can be allocated from bank 0 only */
                if (key_type == NPC_MCAM_KEY_X4)
                        continue;

                /* try in bank 1 for x2 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb,
                                        key_type, sb->b1b, sb->b1t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot, count - tot,
                                        max_alloc, &alloc_cnt);

                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                if (!rc && count == tot)
                        return 0;
        }

        /* Allocate from restricted subbanks */
        for (i = 0; restrict_valid &&
             (i < ARRAY_SIZE(npc_subbank_restricted_idxs)); i++) {
                idx = npc_subbank_restricted_idxs[i];
                sb = &npc_priv->sb[idx];

                /* Skip if not suitable subbank */
                if (!npc_subbank_suits(sb, key_type))
                        continue;

                if (contig && npc_subbank_free_cnt(rvu, sb, key_type) < count)
                        continue;

                /* try in bank 0. Try passing ref and limit equal to
                 * subbank boundaries
                 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb, key_type,
                                        sb->b0b, sb->b0t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot,
                                        count - tot,
                                        max_alloc, &alloc_cnt);

                /* Non contiguous allocation may allocate less than
                 * requested 'count'.
                 */
                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                /* Successfully allocated */
                if (!rc && count == tot)
                        return 0;

                /* x4 entries can be allocated from bank 0 only */
                if (key_type == NPC_MCAM_KEY_X4)
                        continue;

                /* try in bank 1 for x2 */
                alloc_cnt = 0;
                rc =  npc_subbank_alloc(rvu, sb, key_type,
                                        sb->b1b, sb->b1t, 0,
                                        contig, count - tot,
                                        mcam_idx + tot,
                                        count - tot, max_alloc,
                                        &alloc_cnt);

                tot += alloc_cnt;

                dev_dbg(rvu->dev,
                        "%s: Allocated %d from subbank %d, tot=%d count=%d\n",
                        __func__, alloc_cnt, sb->idx, tot, count);

                if (!rc && count == tot)
                        return 0;
        }

        /* non contiguous allocation fails. We need to do clean up */
        if (max_alloc)
                npc_idx_free(rvu, mcam_idx, tot, false);

        dev_dbg(rvu->dev, "%s: non-contig allocation fails\n",
                __func__);

        return -EFAULT;
}

int npc_cn20k_idx_free(struct rvu *rvu, u16 *mcam_idx, int count)
{
        return npc_idx_free(rvu, mcam_idx, count, true);
}

int npc_cn20k_ref_idx_alloc(struct rvu *rvu, int pcifunc, int key_type,
                            int prio, u16 *mcam_idx, int ref, int limit,
                            bool contig, int count, bool virt)
{
        bool defrag_candidate = false;
        int i, eidx, rc, bd;
        bool ref_valid;
        u16 vidx;

        bd = npc_priv->bank_depth;

        /* Special case: ref == 0 && limit= 0 && prio == HIGH && count == 1
         * Here user wants to allocate 0th entry
         */
        if (!ref && !limit && prio == NPC_MCAM_HIGHER_PRIO &&
            count == 1) {
                rc = npc_subbank_ref_alloc(rvu, key_type, ref, limit,
                                           prio, contig, count, mcam_idx);

                if (rc)
                        return rc;
                goto add2map;
        }

        ref_valid = !!(limit || ref);
        defrag_candidate = !ref_valid && !contig && virt;
        if (!ref_valid) {
                if (contig && count > npc_priv->subbank_depth)
                        goto try_noref_multi_subbank;

                rc = npc_subbank_noref_alloc(rvu, key_type, contig,
                                             count, mcam_idx);
                if (!rc)
                        goto add2map;

try_noref_multi_subbank:
                eidx = (key_type == NPC_MCAM_KEY_X4) ? bd - 1 : 2 * bd - 1;

                if (prio == NPC_MCAM_HIGHER_PRIO)
                        rc = npc_multi_subbank_ref_alloc(rvu, key_type,
                                                         eidx, 0,
                                                         NPC_MCAM_HIGHER_PRIO,
                                                         contig, count,
                                                         mcam_idx);
                else
                        rc = npc_multi_subbank_ref_alloc(rvu, key_type,
                                                         0, eidx,
                                                         NPC_MCAM_LOWER_PRIO,
                                                         contig, count,
                                                         mcam_idx);

                if (!rc)
                        goto add2map;

                return rc;
        }

        if ((prio == NPC_MCAM_LOWER_PRIO && ref > limit) ||
            (prio == NPC_MCAM_HIGHER_PRIO && ref < limit)) {
                dev_err(rvu->dev, "%s: Wrong ref_enty(%d) or limit(%d)\n",
                        __func__, ref, limit);
                return -EINVAL;
        }

        if ((key_type == NPC_MCAM_KEY_X4 && (ref >= bd || limit >= bd)) ||
            (key_type == NPC_MCAM_KEY_X2 &&
             (ref >= 2 * bd || limit >= 2 * bd))) {
                dev_err(rvu->dev, "%s: Wrong ref_enty(%d) or limit(%d)\n",
                        __func__, ref, limit);
                return -EINVAL;
        }

        if (contig && count > npc_priv->subbank_depth)
                goto try_ref_multi_subbank;

        rc = npc_subbank_ref_alloc(rvu, key_type, ref, limit,
                                   prio, contig, count, mcam_idx);
        if (!rc)
                goto add2map;

try_ref_multi_subbank:
        rc = npc_multi_subbank_ref_alloc(rvu, key_type,
                                         ref, limit, prio,
                                         contig, count, mcam_idx);
        if (!rc)
                goto add2map;

        return rc;

add2map:
        for (i = 0; i < count; i++) {
                rc = npc_add_to_pf_maps(rvu, mcam_idx[i], pcifunc);
                if (rc)
                        goto err;

                if (!defrag_candidate)
                        continue;

                rc = npc_vidx_maps_add_entry(rvu, mcam_idx[i], pcifunc, &vidx);
                if (rc) {
                        npc_del_from_pf_maps(rvu, mcam_idx[i]);
                        goto err;
                }

                /* Return vidx to caller */
                mcam_idx[i] = vidx;
        }

        return 0;
err:
        for (int j = 0; j < i; j++) {
                npc_del_from_pf_maps(rvu, npc_vidx2idx(mcam_idx[j]));

                if (!defrag_candidate)
                        continue;

                npc_vidx_maps_del_entry(rvu, mcam_idx[j], NULL);
        }

        return rc;

}

void npc_cn20k_subbank_calc_free(struct rvu *rvu, int *x2_free,
                                 int *x4_free, int *sb_free)
{
        struct npc_subbank *sb;
        int i;

        /* Reset all stats to zero */
        *x2_free = 0;
        *x4_free = 0;
        *sb_free = 0;

        for (i = 0; i < npc_priv->num_subbanks; i++) {
                sb = &npc_priv->sb[i];
                mutex_lock(&sb->lock);

                /* Count number of free subbanks */
                if (sb->flags & NPC_SUBBANK_FLAG_FREE) {
                        (*sb_free)++;
                        goto next;
                }

                /* Sumup x4 free count */
                if (sb->key_type == NPC_MCAM_KEY_X4) {
                        (*x4_free) += sb->free_cnt;
                        goto next;
                }

                /* Sumup x2 free counts */
                (*x2_free) += sb->free_cnt;
next:
                mutex_unlock(&sb->lock);
        }
}

int
rvu_mbox_handler_npc_cn20k_get_fcnt(struct rvu *rvu,
                                    struct msg_req *req,
                                    struct npc_cn20k_get_fcnt_rsp *rsp)
{
        npc_cn20k_subbank_calc_free(rvu, &rsp->free_x2,
                                    &rsp->free_x4, &rsp->free_subbanks);
        return 0;
}

int
rvu_mbox_handler_npc_cn20k_get_kex_cfg(struct rvu *rvu,
                                       struct msg_req *req,
                                       struct npc_cn20k_get_kex_cfg_rsp *rsp)
{
        int extr, lt;

        rsp->rx_keyx_cfg = CN20K_GET_KEX_CFG(NIX_INTF_RX);
        rsp->tx_keyx_cfg = CN20K_GET_KEX_CFG(NIX_INTF_TX);

        /* Get EXTRACTOR LID */
        for (extr = 0; extr < NPC_MAX_EXTRACTOR; extr++) {
                rsp->intf_extr_lid[NIX_INTF_RX][extr] =
                        CN20K_GET_EXTR_LID(NIX_INTF_RX, extr);
                rsp->intf_extr_lid[NIX_INTF_TX][extr] =
                        CN20K_GET_EXTR_LID(NIX_INTF_TX, extr);
        }

        /* Get EXTRACTOR LTYPE */
        for (extr = 0; extr < NPC_MAX_EXTRACTOR; extr++) {
                for (lt = 0; lt < NPC_MAX_LT; lt++) {
                        rsp->intf_extr_lt[NIX_INTF_RX][extr][lt] =
                                CN20K_GET_EXTR_LT(NIX_INTF_RX, extr, lt);
                        rsp->intf_extr_lt[NIX_INTF_TX][extr][lt] =
                                CN20K_GET_EXTR_LT(NIX_INTF_TX, extr, lt);
                }
        }

        memcpy(rsp->mkex_pfl_name, rvu->mkex_pfl_name, MKEX_NAME_LEN);
        return 0;
}

static u32 *subbank_srch_order;

static void npc_populate_restricted_idxs(int num_subbanks)
{
        npc_subbank_restricted_idxs[0] = num_subbanks - 1;
        npc_subbank_restricted_idxs[1] = 0;
}

static int npc_create_srch_order(int cnt)
{
        int val = 0;

        subbank_srch_order = kcalloc(cnt, sizeof(u32),
                                     GFP_KERNEL);
        if (!subbank_srch_order)
                return -ENOMEM;

        /* cnt(subbank depth) is always a power of 2. There is a check in
         * npc_priv_init() to check the same.
         */
        for (int i = 0; i < cnt; i += 2) {
                subbank_srch_order[i] = cnt / 2 - val - 1;
                subbank_srch_order[i + 1] = cnt / 2 + 1 + val;
                val++;
        }

        subbank_srch_order[cnt - 1] = cnt / 2;
        return 0;
}

static int npc_subbanks_srch_order_init(struct rvu *rvu)
{
        struct npc_subbank *sb;
        int sb_idx;
        int i, j;
        int rc;

        for (i = 0; i < npc_priv->num_subbanks; i++) {
                sb_idx = subbank_srch_order[i];
                sb = &npc_priv->sb[sb_idx];
                sb->arr_idx = i;

                dev_dbg(rvu->dev, "%s: sb->idx=%u sb->arr_idx=%u\n",
                        __func__, sb->idx, sb->arr_idx);

                rc = xa_err(xa_store(&npc_priv->xa_sb_free, sb->arr_idx,
                                     xa_mk_value(sb->idx), GFP_KERNEL));
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: xa_store(xa_sb_free) failed at slot %d (sb=%d): %d\n",
                                __func__, i, sb_idx, rc);
                        for (j = 0; j < i; j++)
                                xa_erase(&npc_priv->xa_sb_free, j);
                        return rc;
                }
        }

        return 0;
}

static void npc_subbank_init(struct rvu *rvu, struct npc_subbank *sb, int idx)
{
        mutex_init(&sb->lock);

        sb->b0b = idx * npc_priv->subbank_depth;
        sb->b0t = sb->b0b + npc_priv->subbank_depth - 1;

        sb->b1b = npc_priv->bank_depth + idx * npc_priv->subbank_depth;
        sb->b1t = sb->b1b + npc_priv->subbank_depth - 1;

        sb->flags = NPC_SUBBANK_FLAG_FREE;
        sb->idx = idx;
}

static int npc_pcifunc_map_create(struct rvu *rvu)
{
        int pf, vf, numvfs;
        int cnt = 0;
        u16 pcifunc;
        u64 cfg;

        for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
                cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
                numvfs = (cfg >> 12) & 0xFF;

                /* Skip not enabled PFs */
                if (!(cfg & BIT_ULL(20)))
                        goto chk_vfs;

                /* If Admin function, check on VFs */
                if (cfg & BIT_ULL(21))
                        goto chk_vfs;

                pcifunc = pf << 9;

                xa_store(&npc_priv->xa_pf_map, (unsigned long)pcifunc,
                         xa_mk_value(cnt), GFP_KERNEL);

                cnt++;

chk_vfs:
                for (vf = 0; vf < numvfs; vf++) {
                        pcifunc = (pf << 9) | (vf + 1);

                        xa_store(&npc_priv->xa_pf_map, (unsigned long)pcifunc,
                                 xa_mk_value(cnt), GFP_KERNEL);
                        cnt++;
                }
        }

        return cnt;
}

struct npc_defrag_node {
        u8 idx;
        u8 key_type;
        bool valid;
        bool refs;
        u16 free_cnt;
        u16 vidx_cnt;
        u16 *vidx;
        struct list_head list;
};

static bool npc_defrag_skip_restricted_sb(int sb_id)
{
        int i;

        if (!restrict_valid)
                return false;

        for (i = 0; i < ARRAY_SIZE(npc_subbank_restricted_idxs); i++)
                if (sb_id == npc_subbank_restricted_idxs[i])
                        return true;
        return false;
}

/* Find subbank with minimum number of virtual indexes */
static struct npc_defrag_node *npc_subbank_min_vidx(struct list_head *lh)
{
        struct npc_defrag_node *node, *tnode = NULL;
        int min = INT_MAX;

        list_for_each_entry(node, lh, list) {
                if (!node->valid)
                        continue;

                /* if subbank has ref allocated mcam indexes, that subbank
                 * is not a good candidate to move out indexes.
                 */
                if (node->refs)
                        continue;

                if (min > node->vidx_cnt) {
                        min = node->vidx_cnt;
                        tnode = node;
                }
        }

        return tnode;
}

/* Find subbank with maximum number of free spaces */
static struct npc_defrag_node *npc_subbank_max_free(struct list_head *lh)
{
        struct npc_defrag_node *node, *tnode = NULL;
        int max = INT_MIN;

        list_for_each_entry(node, lh, list) {
                if (!node->valid)
                        continue;

                if (max < node->free_cnt) {
                        max = node->free_cnt;
                        tnode = node;
                }
        }

        return tnode;
}

static int npc_defrag_alloc_free_slots(struct rvu *rvu,
                                       struct npc_defrag_node *f,
                                       int cnt, u16 *save)
{
        int alloc_cnt1, alloc_cnt2;
        struct npc_subbank *sb;
        int rc, sb_off, i, err;
        bool deleted;

        sb = &npc_priv->sb[f->idx];

        alloc_cnt1 = 0;
        alloc_cnt2 = 0;

        rc = __npc_subbank_alloc(rvu, sb,
                                 f->key_type, sb->b0b,
                                 sb->b0t,
                                 NPC_MCAM_LOWER_PRIO,
                                 false, cnt, save, cnt, true,
                                 &alloc_cnt1);

        /* X4 entries only occupy bank 0 (b0b..b0t); see npc_subbank_idx_2_mcam_idx().
         * X2 uses both halves of the subbank, so spill into bank 1 if needed.
         */
        if (alloc_cnt1 < cnt && f->key_type == NPC_MCAM_KEY_X2) {
                rc = __npc_subbank_alloc(rvu, sb,
                                         f->key_type, sb->b1b,
                                         sb->b1t,
                                         NPC_MCAM_LOWER_PRIO,
                                         false, cnt - alloc_cnt1,
                                         save + alloc_cnt1,
                                         cnt - alloc_cnt1,
                                         true, &alloc_cnt2);
        }

        if (alloc_cnt1 + alloc_cnt2 != cnt) {
                dev_err(rvu->dev,
                        "%s: Failed to alloc cnt=%u alloc_cnt1=%u alloc_cnt2=%u\n",
                        __func__, cnt, alloc_cnt1, alloc_cnt2);
                rc = -ENOSPC;
                goto fail_free_alloc;
        }

        return 0;

fail_free_alloc:
        for (i = 0; i < alloc_cnt1 + alloc_cnt2; i++) {
                err =  npc_mcam_idx_2_subbank_idx(rvu, save[i],
                                                  &sb, &sb_off);
                if (err) {
                        dev_err(rvu->dev,
                                "%s: Error to find subbank for mcam idx=%u\n",
                                __func__, save[i]);
                        break;
                }

                deleted = __npc_subbank_free(rvu, sb, sb_off);
                if (!deleted) {
                        dev_err(rvu->dev,
                                "%s: Error to free mcam idx=%u\n",
                                __func__, save[i]);
                        break;
                }
        }

        return rc;
}

static int npc_defrag_add_2_show_list(struct rvu *rvu, u16 old_midx,
                                      u16 new_midx, u16 vidx)
{
        struct npc_defrag_show_node *node;

        node = kcalloc(1, sizeof(*node), GFP_KERNEL);
        if (!node)
                return -ENOMEM;

        node->old_midx = old_midx;
        node->new_midx = new_midx;
        node->vidx = vidx;
        INIT_LIST_HEAD(&node->list);

        mutex_lock(&npc_priv->lock);
        list_add_tail(&node->list, &npc_priv->defrag_lh);
        mutex_unlock(&npc_priv->lock);

        return 0;
}

static
int npc_defrag_move_vdx_to_free(struct rvu *rvu,
                                struct npc_defrag_node *f,
                                struct npc_defrag_node *v,
                                int cnt, u16 *save)
{
        u16 new_midx, old_midx, vidx, target_pf;
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct rvu_npc_mcam_rule *rule, *tmp;
        int i, vidx_cnt, rc, sb_off;
        struct npc_subbank *sb;
        bool deleted;
        u16 pcifunc;
        int blkaddr;
        void *map;
        u8 bank;
        u16 midx;
        u64 stats;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);

        vidx_cnt = v->vidx_cnt;
        for (i = 0; i < cnt; i++) {
                vidx = v->vidx[vidx_cnt - i - 1];
                old_midx = npc_vidx2idx(vidx);
                new_midx = save[cnt - i - 1];

                dev_dbg(rvu->dev,
                        "%s: Moving %u ---> %u  (vidx=%u)\n",
                        __func__,
                        old_midx, new_midx, vidx);

                rc = npc_defrag_add_2_show_list(rvu, old_midx, new_midx, vidx);
                if (rc)
                        dev_err(rvu->dev,
                                "%s: Error happened to add to show list vidx=%u\n",
                                __func__, vidx);

                /* Modify vidx to point to new mcam idx */
                rc = npc_vidx_maps_modify(rvu, vidx, new_midx);
                if (rc)
                        return rc;

                midx = old_midx % mcam->banksize;
                bank = old_midx / mcam->banksize;
                stats = rvu_read64(rvu, blkaddr,
                                   NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx,
                                                                      bank));

                /* If bug happened during copy/enable mcam, then there is a bug in allocation
                 * algorithm itself. There is no point in rewinding and returning, as it
                 * will face further issue. Return error after printing error
                 */
                if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false)) {
                        dev_err(rvu->dev,
                                "%s: Error happened while disabling old_mid=%u\n",
                                __func__, old_midx);
                        return -EFAULT;
                }

                if (npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx)) {
                        dev_err(rvu->dev,
                                "%s: Error happened while copying old_midx=%u new_midx=%u\n",
                                __func__, old_midx, new_midx);
                        return -EFAULT;
                }

                if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true)) {
                        dev_err(rvu->dev,
                                "%s: Error happened while enabling new_mid=%u\n",
                                __func__, new_midx);
                        return -EFAULT;
                }

                midx = new_midx % mcam->banksize;
                bank = new_midx / mcam->banksize;
                rvu_write64(rvu, blkaddr,
                            NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx, bank),
                            stats);

                /* Free the old mcam idx */
                rc =  npc_mcam_idx_2_subbank_idx(rvu, old_midx,
                                                 &sb, &sb_off);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: Unable to calculate subbank off for mcamidx=%u\n",
                                __func__, old_midx);
                        return rc;
                }

                deleted = __npc_subbank_free(rvu, sb, sb_off);
                if (!deleted) {
                        dev_err(rvu->dev,
                                "%s:  Failed to free mcamidx=%u sb=%u sb_off=%u\n",
                                __func__, old_midx, sb->idx, sb_off);
                        return -EFAULT;
                }

                /* save pcifunc */
                map = xa_load(&npc_priv->xa_idx2pf_map, old_midx);
                pcifunc = xa_to_value(map);

                /* delete from pf maps */
                rc =  npc_del_from_pf_maps(rvu, old_midx);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s:  Failed to delete pf maps for mcamidx=%u\n",
                                __func__, old_midx);
                        return rc;
                }

                /* add new mcam_idx to pf map */
                rc = npc_add_to_pf_maps(rvu, new_midx, pcifunc);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s:  Failed to add pf maps for mcamidx=%u\n",
                                __func__, new_midx);
                        return rc;
                }

                /* Remove from mcam maps */
                mcam->entry2pfvf_map[old_midx] = NPC_MCAM_INVALID_MAP;
                mcam->entry2cntr_map[old_midx] = NPC_MCAM_INVALID_MAP;
                npc_mcam_clear_bit(mcam, old_midx);

                mcam->entry2pfvf_map[new_midx] = pcifunc;
                /* Counter is not preserved */
                mcam->entry2cntr_map[new_midx] = new_midx;
                target_pf = mcam->entry2target_pffunc[old_midx];
                mcam->entry2target_pffunc[new_midx] = target_pf;
                mcam->entry2target_pffunc[old_midx] = NPC_MCAM_INVALID_MAP;

                npc_mcam_set_bit(mcam, new_midx);

                /* Note: list order is not functionally required for mcam_rules */
                list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) {
                        if (rule->entry != old_midx)
                                continue;

                        rule->entry = new_midx;
                        break;
                }

                /* Mark as invalid */
                v->vidx[vidx_cnt - i - 1] = -1;
                save[cnt - i - 1] = -1;

                f->free_cnt--;
                v->vidx_cnt--;
        }

        return 0;
}

static int npc_defrag_process(struct rvu *rvu, struct list_head *lh)
{
        struct npc_defrag_node *v = NULL;
        struct npc_defrag_node *f = NULL;
        int rc = 0, cnt;
        u16 *save;

        while (1) {
                /* Find subbank with minimum vidx */
                if (!v) {
                        v = npc_subbank_min_vidx(lh);
                        if (!v)
                                break;
                }

                /* Find subbank with maximum free slots */
                if (!f) {
                        f = npc_subbank_max_free(lh);
                        if (!f)
                                break;
                }

                if (!v->vidx_cnt) {
                        list_del_init(&v->list);
                        v = NULL;
                        continue;
                }

                if (!f->free_cnt) {
                        list_del_init(&f->list);
                        f = NULL;
                        continue;
                }

                /* If both subbanks are same, choose vidx and
                 * search for free list again
                 */
                if (f == v) {
                        list_del_init(&f->list);
                        f = NULL;
                        continue;
                }

                /* Calculate minimum free slots needs to be allocated */
                cnt = f->free_cnt > v->vidx_cnt ? v->vidx_cnt :
                        f->free_cnt;

                dev_dbg(rvu->dev,
                        "%s: cnt=%u free_cnt=%u(sb=%u) vidx_cnt=%u(sb=%u)\n",
                        __func__, cnt, f->free_cnt, f->idx,
                        v->vidx_cnt, v->idx);

                /* Allocate an array to store newly allocated
                 * free slots (mcam indexes)
                 */
                save = kcalloc(cnt, sizeof(*save), GFP_KERNEL);
                if (!save) {
                        rc = -ENOMEM;
                        goto err;
                }

                /* Alloc free slots for existing vidx */
                rc = npc_defrag_alloc_free_slots(rvu, f, cnt, save);
                if (rc) {
                        kfree(save);
                        goto err;
                }

                /* Move vidx to free slots; update pf_map and vidx maps,
                 * and free existing vidx mcam slots
                 */
                rc = npc_defrag_move_vdx_to_free(rvu, f, v, cnt, save);
                if (rc) {
                        kfree(save);
                        goto err;
                }

                kfree(save);

                if (!f->free_cnt) {
                        list_del_init(&f->list);
                        f = NULL;
                }

                if (!v->vidx_cnt) {
                        list_del_init(&v->list);
                        v = NULL;
                }
        }

err:
        /* Whole defragmentation process is done within locks. if there
         * is an error, it would be hard to roll back as index remove/add
         * can fail again if it failed before. This would mean that there
         * is bug in the index management algorithm.
         * Return from here than rolling back.
         */
        return rc;
}

static void npc_defrag_list_clear(void)
{
        struct npc_defrag_show_node *node, *next;

        mutex_lock(&npc_priv->lock);
        list_for_each_entry_safe(node, next, &npc_priv->defrag_lh, list) {
                list_del_init(&node->list);
                kfree(node);
        }

        mutex_unlock(&npc_priv->lock);
}

static void npc_lock_all_subbank(void)
{
        int i;

        for (i = 0; i < npc_priv->num_subbanks; i++)
                mutex_lock(&npc_priv->sb[i].lock);
}

static void npc_unlock_all_subbank(void)
{
        int i;

        for (i = npc_priv->num_subbanks - 1; i >= 0; i--)
                mutex_unlock(&npc_priv->sb[i].lock);
}

int npc_cn20k_search_order_set(struct rvu *rvu,
                               u64 narr[MAX_NUM_SUB_BANKS], int cnt)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int rsrc[2][MAX_NUM_SUB_BANKS] = { };
        u8 save[MAX_NUM_SUB_BANKS] = { };
        struct npc_subbank *sb;
        struct xarray *xa;
        int prio, rc, err;
        int sb_idx;
        enum {
                FREE = 0,
                USED = 1,
        };

        if (cnt != npc_priv->num_subbanks) {
                dev_err(rvu->dev, "Number of entries(%u) != %u\n",
                        cnt, npc_priv->num_subbanks);
                return -EINVAL;
        }

        mutex_lock(&mcam->lock);
        npc_lock_all_subbank();

        for (sb_idx = 0; sb_idx < cnt; sb_idx++) {
                sb = &npc_priv->sb[sb_idx];
                save[sb->idx] = sb->arr_idx;
        }

        for (prio = 0; prio < cnt; prio++) {
                sb_idx = narr[prio];
                sb = &npc_priv->sb[sb_idx];

                if (sb->flags & NPC_SUBBANK_FLAG_USED)
                        xa = &npc_priv->xa_sb_used;
                else
                        xa = &npc_priv->xa_sb_free;

                rc = xa_err(xa_store(xa, prio,
                                     xa_mk_value(sb_idx), GFP_KERNEL));
                if (rc) {
                        dev_err(rvu->dev,
                                "Setting arr_idx=%d for sb=%d failed\n",
                                sb->arr_idx, sb_idx);
                        goto fail;
                }

                if (sb->flags & NPC_SUBBANK_FLAG_USED) {
                        rsrc[USED][sb->arr_idx] -= 1;
                        rsrc[USED][prio] += 1;
                } else {
                        rsrc[FREE][sb->arr_idx] -= 1;
                        rsrc[FREE][prio] += 1;
                }

                sb->arr_idx = prio;
        }

        for (prio = 0; prio < cnt; prio++) {
                if (rsrc[FREE][prio] == -1)
                        xa_erase(&npc_priv->xa_sb_free, prio);

                if (rsrc[USED][prio] == -1)
                        xa_erase(&npc_priv->xa_sb_used, prio);
        }

        for (int i = 0; i < cnt; i++)
                subbank_srch_order[i] = (u32)narr[i];

        restrict_valid = false;

        npc_unlock_all_subbank();
        mutex_unlock(&mcam->lock);

        return 0;

fail:
        for (prio = 0; prio < cnt; prio++) {
                if (rsrc[FREE][prio] == 1)
                        xa_erase(&npc_priv->xa_sb_free, prio);

                if (rsrc[USED][prio] == 1)
                        xa_erase(&npc_priv->xa_sb_used, prio);
        }

        for (sb_idx = 0; sb_idx < cnt; sb_idx++) {
                sb = &npc_priv->sb[sb_idx];
                sb->arr_idx = save[sb_idx];

                if (sb->flags & NPC_SUBBANK_FLAG_USED)
                        xa = &npc_priv->xa_sb_used;
                else
                        xa = &npc_priv->xa_sb_free;

                /* Since the entry already exists, xa_store() replaces
                 * the value without a kmalloc(), making failure highly unlikely.
                 */
                err = xa_err(xa_store(xa, sb->arr_idx,
                                      xa_mk_value(sb->idx), GFP_KERNEL));
                WARN(!!err, "Failed to rollback sb=%u idx=%u\n",
                     sb->idx, sb->arr_idx);
        }

        npc_unlock_all_subbank();
        mutex_unlock(&mcam->lock);

        return rc;
}

const u32 *npc_cn20k_search_order_get(bool *restricted_order, u32 *sz)
{
        *restricted_order = restrict_valid;
        *sz = npc_priv->num_subbanks;
        return subbank_srch_order;
}

/* Only non-ref non-contigous mcam indexes
 * are picked for defrag process
 */
int npc_cn20k_defrag(struct rvu *rvu)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct npc_defrag_node *node, *tnode;
        struct list_head x4lh, x2lh, *lh;
        int rc = 0, i, sb_off, tot;
        struct npc_subbank *sb;
        unsigned long index;
        void *map;
        u16 midx;

        /* Free previous show list */
        npc_defrag_list_clear();

        INIT_LIST_HEAD(&x4lh);
        INIT_LIST_HEAD(&x2lh);

        node = kcalloc(npc_priv->num_subbanks, sizeof(*node), GFP_KERNEL);
        if (!node)
                return -ENOMEM;

        /* Lock mcam */
        mutex_lock(&mcam->lock);
        npc_lock_all_subbank();

        /* Fill in node with subbank properties */
        for (i = 0; i < npc_priv->num_subbanks; i++) {
                sb = &npc_priv->sb[i];

                node[i].idx = i;
                node[i].key_type = sb->key_type;
                node[i].free_cnt = sb->free_cnt;
                node[i].vidx = kcalloc(npc_priv->subbank_depth * 2,
                                       sizeof(*node[i].vidx),
                                       GFP_KERNEL);
                if (!node[i].vidx) {
                        rc = -ENOMEM;
                        goto free_vidx;
                }

                /* If subbank is empty, dont include it in defrag
                 * process
                 */
                if (sb->flags & NPC_SUBBANK_FLAG_FREE) {
                        node[i].valid = false;
                        continue;
                }

                if (npc_defrag_skip_restricted_sb(i)) {
                        node[i].valid = false;
                        continue;
                }

                node[i].valid = true;
                INIT_LIST_HEAD(&node[i].list);

                /* Add node to x2 or x4 list */
                lh = sb->key_type == NPC_MCAM_KEY_X2 ? &x2lh : &x4lh;
                list_add_tail(&node[i].list, lh);
        }

        /* Filling vidx[] array with all vidx in that subbank */
        xa_for_each_start(&npc_priv->xa_vidx2idx_map, index, map,
                          npc_priv->bank_depth * 2) {
                midx = xa_to_value(map);
                rc =  npc_mcam_idx_2_subbank_idx(rvu, midx,
                                                 &sb, &sb_off);
                if (rc) {
                        dev_err(rvu->dev,
                                "%s: Error to get mcam_idx for vidx=%lu\n",
                                __func__, index);
                        goto free_vidx;
                }

                tnode = &node[sb->idx];
                tnode->vidx[tnode->vidx_cnt] = index;
                tnode->vidx_cnt++;
        }

        /* Mark all subbank which has ref allocation */
        for (i = 0; i < npc_priv->num_subbanks; i++) {
                tnode = &node[i];

                if (!tnode->valid)
                        continue;

                tot = (tnode->key_type == NPC_MCAM_KEY_X2) ?
                        npc_priv->subbank_depth * 2 : npc_priv->subbank_depth;

                if (node[i].vidx_cnt != tot - tnode->free_cnt)
                        tnode->refs = true;
        }

        rc =  npc_defrag_process(rvu, &x2lh);
        if (rc)
                goto free_vidx;

        rc =  npc_defrag_process(rvu, &x4lh);
        if (rc)
                goto free_vidx;

free_vidx:
        npc_unlock_all_subbank();
        mutex_unlock(&mcam->lock);
        for (i = 0; i < npc_priv->num_subbanks; i++)
                kfree(node[i].vidx);
        kfree(node);
        return rc;
}

int rvu_mbox_handler_npc_defrag(struct rvu *rvu, struct msg_req *req,
                                struct msg_rsp *rsp)
{
        return npc_cn20k_defrag(rvu);
}

int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
                                u16 *mcast, u16 *promisc, u16 *ucast)
{
        u16 *ptr[4] = {promisc, mcast, bcast, ucast};
        unsigned long idx;
        bool set = false;
        void *val;
        int i, j;

        for (i = 0; i < ARRAY_SIZE(ptr); i++) {
                if (!ptr[i])
                        continue;

                *ptr[i] = USHRT_MAX;
        }

        if (!npc_priv)
                return 0;

        if (is_lbk_vf(rvu, pcifunc)) {
                if (!ptr[0])
                        return -EINVAL;

                idx = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID);
                val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx);
                if (!val) {
                        pr_debug("%s: Failed to find %s index for pcifunc=%#x\n",
                                 __func__,
                                 npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID],
                                 pcifunc);

                        return -ESRCH;
                }

                *ptr[0] = xa_to_value(val);
                return 0;
        }

        if (is_vf(pcifunc)) {
                if (!ptr[3])
                        return -EINVAL;

                idx = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID);
                val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx);
                if (!val) {
                        pr_debug("%s: Failed to find %s index for pcifunc=%#x\n",
                                 __func__,
                                 npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID],
                                 pcifunc);

                        return -ESRCH;
                }

                *ptr[3] = xa_to_value(val);
                return 0;
        }

        for (i = NPC_DFT_RULE_START_ID, j = 0; i < NPC_DFT_RULE_MAX_ID; i++,
             j++) {
                if (!ptr[j])
                        continue;

                idx = NPC_DFT_RULE_ID_MK(pcifunc, i);
                val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx);
                if (!val) {
                        pr_debug("%s: Failed to find %s index for pcifunc=%#x\n",
                                 __func__,
                                 npc_dft_rule_name[i], pcifunc);

                        continue;
                }

                *ptr[j] = xa_to_value(val);
                set = true;
        }

        return  set ? 0 : -ESRCH;
}

int rvu_mbox_handler_npc_get_pfl_info(struct rvu *rvu, struct msg_req *req,
                                      struct npc_get_pfl_info_rsp *rsp)
{
        if (!is_cn20k(rvu->pdev)) {
                dev_err(rvu->dev, "Mbox support is only for cn20k\n");
                return -EOPNOTSUPP;
        }

        rsp->kw_type = npc_priv->kw;
        rsp->x4_slots = npc_priv->bank_depth;
        return 0;
}

int rvu_mbox_handler_npc_get_num_kws(struct rvu *rvu,
                                     struct npc_get_num_kws_req *req,
                                     struct npc_get_num_kws_rsp *rsp)
{
        u64 kw_mask[NPC_KWS_IN_KEY_SZ_MAX] = { 0 };
        u64 kw[NPC_KWS_IN_KEY_SZ_MAX] = { 0 };
        struct rvu_npc_mcam_rule dummy = { 0 };
        struct mcam_entry_mdata mdata = { };
        struct npc_install_flow_req *fl;
        int i, cnt = 0, blkaddr;

        if (!is_cn20k(rvu->pdev)) {
                dev_err(rvu->dev, "Mbox support is only for cn20k\n");
                return -EOPNOTSUPP;
        }

        fl = &req->fl;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0) {
                dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__);
                return NPC_MCAM_INVALID_REQ;
        }

        mdata.kw = kw;
        mdata.kw_mask = kw_mask;

        npc_update_flow(rvu, &mdata, fl->features, &fl->packet,
                        &fl->mask, &dummy, fl->intf, blkaddr);

        /* Find the most significant word valid. Traverse from
         * MSB to LSB, check if cam0 or cam1 is set
         */
        for (i = NPC_KWS_IN_KEY_SZ_MAX - 1; i >= 0; i--) {
                if (kw[i] || kw_mask[i]) {
                        cnt = i + 1;
                        break;
                }
        }

        rsp->kws = cnt;

        return 0;
}

int rvu_mbox_handler_npc_get_dft_rl_idxs(struct rvu *rvu, struct msg_req *req,
                                         struct npc_get_dft_rl_idxs_rsp *rsp)
{
        u16 bcast, mcast, promisc, ucast;
        u16 pcifunc;
        int rc;

        if (!is_cn20k(rvu->pdev)) {
                dev_err(rvu->dev, "Mbox support is only for cn20k\n");
                return -EOPNOTSUPP;
        }

        pcifunc = req->hdr.pcifunc;

        rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &bcast, &mcast,
                                         &promisc, &ucast);
        if (rc)
                return rc;

        rsp->bcast = bcast;
        rsp->mcast = mcast;
        rsp->promisc = promisc;
        rsp->ucast = ucast;
        return 0;
}

bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc)
{
        return is_pf_cgxmapped(rvu, rvu_get_pf(rvu->pdev, pcifunc)) ||
                is_lbk_vf(rvu, pcifunc);
}

void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u16 ptr[4] = {[0 ... 3] = USHRT_MAX};
        struct rvu_npc_mcam_rule *rule, *tmp;
        unsigned long index;
        int blkaddr, rc, i;
        void *map;

        if (!npc_priv)
                return;

        if (!npc_is_cgx_or_lbk(rvu, pcifunc)) {
                dev_dbg(rvu->dev,
                        "%s: dft rule allocation is only for cgx mapped device, pcifunc=%#x\n",
                        __func__, pcifunc);
                return;
        }

        rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1],
                                         &ptr[2], &ptr[3]);
        if (rc)
                return;

        /* LBK */
        if (is_lbk_vf(rvu, pcifunc)) {
                index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID);
                map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index);
                if (!map)
                        dev_dbg(rvu->dev,
                                "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n",
                                __func__,
                                npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID],
                                pcifunc);

                goto free_rules;
        }

        /* VF */
        if (is_vf(pcifunc)) {
                index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID);
                map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index);
                if (!map)
                        dev_dbg(rvu->dev,
                                "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n",
                                __func__,
                                npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID],
                                pcifunc);

                goto free_rules;
        }

        /* PF */
        for (i = NPC_DFT_RULE_START_ID; i < NPC_DFT_RULE_MAX_ID; i++)  {
                index = NPC_DFT_RULE_ID_MK(pcifunc, i);
                map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index);
                if (!map)
                        dev_dbg(rvu->dev,
                                "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n",
                                __func__, npc_dft_rule_name[i],
                                pcifunc);
        }

free_rules:
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return;
        for (int i = 0; i < 4; i++) {
                if (ptr[i] == USHRT_MAX)
                        continue;

                mutex_lock(&mcam->lock);
                npc_mcam_clear_bit(mcam, ptr[i]);
                mcam->entry2pfvf_map[ptr[i]] = NPC_MCAM_INVALID_MAP;
                npc_cn20k_enable_mcam_entry(rvu, blkaddr, ptr[i], false);
                mcam->entry2target_pffunc[ptr[i]] = 0x0;
                mutex_unlock(&mcam->lock);

                rc = npc_cn20k_idx_free(rvu, &ptr[i], 1);
                if (rc) {
                        /* Non recoverable error. Let us WARN and return. Keep system alive to
                         * enable debugging
                         */
                        WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n",
                             __func__, pcifunc, ptr[i]);
                        return;
                }
        }

        mutex_lock(&mcam->lock);
        list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) {
                for (int i = 0; i < 4; i++) {
                        if (ptr[i] != rule->entry)
                                continue;

                        list_del(&rule->list);
                        kfree(rule);
                        break;
                }
        }
        mutex_unlock(&mcam->lock);
}

int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc)
{
        struct npc_mcam_free_entry_req free_req = { 0 };
        u16 mcam_idx[4] = { 0 }, pf_ucast, pf_pcifunc;
        struct npc_mcam_alloc_entry_req req = { 0 };
        struct npc_mcam_alloc_entry_rsp rsp = { 0 };
        int ret, eidx, i, k, pf, cnt;
        struct rvu_pfvf *pfvf;
        unsigned long index;
        struct msg_rsp free_rsp;
        u16 b, m, p, u;

        if (!npc_priv)
                return 0;

        if (!npc_is_cgx_or_lbk(rvu, pcifunc)) {
                dev_dbg(rvu->dev,
                        "%s: dft rule allocation is only for cgx mapped device, pcifunc=%#x\n",
                        __func__, pcifunc);
                return 0;
        }

        /* Check if default rules are already alloced for this pcifunc */
        ret =  npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &b, &m, &p, &u);
        if (!ret) {
                dev_dbg(rvu->dev,
                        "%s: default rules are already installed (pcifunc=%#x)\n",
                        __func__, pcifunc);
                dev_dbg(rvu->dev,
                        "%s: bcast(%u) mcast(%u) promisc(%u) ucast(%u)\n",
                        __func__, b, m, p, u);
                return 0;
        }

        /* Set ref index as lowest priority index */
        eidx = 2 * npc_priv->bank_depth - 1;

        /* Install only UCAST for VF */
        cnt = is_vf(pcifunc) ? 1 : ARRAY_SIZE(mcam_idx);

        /* For VF pcifunc, allocate default mcam indexes by taking
         * ref as PF's ucast index.
         */
        if (is_vf(pcifunc)) {
                pf = rvu_get_pf(rvu->pdev, pcifunc);
                pf_pcifunc = pf << RVU_CN20K_PFVF_PF_SHIFT;

                /* Get PF's ucast entry index */
                ret = npc_cn20k_dft_rules_idx_get(rvu, pf_pcifunc, NULL,
                                                  NULL, NULL, &pf_ucast);

                /* There is no PF rules installed; and VF installation comes
                 * first. PF may come later.
                 * TODO: Install PF rules before installing VF rules.
                 */

                /* Set PF's ucast as ref entry */
                if (!ret)
                        eidx = pf_ucast;
        }

        pfvf = rvu_get_pfvf(rvu, pcifunc);
        pfvf->hw_prio = NPC_DFT_RULE_PRIO;

        if (npc_priv->kw == NPC_MCAM_KEY_X4) {
                req.kw_type = NPC_MCAM_KEY_X4;
                req.ref_entry = eidx & (npc_priv->bank_depth - 1);
        } else {
                req.kw_type = NPC_MCAM_KEY_X2;
                req.ref_entry = eidx;
        }

        req.contig = false;
        req.ref_prio = NPC_MCAM_HIGHER_PRIO;
        req.count = cnt;
        req.hdr.pcifunc = pcifunc;

        ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &req, &rsp);

        /* successfully allocated index */
        if (!ret) {
                /* Copy indexes to local array */
                for (i = 0; i < cnt; i++)
                        mcam_idx[i] = rsp.entry_list[i];

                goto chk_sanity;
        }

        /* If there is no slots available and request is for PF,
         * return error.
         */
        if (!is_vf(pcifunc)) {
                dev_err(rvu->dev,
                        "%s: Default index allocation failed for pcifunc=%#x\n",
                        __func__, pcifunc);
                return ret;
        }

        /* We could not find an index with higher priority index for VF.
         * Find rule with lower priority index and set hardware priority
         * as NPC_DFT_RULE_PRIO - 1 (higher hw priority)
         */
        req.contig = false;
        req.count = cnt;
        req.hdr.pcifunc = pcifunc;
        req.ref_prio = NPC_MCAM_LOWER_PRIO;

        if (npc_priv->kw == NPC_MCAM_KEY_X4) {
                req.kw_type = NPC_MCAM_KEY_X4;
                req.ref_entry = eidx & (npc_priv->bank_depth - 1);
        } else {
                req.kw_type = NPC_MCAM_KEY_X2;
                req.ref_entry = eidx;
        }

        ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &req, &rsp);
        if (ret) {
                dev_err(rvu->dev,
                        "%s: Default index allocation failed for pcifunc=%#x\n",
                        __func__, pcifunc);
                return ret;
        }

        /* Copy indexes to local array */
        for (i = 0; i < cnt; i++)
                mcam_idx[i] = rsp.entry_list[i];

        pfvf->hw_prio = NPC_DFT_RULE_PRIO - 1;

chk_sanity:
        /* LBK */
        if (is_lbk_vf(rvu, pcifunc)) {
                index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID);
                ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index,
                                xa_mk_value(mcam_idx[0]), GFP_KERNEL);
                if (ret) {
                        dev_err(rvu->dev,
                                "%s: Err to insert %s mcam idx to xarray pcifunc=%#x\n",
                                __func__,
                                npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID],
                                pcifunc);
                        goto err;
                }

                goto done;
        }

        /* VF */
        if (is_vf(pcifunc)) {
                index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID);
                ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index,
                                xa_mk_value(mcam_idx[0]), GFP_KERNEL);
                if (ret) {
                        dev_err(rvu->dev,
                                "%s: Err to insert %s mcam idx to xarray pcifunc=%#x\n",
                                __func__,
                                npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID],
                                pcifunc);
                        goto err;
                }

                goto done;
        }

        /* PF */
        for (i = NPC_DFT_RULE_START_ID, k = 0; i < NPC_DFT_RULE_MAX_ID &&
             k < cnt; i++, k++) {
                index = NPC_DFT_RULE_ID_MK(pcifunc, i);
                ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index,
                                xa_mk_value(mcam_idx[k]), GFP_KERNEL);
                if (ret) {
                        dev_err(rvu->dev,
                                "%s: Err to insert %s mcam idx to xarray pcifunc=%#x\n",
                                __func__, npc_dft_rule_name[i],
                                pcifunc);
                        for (int p = NPC_DFT_RULE_START_ID; p < i; p++) {
                                index = NPC_DFT_RULE_ID_MK(pcifunc, p);
                                xa_erase(&npc_priv->xa_pf2dfl_rmap, index);
                        }
                        goto err;
                }
        }

done:
        return 0;

err:
        free_req.hdr.pcifunc = pcifunc;
        free_req.all = 1;
        ret = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &free_rsp);
        if (ret)
                dev_err(rvu->dev,
                        "%s: Error deleting default entries (pcifunc=%#x\n",
                        __func__, pcifunc);

        return -EFAULT;
}

static int npc_priv_init(struct rvu *rvu)
{
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int blkaddr, num_banks, bank_depth;
        int num_subbanks, subbank_depth;
        u64 npc_const1, npc_const2 = 0;
        struct npc_subbank *sb;
        int ret = -ENOMEM;
        u64 cfg;
        int i;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0) {
                dev_err(rvu->dev, "%s: NPC block not implemented\n",
                        __func__);
                return -ENODEV;
        }

        npc_const1 = rvu_read64(rvu, blkaddr, NPC_AF_CONST1);
        if (npc_const1 & BIT_ULL(63))
                npc_const2 = rvu_read64(rvu, blkaddr, NPC_AF_CONST2);

        num_banks = mcam->banks;
        if (!num_banks || num_banks > MAX_NUM_BANKS) {
                dev_err(rvu->dev,
                        "Number of banks(%u) is invalid\n", num_banks);
                return -EINVAL;
        }

        num_subbanks = FIELD_GET(GENMASK_ULL(39, 32), npc_const2);
        if (!num_subbanks || num_subbanks > MAX_NUM_SUB_BANKS) {
                dev_err(rvu->dev,
                        "Number of subbanks is invalid %u\n",
                        num_subbanks);
                return -EFAULT;
        }

        if (num_subbanks & (num_subbanks - 1)) {
                dev_err(rvu->dev,
                        "subbanks cnt(%u) should be a power of 2\n",
                        num_subbanks);
                return -EINVAL;
        }

        bank_depth = mcam->banksize;
        if (!bank_depth || bank_depth % num_subbanks) {
                dev_err(rvu->dev,
                        "Bank depth(%u) should be a multiple of num_subbanks(%u)\n",
                        bank_depth, num_subbanks);
                return -EINVAL;
        }

        subbank_depth = bank_depth / num_subbanks;
        if (!subbank_depth || subbank_depth > MAX_SUBBANK_DEPTH) {
                dev_err(rvu->dev,
                        "Invalid subbank depth %u\n",
                        subbank_depth);
                return -EINVAL;
        }

        npc_priv = kcalloc(1, sizeof(*npc_priv), GFP_KERNEL);
        if (!npc_priv)
                return -ENOMEM;

        npc_priv->num_banks = num_banks;
        npc_priv->num_subbanks = num_subbanks;
        npc_priv->bank_depth = bank_depth;
        npc_priv->subbank_depth = subbank_depth;

        /* Get kex configured key size */
        cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(0));
        npc_priv->kw = FIELD_GET(GENMASK_ULL(34, 32), cfg);

        dev_info(rvu->dev,
                 "banks=%u depth=%u, subbanks=%u depth=%u, key type=%s\n",
                 num_banks, bank_depth, num_subbanks, subbank_depth,
                 npc_kw_name[npc_priv->kw]);

        npc_priv->sb = kcalloc(num_subbanks, sizeof(struct npc_subbank),
                               GFP_KERNEL);
        if (!npc_priv->sb)
                goto fail1;

        xa_init_flags(&npc_priv->xa_sb_used, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_sb_free, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_idx2pf_map, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_pf_map, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_pf2dfl_rmap, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_idx2vidx_map, XA_FLAGS_ALLOC);
        xa_init_flags(&npc_priv->xa_vidx2idx_map, XA_FLAGS_ALLOC);

        if (npc_create_srch_order(num_subbanks))
                goto fail2;

        npc_populate_restricted_idxs(num_subbanks);

        /* Initialize subbanks */
        for (i = 0, sb = npc_priv->sb; i < num_subbanks; i++, sb++)
                npc_subbank_init(rvu, sb, i);

        ret = npc_subbanks_srch_order_init(rvu);
        if (ret)
                goto fail3;

        /* Get number of pcifuncs in the system */
        npc_priv->pf_cnt = npc_pcifunc_map_create(rvu);
        npc_priv->xa_pf2idx_map = kcalloc(npc_priv->pf_cnt,
                                          sizeof(struct xarray),
                                          GFP_KERNEL);
        if (!npc_priv->xa_pf2idx_map) {
                ret = -ENOMEM;
                goto fail3;
        }

        for (i = 0; i < npc_priv->pf_cnt; i++)
                xa_init_flags(&npc_priv->xa_pf2idx_map[i], XA_FLAGS_ALLOC);

        INIT_LIST_HEAD(&npc_priv->defrag_lh);
        mutex_init(&npc_priv->lock);

        return 0;

fail3:
        kfree(subbank_srch_order);
        subbank_srch_order = NULL;

fail2:
        xa_destroy(&npc_priv->xa_sb_used);
        xa_destroy(&npc_priv->xa_sb_free);
        xa_destroy(&npc_priv->xa_idx2pf_map);
        xa_destroy(&npc_priv->xa_pf_map);
        xa_destroy(&npc_priv->xa_pf2dfl_rmap);
        xa_destroy(&npc_priv->xa_idx2vidx_map);
        xa_destroy(&npc_priv->xa_vidx2idx_map);
        kfree(npc_priv->sb);
        npc_priv->sb = NULL;
fail1:
        kfree(npc_priv);
        npc_priv = NULL;
        return ret;
}

void npc_cn20k_deinit(struct rvu *rvu)
{
        int i;

        if (!npc_priv)
                return;

        xa_destroy(&npc_priv->xa_sb_used);
        xa_destroy(&npc_priv->xa_sb_free);
        xa_destroy(&npc_priv->xa_idx2pf_map);
        xa_destroy(&npc_priv->xa_pf_map);
        xa_destroy(&npc_priv->xa_pf2dfl_rmap);
        xa_destroy(&npc_priv->xa_idx2vidx_map);
        xa_destroy(&npc_priv->xa_vidx2idx_map);

        for (i = 0; i < npc_priv->pf_cnt; i++)
                xa_destroy(&npc_priv->xa_pf2idx_map[i]);

        kfree(npc_priv->xa_pf2idx_map);
        /* No need to destroy mutex lock as it is
         * part of subbank structure
         */
        kfree(npc_priv->sb);
        kfree(subbank_srch_order);
        bitmap_clear(npc_priv->en_map, 0, MAX_NUM_BANKS * MAX_NUM_SUB_BANKS *
                     MAX_SUBBANK_DEPTH);
        npc_defrag_list_clear();
        kfree(npc_priv);
        npc_priv = NULL;
}

static int npc_setup_mcam_section(struct rvu *rvu, int key_type)
{
        int blkaddr, sec;

        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0) {
                dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__);
                return -ENODEV;
        }

        for (sec = 0; sec < npc_priv->num_subbanks; sec++)
                rvu_write64(rvu, blkaddr,
                            NPC_AF_MCAM_SECTIONX_CFG_EXT(sec), key_type);

        return 0;
}

int npc_cn20k_init(struct rvu *rvu)
{
        int err;

        err = npc_priv_init(rvu);
        if (err) {
                dev_err(rvu->dev, "%s: Error to init\n",
                        __func__);
                return err;
        }

        err = npc_setup_mcam_section(rvu, NPC_MCAM_KEY_X2);
        if (err) {
                dev_err(rvu->dev, "%s: mcam section configuration failure\n",
                        __func__);
                goto fail;
        }

        return 0;

fail:
        npc_cn20k_deinit(rvu);
        return err;
}