root/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
// SPDX-License-Identifier: GPL-2.0
/* Marvell MCS driver
 *
 * Copyright (C) 2022 Marvell.
 */

#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>

#include "mcs.h"
#include "mcs_reg.h"

#define DRV_NAME        "Marvell MCS Driver"

#define PCI_CFG_REG_BAR_NUM     0

static const struct pci_device_id mcs_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_MCS) },
        { 0, }  /* end of table */
};

static LIST_HEAD(mcs_list);

void mcs_get_tx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id)
{
        u64 reg;

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLBCPKTSX(id);
        stats->ctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLMCPKTSX(id);
        stats->ctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLOCTETSX(id);
        stats->ctl_octet_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLUCPKTSX(id);
        stats->ctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLBCPKTSX(id);
        stats->unctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLMCPKTSX(id);
        stats->unctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLOCTETSX(id);
        stats->unctl_octet_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLUCPKTSX(id);
        stats->unctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYENCRYPTEDX(id);
        stats->octet_encrypted_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYPROTECTEDX(id);
        stats->octet_protected_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYNOACTIVESAX(id);
        stats->pkt_noactivesa_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYTOOLONGX(id);
        stats->pkt_toolong_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYUNTAGGEDX(id);
        stats->pkt_untagged_cnt =  mcs_reg_read(mcs, reg);
}

void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id)
{
        u64 reg;

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLBCPKTSX(id);
        stats->ctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLMCPKTSX(id);
        stats->ctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLOCTETSX(id);
        stats->ctl_octet_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLUCPKTSX(id);
        stats->ctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLBCPKTSX(id);
        stats->unctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLMCPKTSX(id);
        stats->unctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLOCTETSX(id);
        stats->unctl_octet_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLUCPKTSX(id);
        stats->unctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYDECRYPTEDX(id);
        stats->octet_decrypted_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYVALIDATEX(id);
        stats->octet_validated_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSCTRLPORTDISABLEDX(id);
        stats->pkt_port_disabled_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYBADTAGX(id);
        stats->pkt_badtag_cnt =  mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAX(id);
        stats->pkt_nosa_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAERRORX(id);
        stats->pkt_nosaerror_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYTAGGEDCTLX(id);
        stats->pkt_tagged_ctl_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(id);
        stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg);

        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id);
        stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg);

        if (mcs->hw->mcs_blks > 1) {
                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id);
                stats->pkt_notag_cnt = mcs_reg_read(mcs, reg);
        }
}

void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats,
                          int id, int dir)
{
        u64 reg;

        if (dir == MCS_RX)
                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMHITX(id);
        else
                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMHITX(id);

        stats->tcam_hit_cnt = mcs_reg_read(mcs, reg);
}

void mcs_get_port_stats(struct mcs *mcs, struct mcs_port_stats *stats,
                        int id, int dir)
{
        u64 reg;

        if (dir == MCS_RX) {
                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMMISSX(id);
                stats->tcam_miss_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSPARSEERRX(id);
                stats->parser_err_cnt = mcs_reg_read(mcs, reg);
                if (mcs->hw->mcs_blks > 1) {
                        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSEARLYPREEMPTERRX(id);
                        stats->preempt_err_cnt = mcs_reg_read(mcs, reg);
                }
        } else {
                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMMISSX(id);
                stats->tcam_miss_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSPARSEERRX(id);
                stats->parser_err_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECTAGINSERTIONERRX(id);
                stats->sectag_insert_err_cnt = mcs_reg_read(mcs, reg);
        }
}

void mcs_get_sa_stats(struct mcs *mcs, struct mcs_sa_stats *stats, int id, int dir)
{
        u64 reg;

        if (dir == MCS_RX) {
                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAINVALIDX(id);
                stats->pkt_invalid_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTUSINGSAERRORX(id);
                stats->pkt_nosaerror_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTVALIDX(id);
                stats->pkt_notvalid_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAOKX(id);
                stats->pkt_ok_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAUNUSEDSAX(id);
                stats->pkt_nosa_cnt = mcs_reg_read(mcs, reg);
        } else {
                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAENCRYPTEDX(id);
                stats->pkt_encrypt_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAPROTECTEDX(id);
                stats->pkt_protected_cnt = mcs_reg_read(mcs, reg);
        }
}

void mcs_get_sc_stats(struct mcs *mcs, struct mcs_sc_stats *stats,
                      int id, int dir)
{
        u64 reg;

        if (dir == MCS_RX) {
                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCCAMHITX(id);
                stats->hit_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCINVALIDX(id);
                stats->pkt_invalid_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCLATEORDELAYEDX(id);
                stats->pkt_late_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCNOTVALIDX(id);
                stats->pkt_notvalid_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCUNCHECKEDX(id);
                stats->pkt_unchecked_cnt = mcs_reg_read(mcs, reg);

                if (mcs->hw->mcs_blks > 1) {
                        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCDELAYEDX(id);
                        stats->pkt_delay_cnt = mcs_reg_read(mcs, reg);

                        reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCOKX(id);
                        stats->pkt_ok_cnt = mcs_reg_read(mcs, reg);
                }
                if (mcs->hw->mcs_blks == 1) {
                        reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCDECRYPTEDX(id);
                        stats->octet_decrypt_cnt = mcs_reg_read(mcs, reg);

                        reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCVALIDATEX(id);
                        stats->octet_validate_cnt = mcs_reg_read(mcs, reg);
                }
        } else {
                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCENCRYPTEDX(id);
                stats->pkt_encrypt_cnt = mcs_reg_read(mcs, reg);

                reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCPROTECTEDX(id);
                stats->pkt_protected_cnt = mcs_reg_read(mcs, reg);

                if (mcs->hw->mcs_blks == 1) {
                        reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCENCRYPTEDX(id);
                        stats->octet_encrypt_cnt = mcs_reg_read(mcs, reg);

                        reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCPROTECTEDX(id);
                        stats->octet_protected_cnt = mcs_reg_read(mcs, reg);
                }
        }
}

void mcs_clear_stats(struct mcs *mcs, u8 type, u8 id, int dir)
{
        struct mcs_flowid_stats flowid_st;
        struct mcs_port_stats port_st;
        struct mcs_secy_stats secy_st;
        struct mcs_sc_stats sc_st;
        struct mcs_sa_stats sa_st;
        u64 reg;

        if (dir == MCS_RX)
                reg = MCSX_CSE_RX_SLAVE_CTRL;
        else
                reg = MCSX_CSE_TX_SLAVE_CTRL;

        mcs_reg_write(mcs, reg, BIT_ULL(0));

        switch (type) {
        case MCS_FLOWID_STATS:
                mcs_get_flowid_stats(mcs, &flowid_st, id, dir);
                break;
        case MCS_SECY_STATS:
                if (dir == MCS_RX)
                        mcs_get_rx_secy_stats(mcs, &secy_st, id);
                else
                        mcs_get_tx_secy_stats(mcs, &secy_st, id);
                break;
        case MCS_SC_STATS:
                mcs_get_sc_stats(mcs, &sc_st, id, dir);
                break;
        case MCS_SA_STATS:
                mcs_get_sa_stats(mcs, &sa_st, id, dir);
                break;
        case MCS_PORT_STATS:
                mcs_get_port_stats(mcs, &port_st, id, dir);
                break;
        }

        mcs_reg_write(mcs, reg, 0x0);
}

int mcs_clear_all_stats(struct mcs *mcs, u16 pcifunc, int dir)
{
        struct mcs_rsrc_map *map;
        int id;

        if (dir == MCS_RX)
                map = &mcs->rx;
        else
                map = &mcs->tx;

        /* Clear FLOWID stats */
        for (id = 0; id < map->flow_ids.max; id++) {
                if (map->flowid2pf_map[id] != pcifunc)
                        continue;
                mcs_clear_stats(mcs, MCS_FLOWID_STATS, id, dir);
        }

        /* Clear SECY stats */
        for (id = 0; id < map->secy.max; id++) {
                if (map->secy2pf_map[id] != pcifunc)
                        continue;
                mcs_clear_stats(mcs, MCS_SECY_STATS, id, dir);
        }

        /* Clear SC stats */
        for (id = 0; id < map->secy.max; id++) {
                if (map->sc2pf_map[id] != pcifunc)
                        continue;
                mcs_clear_stats(mcs, MCS_SC_STATS, id, dir);
        }

        /* Clear SA stats */
        for (id = 0; id < map->sa.max; id++) {
                if (map->sa2pf_map[id] != pcifunc)
                        continue;
                mcs_clear_stats(mcs, MCS_SA_STATS, id, dir);
        }
        return 0;
}

void mcs_pn_table_write(struct mcs *mcs, u8 pn_id, u64 next_pn, u8 dir)
{
        u64 reg;

        if (dir == MCS_RX)
                reg = MCSX_CPM_RX_SLAVE_SA_PN_TABLE_MEMX(pn_id);
        else
                reg = MCSX_CPM_TX_SLAVE_SA_PN_TABLE_MEMX(pn_id);
        mcs_reg_write(mcs, reg, next_pn);
}

void cn10kb_mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map)
{
        u64 reg, val;

        val = (map->sa_index0 & 0xFF) |
              (map->sa_index1 & 0xFF) << 9 |
              (map->rekey_ena & 0x1) << 18 |
              (map->sa_index0_vld & 0x1) << 19 |
              (map->sa_index1_vld & 0x1) << 20 |
              (map->tx_sa_active & 0x1) << 21 |
              map->sectag_sci << 22;
        reg = MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(map->sc_id);
        mcs_reg_write(mcs, reg, val);

        val = map->sectag_sci >> 42;
        reg = MCSX_CPM_TX_SLAVE_SA_MAP_MEM_1X(map->sc_id);
        mcs_reg_write(mcs, reg, val);
}

void cn10kb_mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map)
{
        u64 val, reg;

        val = (map->sa_index & 0xFF) | map->sa_in_use << 9;

        reg = MCSX_CPM_RX_SLAVE_SA_MAP_MEMX((4 * map->sc_id) + map->an);
        mcs_reg_write(mcs, reg, val);
}

void mcs_sa_plcy_write(struct mcs *mcs, u64 *plcy, int sa_id, int dir)
{
        int reg_id;
        u64 reg;

        if (dir == MCS_RX) {
                for (reg_id = 0; reg_id < 8; reg_id++) {
                        reg =  MCSX_CPM_RX_SLAVE_SA_PLCY_MEMX(reg_id, sa_id);
                        mcs_reg_write(mcs, reg, plcy[reg_id]);
                }
        } else {
                for (reg_id = 0; reg_id < 9; reg_id++) {
                        reg =  MCSX_CPM_TX_SLAVE_SA_PLCY_MEMX(reg_id, sa_id);
                        mcs_reg_write(mcs, reg, plcy[reg_id]);
                }
        }
}

void mcs_ena_dis_sc_cam_entry(struct mcs *mcs, int sc_id, int ena)
{
        u64 reg, val;

        reg = MCSX_CPM_RX_SLAVE_SC_CAM_ENA(0);
        if (sc_id > 63)
                reg = MCSX_CPM_RX_SLAVE_SC_CAM_ENA(1);

        if (ena)
                val = mcs_reg_read(mcs, reg) | BIT_ULL(sc_id);
        else
                val = mcs_reg_read(mcs, reg) & ~BIT_ULL(sc_id);

        mcs_reg_write(mcs, reg, val);
}

void mcs_rx_sc_cam_write(struct mcs *mcs, u64 sci, u64 secy, int sc_id)
{
        mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SC_CAMX(0, sc_id), sci);
        mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SC_CAMX(1, sc_id), secy);
        /* Enable SC CAM */
        mcs_ena_dis_sc_cam_entry(mcs, sc_id, true);
}

void mcs_secy_plcy_write(struct mcs *mcs, u64 plcy, int secy_id, int dir)
{
        u64 reg;

        if (dir == MCS_RX)
                reg = MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_0X(secy_id);
        else
                reg = MCSX_CPM_TX_SLAVE_SECY_PLCY_MEMX(secy_id);

        mcs_reg_write(mcs, reg, plcy);

        if (mcs->hw->mcs_blks == 1 && dir == MCS_RX)
                mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_1X(secy_id), 0x0ull);
}

void cn10kb_mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir)
{
        u64 reg, val;

        val = (map->secy & 0x7F) | (map->ctrl_pkt & 0x1) << 8;
        if (dir == MCS_RX) {
                reg = MCSX_CPM_RX_SLAVE_SECY_MAP_MEMX(map->flow_id);
        } else {
                val |= (map->sc & 0x7F) << 9;
                reg = MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_0X(map->flow_id);
        }

        mcs_reg_write(mcs, reg, val);
}

void mcs_ena_dis_flowid_entry(struct mcs *mcs, int flow_id, int dir, int ena)
{
        u64 reg, val;

        if (dir == MCS_RX) {
                reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_0;
                if (flow_id > 63)
                        reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_1;
        } else {
                reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_0;
                if (flow_id > 63)
                        reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_1;
        }

        /* Enable/Disable the tcam entry */
        if (ena)
                val = mcs_reg_read(mcs, reg) | BIT_ULL(flow_id);
        else
                val = mcs_reg_read(mcs, reg) & ~BIT_ULL(flow_id);

        mcs_reg_write(mcs, reg, val);
}

void mcs_flowid_entry_write(struct mcs *mcs, u64 *data, u64 *mask, int flow_id, int dir)
{
        int reg_id;
        u64 reg;

        if (dir == MCS_RX) {
                for (reg_id = 0; reg_id < 4; reg_id++) {
                        reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_DATAX(reg_id, flow_id);
                        mcs_reg_write(mcs, reg, data[reg_id]);
                }
                for (reg_id = 0; reg_id < 4; reg_id++) {
                        reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id);
                        mcs_reg_write(mcs, reg, mask[reg_id]);
                }
        } else {
                for (reg_id = 0; reg_id < 4; reg_id++) {
                        reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_DATAX(reg_id, flow_id);
                        mcs_reg_write(mcs, reg, data[reg_id]);
                }
                for (reg_id = 0; reg_id < 4; reg_id++) {
                        reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id);
                        mcs_reg_write(mcs, reg, mask[reg_id]);
                }
        }
}

int mcs_install_flowid_bypass_entry(struct mcs *mcs)
{
        int flow_id, secy_id, reg_id;
        struct secy_mem_map map;
        u64 reg, plcy = 0;

        /* Flow entry */
        flow_id = mcs->hw->tcam_entries - MCS_RSRC_RSVD_CNT;
        __set_bit(flow_id, mcs->rx.flow_ids.bmap);
        __set_bit(flow_id, mcs->tx.flow_ids.bmap);

        for (reg_id = 0; reg_id < 4; reg_id++) {
                reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id);
                mcs_reg_write(mcs, reg, GENMASK_ULL(63, 0));
        }
        for (reg_id = 0; reg_id < 4; reg_id++) {
                reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id);
                mcs_reg_write(mcs, reg, GENMASK_ULL(63, 0));
        }
        /* secy */
        secy_id = mcs->hw->secy_entries - MCS_RSRC_RSVD_CNT;
        __set_bit(secy_id, mcs->rx.secy.bmap);
        __set_bit(secy_id, mcs->tx.secy.bmap);

        /* Set validate frames to NULL and enable control port */
        plcy = 0x7ull;
        if (mcs->hw->mcs_blks > 1)
                plcy = BIT_ULL(0) | 0x3ull << 4;
        mcs_secy_plcy_write(mcs, plcy, secy_id, MCS_RX);

        /* Enable control port and set mtu to max */
        plcy = BIT_ULL(0) | GENMASK_ULL(43, 28);
        if (mcs->hw->mcs_blks > 1)
                plcy = BIT_ULL(0) | GENMASK_ULL(63, 48);
        mcs_secy_plcy_write(mcs, plcy, secy_id, MCS_TX);

        /* Map flowid to secy */
        map.secy = secy_id;
        map.ctrl_pkt = 0;
        map.flow_id = flow_id;
        mcs->mcs_ops->mcs_flowid_secy_map(mcs, &map, MCS_RX);
        map.sc = secy_id;
        mcs->mcs_ops->mcs_flowid_secy_map(mcs, &map, MCS_TX);

        /* Enable Flowid entry */
        mcs_ena_dis_flowid_entry(mcs, flow_id, MCS_RX, true);
        mcs_ena_dis_flowid_entry(mcs, flow_id, MCS_TX, true);

        return 0;
}

void mcs_clear_secy_plcy(struct mcs *mcs, int secy_id, int dir)
{
        struct mcs_rsrc_map *map;
        int flow_id;

        if (dir == MCS_RX)
                map = &mcs->rx;
        else
                map = &mcs->tx;

        /* Clear secy memory to zero */
        mcs_secy_plcy_write(mcs, 0, secy_id, dir);

        /* Disable the tcam entry using this secy */
        for (flow_id = 0; flow_id < map->flow_ids.max; flow_id++) {
                if (map->flowid2secy_map[flow_id] != secy_id)
                        continue;
                mcs_ena_dis_flowid_entry(mcs, flow_id, dir, false);
        }
}

int mcs_alloc_ctrlpktrule(struct rsrc_bmap *rsrc, u16 *pf_map, u16 offset, u16 pcifunc)
{
        int rsrc_id;

        if (!rsrc->bmap)
                return -EINVAL;

        rsrc_id = bitmap_find_next_zero_area(rsrc->bmap, rsrc->max, offset, 1, 0);
        if (rsrc_id >= rsrc->max)
                return -ENOSPC;

        bitmap_set(rsrc->bmap, rsrc_id, 1);
        pf_map[rsrc_id] = pcifunc;

        return rsrc_id;
}

int mcs_free_ctrlpktrule(struct mcs *mcs, struct mcs_free_ctrl_pkt_rule_req *req)
{
        u16 pcifunc = req->hdr.pcifunc;
        struct mcs_rsrc_map *map;
        u64 dis, reg;
        int id, rc;

        reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ENABLE : MCSX_PEX_TX_SLAVE_RULE_ENABLE;
        map = (req->dir == MCS_RX) ? &mcs->rx : &mcs->tx;

        if (req->all) {
                for (id = 0; id < map->ctrlpktrule.max; id++) {
                        if (map->ctrlpktrule2pf_map[id] != pcifunc)
                                continue;
                        mcs_free_rsrc(&map->ctrlpktrule, map->ctrlpktrule2pf_map, id, pcifunc);
                        dis = mcs_reg_read(mcs, reg);
                        dis &= ~BIT_ULL(id);
                        mcs_reg_write(mcs, reg, dis);
                }
                return 0;
        }

        rc = mcs_free_rsrc(&map->ctrlpktrule, map->ctrlpktrule2pf_map, req->rule_idx, pcifunc);
        dis = mcs_reg_read(mcs, reg);
        dis &= ~BIT_ULL(req->rule_idx);
        mcs_reg_write(mcs, reg, dis);

        return rc;
}

int mcs_ctrlpktrule_write(struct mcs *mcs, struct mcs_ctrl_pkt_rule_write_req *req)
{
        u64 reg, enb;
        u64 idx;

        switch (req->rule_type) {
        case MCS_CTRL_PKT_RULE_TYPE_ETH:
                req->data0 &= GENMASK(15, 0);
                if (req->data0 != ETH_P_PAE)
                        return -EINVAL;

                idx = req->rule_idx - MCS_CTRLPKT_ETYPE_RULE_OFFSET;
                reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ETYPE_CFGX(idx) :
                      MCSX_PEX_TX_SLAVE_RULE_ETYPE_CFGX(idx);

                mcs_reg_write(mcs, reg, req->data0);
                break;
        case MCS_CTRL_PKT_RULE_TYPE_DA:
                if (!(req->data0 & BIT_ULL(40)))
                        return -EINVAL;

                idx = req->rule_idx - MCS_CTRLPKT_DA_RULE_OFFSET;
                reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_DAX(idx) :
                      MCSX_PEX_TX_SLAVE_RULE_DAX(idx);

                mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                break;
        case MCS_CTRL_PKT_RULE_TYPE_RANGE:
                if (!(req->data0 & BIT_ULL(40)) || !(req->data1 & BIT_ULL(40)))
                        return -EINVAL;

                idx = req->rule_idx - MCS_CTRLPKT_DA_RANGE_RULE_OFFSET;
                if (req->dir == MCS_RX) {
                        reg = MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MINX(idx);
                        mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MAXX(idx);
                        mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0));
                } else {
                        reg = MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MINX(idx);
                        mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MAXX(idx);
                        mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0));
                }
                break;
        case MCS_CTRL_PKT_RULE_TYPE_COMBO:
                req->data2 &= GENMASK(15, 0);
                if (req->data2 != ETH_P_PAE || !(req->data0 & BIT_ULL(40)) ||
                    !(req->data1 & BIT_ULL(40)))
                        return -EINVAL;

                idx = req->rule_idx - MCS_CTRLPKT_COMBO_RULE_OFFSET;
                if (req->dir == MCS_RX) {
                        reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_MINX(idx);
                        mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_MAXX(idx);
                        mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_ETX(idx);
                        mcs_reg_write(mcs, reg, req->data2);
                } else {
                        reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_MINX(idx);
                        mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_MAXX(idx);
                        mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0));
                        reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_ETX(idx);
                        mcs_reg_write(mcs, reg, req->data2);
                }
                break;
        case MCS_CTRL_PKT_RULE_TYPE_MAC:
                if (!(req->data0 & BIT_ULL(40)))
                        return -EINVAL;

                idx = req->rule_idx - MCS_CTRLPKT_MAC_EN_RULE_OFFSET;
                reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_MAC :
                      MCSX_PEX_TX_SLAVE_RULE_MAC;

                mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0));
                break;
        }

        reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ENABLE : MCSX_PEX_TX_SLAVE_RULE_ENABLE;

        enb = mcs_reg_read(mcs, reg);
        enb |= BIT_ULL(req->rule_idx);
        mcs_reg_write(mcs, reg, enb);

        return 0;
}

int mcs_free_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, int rsrc_id, u16 pcifunc)
{
        /* Check if the rsrc_id is mapped to PF/VF */
        if (pf_map[rsrc_id] != pcifunc)
                return -EINVAL;

        rvu_free_rsrc(rsrc, rsrc_id);
        pf_map[rsrc_id] = 0;
        return 0;
}

/* Free all the cam resources mapped to pf */
int mcs_free_all_rsrc(struct mcs *mcs, int dir, u16 pcifunc)
{
        struct mcs_rsrc_map *map;
        int id;

        if (dir == MCS_RX)
                map = &mcs->rx;
        else
                map = &mcs->tx;

        /* free tcam entries */
        for (id = 0; id < map->flow_ids.max; id++) {
                if (map->flowid2pf_map[id] != pcifunc)
                        continue;
                mcs_free_rsrc(&map->flow_ids, map->flowid2pf_map,
                              id, pcifunc);
                mcs_ena_dis_flowid_entry(mcs, id, dir, false);
        }

        /* free secy entries */
        for (id = 0; id < map->secy.max; id++) {
                if (map->secy2pf_map[id] != pcifunc)
                        continue;
                mcs_free_rsrc(&map->secy, map->secy2pf_map,
                              id, pcifunc);
                mcs_clear_secy_plcy(mcs, id, dir);
        }

        /* free sc entries */
        for (id = 0; id < map->secy.max; id++) {
                if (map->sc2pf_map[id] != pcifunc)
                        continue;
                mcs_free_rsrc(&map->sc, map->sc2pf_map, id, pcifunc);

                /* Disable SC CAM only on RX side */
                if (dir == MCS_RX)
                        mcs_ena_dis_sc_cam_entry(mcs, id, false);
        }

        /* free sa entries */
        for (id = 0; id < map->sa.max; id++) {
                if (map->sa2pf_map[id] != pcifunc)
                        continue;
                mcs_free_rsrc(&map->sa, map->sa2pf_map, id, pcifunc);
        }
        return 0;
}

int mcs_alloc_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, u16 pcifunc)
{
        int rsrc_id;

        rsrc_id = rvu_alloc_rsrc(rsrc);
        if (rsrc_id < 0)
                return -ENOMEM;
        pf_map[rsrc_id] = pcifunc;
        return rsrc_id;
}

int mcs_alloc_all_rsrc(struct mcs *mcs, u8 *flow_id, u8 *secy_id,
                       u8 *sc_id, u8 *sa1_id, u8 *sa2_id, u16 pcifunc, int dir)
{
        struct mcs_rsrc_map *map;
        int id;

        if (dir == MCS_RX)
                map = &mcs->rx;
        else
                map = &mcs->tx;

        id = mcs_alloc_rsrc(&map->flow_ids, map->flowid2pf_map, pcifunc);
        if (id < 0)
                return -ENOMEM;
        *flow_id = id;

        id = mcs_alloc_rsrc(&map->secy, map->secy2pf_map, pcifunc);
        if (id < 0)
                return -ENOMEM;
        *secy_id = id;

        id = mcs_alloc_rsrc(&map->sc, map->sc2pf_map, pcifunc);
        if (id < 0)
                return -ENOMEM;
        *sc_id = id;

        id =  mcs_alloc_rsrc(&map->sa, map->sa2pf_map, pcifunc);
        if (id < 0)
                return -ENOMEM;
        *sa1_id = id;

        id =  mcs_alloc_rsrc(&map->sa, map->sa2pf_map, pcifunc);
        if (id < 0)
                return -ENOMEM;
        *sa2_id = id;

        return 0;
}

static void cn10kb_mcs_tx_pn_wrapped_handler(struct mcs *mcs)
{
        struct mcs_intr_event event = { 0 };
        struct rsrc_bmap *sc_bmap;
        u64 val;
        int sc;

        sc_bmap = &mcs->tx.sc;

        event.mcs_id = mcs->mcs_id;
        event.intr_mask = MCS_CPM_TX_PACKET_XPN_EQ0_INT;

        for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) {
                val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc));

                if (mcs->tx_sa_active[sc])
                        /* SA_index1 was used and got expired */
                        event.sa_id = (val >> 9) & 0xFF;
                else
                        /* SA_index0 was used and got expired */
                        event.sa_id = val & 0xFF;

                event.pcifunc = mcs->tx.sa2pf_map[event.sa_id];
                mcs_add_intr_wq_entry(mcs, &event);
        }
}

static void cn10kb_mcs_tx_pn_thresh_reached_handler(struct mcs *mcs)
{
        struct mcs_intr_event event = { 0 };
        struct rsrc_bmap *sc_bmap;
        u64 val, status;
        int sc;

        sc_bmap = &mcs->tx.sc;

        event.mcs_id = mcs->mcs_id;
        event.intr_mask = MCS_CPM_TX_PN_THRESH_REACHED_INT;

        /* TX SA interrupt is raised only if autorekey is enabled.
         * MCS_CPM_TX_SLAVE_SA_MAP_MEM_0X[sc].tx_sa_active bit gets toggled if
         * one of two SAs mapped to SC gets expired. If tx_sa_active=0 implies
         * SA in SA_index1 got expired else SA in SA_index0 got expired.
         */
        for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) {
                val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc));
                /* Auto rekey is enable */
                if (!((val >> 18) & 0x1))
                        continue;

                status = (val >> 21) & 0x1;

                /* Check if tx_sa_active status had changed */
                if (status == mcs->tx_sa_active[sc])
                        continue;
                /* SA_index0 is expired */
                if (status)
                        event.sa_id = val & 0xFF;
                else
                        event.sa_id = (val >> 9) & 0xFF;

                event.pcifunc = mcs->tx.sa2pf_map[event.sa_id];
                mcs_add_intr_wq_entry(mcs, &event);
        }
}

static void mcs_rx_pn_thresh_reached_handler(struct mcs *mcs)
{
        struct mcs_intr_event event = { 0 };
        int sa, reg;
        u64 intr;

        /* Check expired SAs */
        for (reg = 0; reg < (mcs->hw->sa_entries / 64); reg++) {
                /* Bit high in *PN_THRESH_REACHEDX implies
                 * corresponding SAs are expired.
                 */
                intr = mcs_reg_read(mcs, MCSX_CPM_RX_SLAVE_PN_THRESH_REACHEDX(reg));
                for (sa = 0; sa < 64; sa++) {
                        if (!(intr & BIT_ULL(sa)))
                                continue;

                        event.mcs_id = mcs->mcs_id;
                        event.intr_mask = MCS_CPM_RX_PN_THRESH_REACHED_INT;
                        event.sa_id = sa + (reg * 64);
                        event.pcifunc = mcs->rx.sa2pf_map[event.sa_id];
                        mcs_add_intr_wq_entry(mcs, &event);
                }
        }
}

static void mcs_rx_misc_intr_handler(struct mcs *mcs, u64 intr)
{
        struct mcs_intr_event event = { 0 };

        event.mcs_id = mcs->mcs_id;
        event.pcifunc = mcs->pf_map[0];

        if (intr & MCS_CPM_RX_INT_SECTAG_V_EQ1)
                event.intr_mask = MCS_CPM_RX_SECTAG_V_EQ1_INT;
        if (intr & MCS_CPM_RX_INT_SECTAG_E_EQ0_C_EQ1)
                event.intr_mask |= MCS_CPM_RX_SECTAG_E_EQ0_C_EQ1_INT;
        if (intr & MCS_CPM_RX_INT_SL_GTE48)
                event.intr_mask |= MCS_CPM_RX_SECTAG_SL_GTE48_INT;
        if (intr & MCS_CPM_RX_INT_ES_EQ1_SC_EQ1)
                event.intr_mask |= MCS_CPM_RX_SECTAG_ES_EQ1_SC_EQ1_INT;
        if (intr & MCS_CPM_RX_INT_SC_EQ1_SCB_EQ1)
                event.intr_mask |= MCS_CPM_RX_SECTAG_SC_EQ1_SCB_EQ1_INT;
        if (intr & MCS_CPM_RX_INT_PACKET_XPN_EQ0)
                event.intr_mask |= MCS_CPM_RX_PACKET_XPN_EQ0_INT;

        mcs_add_intr_wq_entry(mcs, &event);
}

static void mcs_tx_misc_intr_handler(struct mcs *mcs, u64 intr)
{
        struct mcs_intr_event event = { 0 };

        if (!(intr & MCS_CPM_TX_INT_SA_NOT_VALID))
                return;

        event.mcs_id = mcs->mcs_id;
        event.pcifunc = mcs->pf_map[0];

        event.intr_mask = MCS_CPM_TX_SA_NOT_VALID_INT;

        mcs_add_intr_wq_entry(mcs, &event);
}

void cn10kb_mcs_bbe_intr_handler(struct mcs *mcs, u64 intr,
                                 enum mcs_direction dir)
{
        u64 val, reg;
        int lmac;

        if (!(intr & 0x6ULL))
                return;

        if (intr & BIT_ULL(1))
                reg = (dir == MCS_RX) ? MCSX_BBE_RX_SLAVE_DFIFO_OVERFLOW_0 :
                                        MCSX_BBE_TX_SLAVE_DFIFO_OVERFLOW_0;
        else
                reg = (dir == MCS_RX) ? MCSX_BBE_RX_SLAVE_PLFIFO_OVERFLOW_0 :
                                        MCSX_BBE_TX_SLAVE_PLFIFO_OVERFLOW_0;
        val = mcs_reg_read(mcs, reg);

        /* policy/data over flow occurred */
        for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) {
                if (!(val & BIT_ULL(lmac)))
                        continue;
                dev_warn(mcs->dev, "BEE:Policy or data overflow occurred on lmac:%d\n", lmac);
        }
}

void cn10kb_mcs_pab_intr_handler(struct mcs *mcs, u64 intr,
                                 enum mcs_direction dir)
{
        int lmac;

        if (!(intr & 0xFFFFFULL))
                return;

        for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) {
                if (intr & BIT_ULL(lmac))
                        dev_warn(mcs->dev, "PAB: overflow occurred on lmac:%d\n", lmac);
        }
}

static irqreturn_t mcs_ip_intr_handler(int irq, void *mcs_irq)
{
        struct mcs *mcs = (struct mcs *)mcs_irq;
        u64 intr, cpm_intr, bbe_intr, pab_intr;

        /* Disable  the interrupt */
        mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1C, BIT_ULL(0));

        /* Check which block has interrupt*/
        intr = mcs_reg_read(mcs, MCSX_TOP_SLAVE_INT_SUM);

        /* CPM RX */
        if (intr & MCS_CPM_RX_INT_ENA) {
                /* Check for PN thresh interrupt bit */
                cpm_intr = mcs_reg_read(mcs, MCSX_CPM_RX_SLAVE_RX_INT);

                if (cpm_intr & MCS_CPM_RX_INT_PN_THRESH_REACHED)
                        mcs_rx_pn_thresh_reached_handler(mcs);

                if (cpm_intr & MCS_CPM_RX_INT_ALL)
                        mcs_rx_misc_intr_handler(mcs, cpm_intr);

                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_RX_INT, cpm_intr);
        }

        /* CPM TX */
        if (intr & MCS_CPM_TX_INT_ENA) {
                cpm_intr = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_TX_INT);

                if (cpm_intr & MCS_CPM_TX_INT_PN_THRESH_REACHED) {
                        if (mcs->hw->mcs_blks > 1)
                                cnf10kb_mcs_tx_pn_thresh_reached_handler(mcs);
                        else
                                cn10kb_mcs_tx_pn_thresh_reached_handler(mcs);
                }

                if (cpm_intr & MCS_CPM_TX_INT_SA_NOT_VALID)
                        mcs_tx_misc_intr_handler(mcs, cpm_intr);

                if (cpm_intr & MCS_CPM_TX_INT_PACKET_XPN_EQ0) {
                        if (mcs->hw->mcs_blks > 1)
                                cnf10kb_mcs_tx_pn_wrapped_handler(mcs);
                        else
                                cn10kb_mcs_tx_pn_wrapped_handler(mcs);
                }
                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_TX_INT, cpm_intr);
        }

        /* BBE RX */
        if (intr & MCS_BBE_RX_INT_ENA) {
                bbe_intr = mcs_reg_read(mcs, MCSX_BBE_RX_SLAVE_BBE_INT);
                mcs->mcs_ops->mcs_bbe_intr_handler(mcs, bbe_intr, MCS_RX);

                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT_INTR_RW, 0);
                mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT, bbe_intr);
        }

        /* BBE TX */
        if (intr & MCS_BBE_TX_INT_ENA) {
                bbe_intr = mcs_reg_read(mcs, MCSX_BBE_TX_SLAVE_BBE_INT);
                mcs->mcs_ops->mcs_bbe_intr_handler(mcs, bbe_intr, MCS_TX);

                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT_INTR_RW, 0);
                mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT, bbe_intr);
        }

        /* PAB RX */
        if (intr & MCS_PAB_RX_INT_ENA) {
                pab_intr = mcs_reg_read(mcs, MCSX_PAB_RX_SLAVE_PAB_INT);
                mcs->mcs_ops->mcs_pab_intr_handler(mcs, pab_intr, MCS_RX);

                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT_INTR_RW, 0);
                mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT, pab_intr);
        }

        /* PAB TX */
        if (intr & MCS_PAB_TX_INT_ENA) {
                pab_intr = mcs_reg_read(mcs, MCSX_PAB_TX_SLAVE_PAB_INT);
                mcs->mcs_ops->mcs_pab_intr_handler(mcs, pab_intr, MCS_TX);

                /* Clear the interrupt */
                mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT_INTR_RW, 0);
                mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT, pab_intr);
        }

        /* Clear and enable the interrupt */
        mcs_reg_write(mcs, MCSX_IP_INT, BIT_ULL(0));
        mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1S, BIT_ULL(0));

        return IRQ_HANDLED;
}

static void *alloc_mem(struct mcs *mcs, int n)
{
        return devm_kcalloc(mcs->dev, n, sizeof(u16), GFP_KERNEL);
}

static int mcs_alloc_struct_mem(struct mcs *mcs, struct mcs_rsrc_map *res)
{
        struct hwinfo *hw = mcs->hw;
        int err;

        res->flowid2pf_map = alloc_mem(mcs, hw->tcam_entries);
        if (!res->flowid2pf_map)
                return -ENOMEM;

        res->secy2pf_map = alloc_mem(mcs, hw->secy_entries);
        if (!res->secy2pf_map)
                return -ENOMEM;

        res->sc2pf_map = alloc_mem(mcs, hw->sc_entries);
        if (!res->sc2pf_map)
                return -ENOMEM;

        res->sa2pf_map = alloc_mem(mcs, hw->sa_entries);
        if (!res->sa2pf_map)
                return -ENOMEM;

        res->flowid2secy_map = alloc_mem(mcs, hw->tcam_entries);
        if (!res->flowid2secy_map)
                return -ENOMEM;

        res->ctrlpktrule2pf_map = alloc_mem(mcs, MCS_MAX_CTRLPKT_RULES);
        if (!res->ctrlpktrule2pf_map)
                return -ENOMEM;

        res->flow_ids.max = hw->tcam_entries - MCS_RSRC_RSVD_CNT;
        err = rvu_alloc_bitmap(&res->flow_ids);
        if (err)
                return err;

        res->secy.max = hw->secy_entries - MCS_RSRC_RSVD_CNT;
        err = rvu_alloc_bitmap(&res->secy);
        if (err)
                return err;

        res->sc.max = hw->sc_entries;
        err = rvu_alloc_bitmap(&res->sc);
        if (err)
                return err;

        res->sa.max = hw->sa_entries;
        err = rvu_alloc_bitmap(&res->sa);
        if (err)
                return err;

        res->ctrlpktrule.max = MCS_MAX_CTRLPKT_RULES;
        err = rvu_alloc_bitmap(&res->ctrlpktrule);
        if (err)
                return err;

        return 0;
}

static int mcs_register_interrupts(struct mcs *mcs)
{
        int ret = 0;

        mcs->num_vec = pci_msix_vec_count(mcs->pdev);

        ret = pci_alloc_irq_vectors(mcs->pdev, mcs->num_vec,
                                    mcs->num_vec, PCI_IRQ_MSIX);
        if (ret < 0) {
                dev_err(mcs->dev, "MCS Request for %d msix vector failed err:%d\n",
                        mcs->num_vec, ret);
                return ret;
        }

        ret = request_irq(pci_irq_vector(mcs->pdev, mcs->hw->ip_vec),
                          mcs_ip_intr_handler, 0, "MCS_IP", mcs);
        if (ret) {
                dev_err(mcs->dev, "MCS IP irq registration failed\n");
                goto exit;
        }

        /* MCS enable IP interrupts */
        mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1S, BIT_ULL(0));

        /* Enable CPM Rx/Tx interrupts */
        mcs_reg_write(mcs, MCSX_TOP_SLAVE_INT_SUM_ENB,
                      MCS_CPM_RX_INT_ENA | MCS_CPM_TX_INT_ENA |
                      MCS_BBE_RX_INT_ENA | MCS_BBE_TX_INT_ENA |
                      MCS_PAB_RX_INT_ENA | MCS_PAB_TX_INT_ENA);

        mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_TX_INT_ENB, 0x7ULL);
        mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_RX_INT_ENB, 0x7FULL);

        mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT_ENB, 0xFFULL);
        mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT_ENB, 0xFFULL);

        mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT_ENB, 0xFFFFFULL);
        mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT_ENB, 0xFFFFFULL);

        mcs->tx_sa_active = alloc_mem(mcs, mcs->hw->sc_entries);
        if (!mcs->tx_sa_active) {
                ret = -ENOMEM;
                goto free_irq;
        }

        return ret;

free_irq:
        free_irq(pci_irq_vector(mcs->pdev, mcs->hw->ip_vec), mcs);
exit:
        pci_free_irq_vectors(mcs->pdev);
        mcs->num_vec = 0;
        return ret;
}

int mcs_get_blkcnt(void)
{
        struct mcs *mcs;
        int idmax = -ENODEV;

        /* Check MCS block is present in hardware */
        if (!pci_dev_present(mcs_id_table))
                return 0;

        list_for_each_entry(mcs, &mcs_list, mcs_list)
                if (mcs->mcs_id > idmax)
                        idmax = mcs->mcs_id;

        if (idmax < 0)
                return 0;

        return idmax + 1;
}

struct mcs *mcs_get_pdata(int mcs_id)
{
        struct mcs *mcs_dev;

        list_for_each_entry(mcs_dev, &mcs_list, mcs_list) {
                if (mcs_dev->mcs_id == mcs_id)
                        return mcs_dev;
        }
        return NULL;
}

bool is_mcs_bypass(int mcs_id)
{
        struct mcs *mcs_dev;

        list_for_each_entry(mcs_dev, &mcs_list, mcs_list) {
                if (mcs_dev->mcs_id == mcs_id)
                        return mcs_dev->bypass;
        }
        return true;
}

void mcs_set_port_cfg(struct mcs *mcs, struct mcs_port_cfg_set_req *req)
{
        u64 val = 0;

        mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PORT_CFGX(req->port_id),
                      req->port_mode & MCS_PORT_MODE_MASK);

        req->cstm_tag_rel_mode_sel &= 0x3;

        if (mcs->hw->mcs_blks > 1) {
                req->fifo_skid &= MCS_PORT_FIFO_SKID_MASK;
                val = (u32)req->fifo_skid << 0x10;
                val |= req->fifo_skid;
                mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(req->port_id), val);
                mcs_reg_write(mcs, MCSX_PEX_TX_SLAVE_CUSTOM_TAG_REL_MODE_SEL(req->port_id),
                              req->cstm_tag_rel_mode_sel);
                val = mcs_reg_read(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION);

                if (req->custom_hdr_enb)
                        val |= BIT_ULL(req->port_id);
                else
                        val &= ~BIT_ULL(req->port_id);

                mcs_reg_write(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION, val);
        } else {
                val = mcs_reg_read(mcs, MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id));
                val |= (req->cstm_tag_rel_mode_sel << 2);
                mcs_reg_write(mcs, MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id), val);
        }
}

void mcs_get_port_cfg(struct mcs *mcs, struct mcs_port_cfg_get_req *req,
                      struct mcs_port_cfg_get_rsp *rsp)
{
        u64 reg = 0;

        rsp->port_mode = mcs_reg_read(mcs, MCSX_PAB_RX_SLAVE_PORT_CFGX(req->port_id)) &
                         MCS_PORT_MODE_MASK;

        if (mcs->hw->mcs_blks > 1) {
                reg = MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(req->port_id);
                rsp->fifo_skid = mcs_reg_read(mcs, reg) & MCS_PORT_FIFO_SKID_MASK;
                reg = MCSX_PEX_TX_SLAVE_CUSTOM_TAG_REL_MODE_SEL(req->port_id);
                rsp->cstm_tag_rel_mode_sel = mcs_reg_read(mcs, reg) & 0x3;
                if (mcs_reg_read(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION) & BIT_ULL(req->port_id))
                        rsp->custom_hdr_enb = 1;
        } else {
                reg = MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id);
                rsp->cstm_tag_rel_mode_sel = mcs_reg_read(mcs, reg) >> 2;
        }

        rsp->port_id = req->port_id;
        rsp->mcs_id = req->mcs_id;
}

void mcs_get_custom_tag_cfg(struct mcs *mcs, struct mcs_custom_tag_cfg_get_req *req,
                            struct mcs_custom_tag_cfg_get_rsp *rsp)
{
        u64 reg = 0, val = 0;
        u8 idx;

        for (idx = 0; idx < MCS_MAX_CUSTOM_TAGS; idx++) {
                if (mcs->hw->mcs_blks > 1)
                        reg  = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_CUSTOM_TAGX(idx) :
                                MCSX_PEX_TX_SLAVE_CUSTOM_TAGX(idx);
                else
                        reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_VLAN_CFGX(idx) :
                                MCSX_PEX_TX_SLAVE_VLAN_CFGX(idx);

                val = mcs_reg_read(mcs, reg);
                if (mcs->hw->mcs_blks > 1) {
                        rsp->cstm_etype[idx] = val & GENMASK(15, 0);
                        rsp->cstm_indx[idx] = (val >> 0x16) & 0x3;
                        reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_ETYPE_ENABLE :
                                MCSX_PEX_TX_SLAVE_ETYPE_ENABLE;
                        rsp->cstm_etype_en = mcs_reg_read(mcs, reg) & 0xFF;
                } else {
                        rsp->cstm_etype[idx] = (val >> 0x1) & GENMASK(15, 0);
                        rsp->cstm_indx[idx] = (val >> 0x11) & 0x3;
                        rsp->cstm_etype_en |= (val & 0x1) << idx;
                }
        }

        rsp->mcs_id = req->mcs_id;
        rsp->dir = req->dir;
}

void mcs_reset_port(struct mcs *mcs, u8 port_id, u8 reset)
{
        u64 reg = MCSX_MCS_TOP_SLAVE_PORT_RESET(port_id);

        mcs_reg_write(mcs, reg, reset & 0x1);
}

/* Set lmac to bypass/operational mode */
void mcs_set_lmac_mode(struct mcs *mcs, int lmac_id, u8 mode)
{
        u64 reg;
        int id = lmac_id * 2;

        reg = MCSX_MCS_TOP_SLAVE_CHANNEL_CFG(id);
        mcs_reg_write(mcs, reg, (u64)mode);
        reg = MCSX_MCS_TOP_SLAVE_CHANNEL_CFG((id + 1));
        mcs_reg_write(mcs, reg, (u64)mode);
}

void mcs_pn_threshold_set(struct mcs *mcs, struct mcs_set_pn_threshold *pn)
{
        u64 reg;

        if (pn->dir == MCS_RX)
                reg = pn->xpn ? MCSX_CPM_RX_SLAVE_XPN_THRESHOLD : MCSX_CPM_RX_SLAVE_PN_THRESHOLD;
        else
                reg = pn->xpn ? MCSX_CPM_TX_SLAVE_XPN_THRESHOLD : MCSX_CPM_TX_SLAVE_PN_THRESHOLD;

        mcs_reg_write(mcs, reg, pn->threshold);
}

void cn10kb_mcs_parser_cfg(struct mcs *mcs)
{
        u64 reg, val;

        /* VLAN CTag */
        val = BIT_ULL(0) | (0x8100ull & 0xFFFF) << 1 | BIT_ULL(17);
        /* RX */
        reg = MCSX_PEX_RX_SLAVE_VLAN_CFGX(0);
        mcs_reg_write(mcs, reg, val);

        /* TX */
        reg = MCSX_PEX_TX_SLAVE_VLAN_CFGX(0);
        mcs_reg_write(mcs, reg, val);

        /* VLAN STag */
        val = BIT_ULL(0) | (0x88a8ull & 0xFFFF) << 1 | BIT_ULL(18);
        /* RX */
        reg = MCSX_PEX_RX_SLAVE_VLAN_CFGX(1);
        mcs_reg_write(mcs, reg, val);

        /* TX */
        reg = MCSX_PEX_TX_SLAVE_VLAN_CFGX(1);
        mcs_reg_write(mcs, reg, val);
}

static void mcs_lmac_init(struct mcs *mcs, int lmac_id)
{
        u64 reg;

        /* Port mode 25GB */
        reg = MCSX_PAB_RX_SLAVE_PORT_CFGX(lmac_id);
        mcs_reg_write(mcs, reg, 0);

        if (mcs->hw->mcs_blks > 1) {
                reg = MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(lmac_id);
                mcs_reg_write(mcs, reg, 0xe000e);
                return;
        }

        reg = MCSX_PAB_TX_SLAVE_PORT_CFGX(lmac_id);
        mcs_reg_write(mcs, reg, 0);
}

int mcs_set_lmac_channels(int mcs_id, u16 base)
{
        struct mcs *mcs;
        int lmac;
        u64 cfg;

        mcs = mcs_get_pdata(mcs_id);
        if (!mcs)
                return -ENODEV;
        for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) {
                cfg = mcs_reg_read(mcs, MCSX_LINK_LMACX_CFG(lmac));
                cfg &= ~(MCSX_LINK_LMAC_BASE_MASK | MCSX_LINK_LMAC_RANGE_MASK);
                cfg |=  FIELD_PREP(MCSX_LINK_LMAC_RANGE_MASK, ilog2(16));
                cfg |=  FIELD_PREP(MCSX_LINK_LMAC_BASE_MASK, base);
                mcs_reg_write(mcs, MCSX_LINK_LMACX_CFG(lmac), cfg);
                base += 16;
        }
        return 0;
}

static int mcs_x2p_calibration(struct mcs *mcs)
{
        unsigned long timeout = jiffies + usecs_to_jiffies(20000);
        int i, err = 0;
        u64 val;

        /* set X2P calibration */
        val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL);
        val |= BIT_ULL(5);
        mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val);

        /* Wait for calibration to complete */
        while (!(mcs_reg_read(mcs, MCSX_MIL_RX_GBL_STATUS) & BIT_ULL(0))) {
                if (time_before(jiffies, timeout)) {
                        usleep_range(80, 100);
                        continue;
                } else {
                        err = -EBUSY;
                        dev_err(mcs->dev, "MCS X2P calibration failed..ignoring\n");
                        return err;
                }
        }

        val = mcs_reg_read(mcs, MCSX_MIL_RX_GBL_STATUS);
        for (i = 0; i < mcs->hw->mcs_x2p_intf; i++) {
                if (val & BIT_ULL(1 + i))
                        continue;
                err = -EBUSY;
                dev_err(mcs->dev, "MCS:%d didn't respond to X2P calibration\n", i);
        }
        /* Clear X2P calibrate */
        mcs_reg_write(mcs, MCSX_MIL_GLOBAL, mcs_reg_read(mcs, MCSX_MIL_GLOBAL) & ~BIT_ULL(5));

        return err;
}

static void mcs_set_external_bypass(struct mcs *mcs, bool bypass)
{
        u64 val;

        /* Set MCS to external bypass */
        val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL);
        if (bypass)
                val |= BIT_ULL(6);
        else
                val &= ~BIT_ULL(6);
        mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val);
        mcs->bypass = bypass;
}

static void mcs_global_cfg(struct mcs *mcs)
{
        /* Disable external bypass */
        mcs_set_external_bypass(mcs, false);

        /* Reset TX/RX stats memory */
        mcs_reg_write(mcs, MCSX_CSE_RX_SLAVE_STATS_CLEAR, 0x1F);
        mcs_reg_write(mcs, MCSX_CSE_TX_SLAVE_STATS_CLEAR, 0x1F);

        /* Set MCS to perform standard IEEE802.1AE macsec processing */
        if (mcs->hw->mcs_blks == 1) {
                mcs_reg_write(mcs, MCSX_IP_MODE, BIT_ULL(3));
                return;
        }

        mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_CAL_ENTRY, 0xe4);
        mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_CAL_LEN, 4);
}

void cn10kb_mcs_set_hw_capabilities(struct mcs *mcs)
{
        struct hwinfo *hw = mcs->hw;

        hw->tcam_entries = 128;         /* TCAM entries */
        hw->secy_entries  = 128;        /* SecY entries */
        hw->sc_entries = 128;           /* SC CAM entries */
        hw->sa_entries = 256;           /* SA entries */
        hw->lmac_cnt = 20;              /* lmacs/ports per mcs block */
        hw->mcs_x2p_intf = 5;           /* x2p clabration intf */
        hw->mcs_blks = 1;               /* MCS blocks */
        hw->ip_vec = MCS_CN10KB_INT_VEC_IP; /* IP vector */
}

static struct mcs_ops cn10kb_mcs_ops = {
        .mcs_set_hw_capabilities        = cn10kb_mcs_set_hw_capabilities,
        .mcs_parser_cfg                 = cn10kb_mcs_parser_cfg,
        .mcs_tx_sa_mem_map_write        = cn10kb_mcs_tx_sa_mem_map_write,
        .mcs_rx_sa_mem_map_write        = cn10kb_mcs_rx_sa_mem_map_write,
        .mcs_flowid_secy_map            = cn10kb_mcs_flowid_secy_map,
        .mcs_bbe_intr_handler           = cn10kb_mcs_bbe_intr_handler,
        .mcs_pab_intr_handler           = cn10kb_mcs_pab_intr_handler,
};

static int mcs_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
        struct device *dev = &pdev->dev;
        int lmac, err = 0;
        struct mcs *mcs;

        mcs = devm_kzalloc(dev, sizeof(*mcs), GFP_KERNEL);
        if (!mcs)
                return -ENOMEM;

        mcs->hw = devm_kzalloc(dev, sizeof(struct hwinfo), GFP_KERNEL);
        if (!mcs->hw)
                return -ENOMEM;

        err = pci_enable_device(pdev);
        if (err) {
                dev_err(dev, "Failed to enable PCI device\n");
                pci_set_drvdata(pdev, NULL);
                return err;
        }

        err = pci_request_regions(pdev, DRV_NAME);
        if (err) {
                dev_err(dev, "PCI request regions failed 0x%x\n", err);
                goto exit;
        }

        mcs->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
        if (!mcs->reg_base) {
                dev_err(dev, "mcs: Cannot map CSR memory space, aborting\n");
                err = -ENOMEM;
                goto exit;
        }

        pci_set_drvdata(pdev, mcs);
        mcs->pdev = pdev;
        mcs->dev = &pdev->dev;

        if (pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_B)
                mcs->mcs_ops = &cn10kb_mcs_ops;
        else
                mcs->mcs_ops = cnf10kb_get_mac_ops();

        /* Set hardware capabilities */
        mcs->mcs_ops->mcs_set_hw_capabilities(mcs);

        mcs_global_cfg(mcs);

        /* Perform X2P clibration */
        err = mcs_x2p_calibration(mcs);
        if (err)
                goto err_x2p;

        mcs->mcs_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24)
                        & MCS_ID_MASK;

        /* Set mcs tx side resources */
        err = mcs_alloc_struct_mem(mcs, &mcs->tx);
        if (err)
                goto err_x2p;

        /* Set mcs rx side resources */
        err = mcs_alloc_struct_mem(mcs, &mcs->rx);
        if (err)
                goto err_x2p;

        /* per port config */
        for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++)
                mcs_lmac_init(mcs, lmac);

        /* Parser configuration */
        mcs->mcs_ops->mcs_parser_cfg(mcs);

        err = mcs_register_interrupts(mcs);
        if (err)
                goto exit;

        list_add(&mcs->mcs_list, &mcs_list);
        mutex_init(&mcs->stats_lock);

        return 0;

err_x2p:
        /* Enable external bypass */
        mcs_set_external_bypass(mcs, true);
exit:
        pci_release_regions(pdev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
        return err;
}

static void mcs_remove(struct pci_dev *pdev)
{
        struct mcs *mcs = pci_get_drvdata(pdev);

        /* Set MCS to external bypass */
        mcs_set_external_bypass(mcs, true);
        free_irq(pci_irq_vector(pdev, mcs->hw->ip_vec), mcs);
        pci_free_irq_vectors(pdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
}

struct pci_driver mcs_driver = {
        .name = DRV_NAME,
        .id_table = mcs_id_table,
        .probe = mcs_probe,
        .remove = mcs_remove,
};