SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4_hw.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += clip_tbl.c
+SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += mps_tcam.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += l2t.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4vf_hw.c
 
 
        unsigned int l2t_end;     /* Layer 2 table end */
        struct clip_tbl *clipt;   /* CLIP table */
        struct l2t_data *l2t;     /* Layer 2 table */
+       struct mpstcam_table *mpstcam;
 
        struct tid_info tids;     /* Info used to access TID related tables */
 };
 
 int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
                  int mtu, int promisc, int all_multi, int bcast, int vlanex,
                  bool sleep_ok);
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok);
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok);
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
                  int idx, const u8 *addr, bool persist, bool add_smt);
 int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
 
                return t4vf_wr_mbox(adap, &c, sizeof(c), NULL);
 }
 
+/**
+ *     t4_alloc_raw_mac_filt - Adds a raw mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @mac: the MAC address
+ *     @mask: the mask
+ *     @idx: index at which to add this entry
+ *     @port_id: the port index
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Adds the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number or the allocated index for this mac.
+ */
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       int ret = 0;
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 val;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(V_FW_CMD_OP(FW_VI_MAC_CMD) |
+                                  F_FW_CMD_REQUEST | F_FW_CMD_WRITE |
+                                  V_FW_VI_MAC_CMD_VIID(viid));
+       val = V_FW_CMD_LEN16(1) |
+             V_FW_VI_MAC_CMD_ENTRY_TYPE(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(val);
+
+       /* Specify that this is an inner mac address */
+       p->raw_idx_pkd = cpu_to_be32(V_FW_VI_MAC_CMD_RAW_IDX(idx));
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(V_DATALKPTYPE(lookup_type) |
+                                  V_DATAPORTNUM(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(V_DATALKPTYPE(M_DATALKPTYPE) |
+                                   V_DATAPORTNUM(M_DATAPORTNUM));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETHER_ADDR_LEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETHER_ADDR_LEN);
+
+       ret = t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+       if (ret == 0) {
+               ret = G_FW_VI_MAC_CMD_RAW_IDX(be32_to_cpu(p->raw_idx_pkd));
+               if (ret != (int)idx)
+                       ret = -ENOMEM;
+       }
+
+       return ret;
+}
+
+/**
+ *     t4_free_raw_mac_filt - Frees a raw mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @addr: the MAC address
+ *     @mask: the mask
+ *     @idx: index of the entry in mps tcam
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @port_id: the port index
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Removes the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number on failure.
+ */
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 raw;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(V_FW_CMD_OP(FW_VI_MAC_CMD) |
+                                  F_FW_CMD_REQUEST | F_FW_CMD_WRITE |
+                                  V_FW_CMD_EXEC(0) |
+                                  V_FW_VI_MAC_CMD_VIID(viid));
+       raw = V_FW_VI_MAC_CMD_ENTRY_TYPE(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(V_FW_VI_MAC_CMD_FREEMACS(0) |
+                                         raw |
+                                         V_FW_CMD_LEN16(1));
+
+       p->raw_idx_pkd = cpu_to_be32(V_FW_VI_MAC_CMD_RAW_IDX(idx) |
+                                    FW_VI_MAC_ID_BASED_FREE);
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(V_DATALKPTYPE(lookup_type) |
+                                  V_DATAPORTNUM(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(V_DATALKPTYPE(M_DATALKPTYPE) |
+                                   V_DATAPORTNUM(M_DATAPORTNUM));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETHER_ADDR_LEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETHER_ADDR_LEN);
+
+       return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+}
+
 /**
  * t4_change_mac - modifies the exact-match filter for a MAC address
  * @adap: the adapter
 
 #define MPS_T5_CLS_SRAM_H(idx) (A_MPS_T5_CLS_SRAM_H + (idx) * 8)
 #define NUM_MPS_T5_CLS_SRAM_H_INSTANCES 512
 
+#define S_DATAPORTNUM    12
+#define M_DATAPORTNUM    0xfU
+#define V_DATAPORTNUM(x) ((x) << S_DATAPORTNUM)
+
+#define S_DATALKPTYPE    10
+#define M_DATALKPTYPE    0x3U
+#define V_DATALKPTYPE(x) ((x) << S_DATALKPTYPE)
+
 /* registers for module SGE */
 #define SGE_BASE_ADDR 0x1000
 
 
 /* Special VI_MAC command index ids */
 #define FW_VI_MAC_ADD_MAC              0x3FF
 #define FW_VI_MAC_ADD_PERSIST_MAC      0x3FE
+#define FW_VI_MAC_ID_BASED_FREE         0x3FC
 
 enum fw_vi_mac_smac {
        FW_VI_MAC_MPS_TCAM_ENTRY,
        FW_VI_MAC_SMT_AND_MPSTCAM
 };
 
+enum fw_vi_mac_entry_types {
+       FW_VI_MAC_TYPE_RAW = 0x2,
+};
+
 struct fw_vi_mac_cmd {
        __be32 op_to_viid;
        __be32 freemacs_to_len16;
                struct fw_vi_mac_hash {
                        __be64 hashvec;
                } hash;
+               struct fw_vi_mac_raw {
+                       __be32 raw_idx_pkd;
+                       __be32 data0_pkd;
+                       __be32 data1[2];
+                       __be64 data0m_pkd;
+                       __be32 data1m[2];
+               } raw;
        } u;
 };
 
 #define G_FW_VI_MAC_CMD_VIID(x)        \
        (((x) >> S_FW_VI_MAC_CMD_VIID) & M_FW_VI_MAC_CMD_VIID)
 
+#define S_FW_VI_MAC_CMD_FREEMACS       31
+#define V_FW_VI_MAC_CMD_FREEMACS(x)    ((x) << S_FW_VI_MAC_CMD_FREEMACS)
+
+#define S_FW_VI_MAC_CMD_ENTRY_TYPE      23
+#define V_FW_VI_MAC_CMD_ENTRY_TYPE(x)   ((x) << S_FW_VI_MAC_CMD_ENTRY_TYPE)
+
 #define S_FW_VI_MAC_CMD_VALID          15
 #define M_FW_VI_MAC_CMD_VALID          0x1
 #define V_FW_VI_MAC_CMD_VALID(x)       ((x) << S_FW_VI_MAC_CMD_VALID)
 #define G_FW_VI_MAC_CMD_IDX(x) \
        (((x) >> S_FW_VI_MAC_CMD_IDX) & M_FW_VI_MAC_CMD_IDX)
 
+#define S_FW_VI_MAC_CMD_RAW_IDX         16
+#define M_FW_VI_MAC_CMD_RAW_IDX         0xffff
+#define V_FW_VI_MAC_CMD_RAW_IDX(x)      ((x) << S_FW_VI_MAC_CMD_RAW_IDX)
+#define G_FW_VI_MAC_CMD_RAW_IDX(x)      \
+       (((x) >> S_FW_VI_MAC_CMD_RAW_IDX) & M_FW_VI_MAC_CMD_RAW_IDX)
+
 struct fw_vi_rxmode_cmd {
        __be32 op_to_viid;
        __be32 retval_len16;
 
 #include "cxgbe.h"
 #include "clip_tbl.h"
 #include "l2t.h"
+#include "mps_tcam.h"
 
 /**
  * Allocate a chunk of memory. The allocated memory is cleared.
 
        if (adapter->flags & FULL_INIT_DONE) {
                tid_free(&adapter->tids);
+               t4_cleanup_mpstcam(adapter);
                t4_cleanup_clip_tbl(adapter);
                t4_cleanup_l2t(adapter);
                if (is_pf4(adapter))
                         "filter support disabled. Continuing\n");
        }
 
+       adapter->mpstcam = t4_init_mpstcam(adapter);
+       if (!adapter->mpstcam)
+               dev_warn(adapter, "could not allocate mps tcam table."
+                        " Continuing\n");
+
        if (is_hashfilter(adapter)) {
                if (t4_read_reg(adapter, A_LE_DB_CONFIG) & F_HASHEN) {
                        u32 hash_base, hash_reg;
 
        'cxgbe_filter.c',
        'cxgbe_flow.c',
        'clip_tbl.c',
+       'mps_tcam.c',
        'l2t.c',
        'base/t4_hw.c',
        'base/t4vf_hw.c')
 
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#include "mps_tcam.h"
+
+static inline bool
+match_entry(struct mps_tcam_entry *entry, const u8 *eth_addr, const u8 *mask)
+{
+       if (!memcmp(eth_addr, entry->eth_addr, ETHER_ADDR_LEN) &&
+           !memcmp(mask, entry->mask, ETHER_ADDR_LEN))
+               return true;
+       return false;
+}
+
+static int cxgbe_update_free_idx(struct mpstcam_table *t)
+{
+       struct mps_tcam_entry *entry = t->entry;
+       u16 i, next = t->free_idx + 1;
+
+       if (entry[t->free_idx].state == MPS_ENTRY_UNUSED)
+               /* You are already pointing to a free entry !! */
+               return 0;
+
+       /* loop, till we don't rollback to same index where we started */
+       for (i = next; i != t->free_idx; i++) {
+               if (i == t->size)
+                       /* rollback and search free entry from start */
+                       i = 0;
+
+               if (entry[i].state == MPS_ENTRY_UNUSED) {
+                       t->free_idx = i;
+                       return 0;
+               }
+       }
+
+       return -1;      /* table is full */
+}
+
+static struct mps_tcam_entry *
+cxgbe_mpstcam_lookup(struct mpstcam_table *t, const u8 *eth_addr,
+                    const u8 *mask)
+{
+       struct mps_tcam_entry *entry = t->entry;
+       int i;
+
+       if (!entry)
+               return NULL;
+
+       for (i = 0; i < t->size; i++) {
+               if (entry[i].state == MPS_ENTRY_UNUSED)
+                       continue;       /* entry is not being used */
+               if (match_entry(&entry[i], eth_addr, mask))
+                       return &entry[i];
+       }
+
+       return NULL;
+}
+
+int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *eth_addr,
+                       const u8 *mask)
+{
+       struct adapter *adap = pi->adapter;
+       struct mpstcam_table *mpstcam = adap->mpstcam;
+       struct mps_tcam_entry *entry;
+       int ret;
+
+       if (!adap->mpstcam) {
+               dev_err(adap, "mpstcam table is not available\n");
+               return -EOPNOTSUPP;
+       }
+
+       /* If entry already present, return it. */
+       t4_os_write_lock(&mpstcam->lock);
+       entry = cxgbe_mpstcam_lookup(adap->mpstcam, eth_addr, mask);
+       if (entry) {
+               rte_atomic32_add(&entry->refcnt, 1);
+               t4_os_write_unlock(&mpstcam->lock);
+               return entry->idx;
+       }
+
+       if (mpstcam->full) {
+               t4_os_write_unlock(&mpstcam->lock);
+               dev_err(adap, "mps-tcam table is full\n");
+               return -ENOMEM;
+       }
+
+       ret = t4_alloc_raw_mac_filt(adap, pi->viid, eth_addr, mask,
+                                   mpstcam->free_idx, 0, pi->port_id, false);
+       if (ret <= 0) {
+               t4_os_write_unlock(&mpstcam->lock);
+               return ret;
+       }
+
+       /* Fill in the new values */
+       entry = &mpstcam->entry[ret];
+       memcpy(entry->eth_addr, eth_addr, ETHER_ADDR_LEN);
+       memcpy(entry->mask, mask, ETHER_ADDR_LEN);
+       rte_atomic32_set(&entry->refcnt, 1);
+       entry->state = MPS_ENTRY_USED;
+
+       if (cxgbe_update_free_idx(mpstcam))
+               mpstcam->full = true;
+
+       t4_os_write_unlock(&mpstcam->lock);
+       return ret;
+}
+
+int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr)
+{
+       struct adapter *adap = pi->adapter;
+       struct mpstcam_table *mpstcam = adap->mpstcam;
+       struct mps_tcam_entry *entry;
+
+       if (!mpstcam)
+               return -EOPNOTSUPP;
+       t4_os_write_lock(&mpstcam->lock);
+       if (idx != -1 && idx >= mpstcam->size) {
+               t4_os_write_unlock(&mpstcam->lock);
+               return -EINVAL;
+       }
+       if (idx >= 0) {
+               entry = &mpstcam->entry[idx];
+               /* user wants to modify an existing entry.
+                * verify if entry exists
+                */
+               if (entry->state != MPS_ENTRY_USED) {
+                       t4_os_write_unlock(&mpstcam->lock);
+                       return -EINVAL;
+               }
+       }
+
+       idx = t4_change_mac(adap, adap->mbox, pi->viid, idx, addr, true, true);
+       if (idx < 0) {
+               t4_os_write_unlock(&mpstcam->lock);
+               return idx;
+       }
+
+       /* idx can now be different from what user provided */
+       entry = &mpstcam->entry[idx];
+       memcpy(entry->eth_addr, addr, ETHER_ADDR_LEN);
+       /* NOTE: we have considered the case that idx returned by t4_change_mac
+        * will be different from the user provided value only if user
+        * provided value is -1
+        */
+       if (entry->state == MPS_ENTRY_UNUSED) {
+               rte_atomic32_set(&entry->refcnt, 1);
+               entry->state = MPS_ENTRY_USED;
+       }
+
+       if (cxgbe_update_free_idx(mpstcam))
+               mpstcam->full = true;
+
+       t4_os_write_unlock(&mpstcam->lock);
+       return idx;
+}
+
+/**
+ * hold appropriate locks while calling this.
+ */
+static inline void reset_mpstcam_entry(struct mps_tcam_entry *entry)
+{
+       memset(entry->eth_addr, 0, ETHER_ADDR_LEN);
+       memset(entry->mask, 0, ETHER_ADDR_LEN);
+       rte_atomic32_clear(&entry->refcnt);
+       entry->state = MPS_ENTRY_UNUSED;
+}
+
+/**
+ * ret < 0: fatal error
+ * ret = 0: entry removed in h/w
+ * ret > 0: updated refcount.
+ */
+int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx)
+{
+       struct adapter *adap = pi->adapter;
+       struct mpstcam_table *t = adap->mpstcam;
+       struct mps_tcam_entry *entry;
+       int ret;
+
+       if (!t)
+               return -EOPNOTSUPP;
+       t4_os_write_lock(&t->lock);
+       entry = &t->entry[idx];
+       if (entry->state == MPS_ENTRY_UNUSED) {
+               t4_os_write_unlock(&t->lock);
+               return -EINVAL;
+       }
+
+       if (rte_atomic32_read(&entry->refcnt) == 1)
+               ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr,
+                                          entry->mask, idx, 1, pi->port_id,
+                                          false);
+       else
+               ret = rte_atomic32_sub_return(&entry->refcnt, 1);
+
+       if (ret == 0) {
+               reset_mpstcam_entry(entry);
+               t->full = false;        /* We have atleast 1 free entry */
+               cxgbe_update_free_idx(t);
+       }
+
+       t4_os_write_unlock(&t->lock);
+       return ret;
+}
+
+struct mpstcam_table *t4_init_mpstcam(struct adapter *adap)
+{
+       struct mpstcam_table *t;
+       int i;
+       u16 size = adap->params.arch.mps_tcam_size;
+
+       t =  t4_os_alloc(sizeof(*t) + size * sizeof(struct mps_tcam_entry));
+       if (!t)
+               return NULL;
+
+       t4_os_rwlock_init(&t->lock);
+       t->full = false;
+       t->size = size;
+
+       for (i = 0; i < size; i++) {
+               reset_mpstcam_entry(&t->entry[i]);
+               t->entry[i].mpstcam = t;
+               t->entry[i].idx = i;
+       }
+
+       /* first entry is used by chip. this is overwritten only
+        * in t4_cleanup_mpstcam()
+        */
+       t->entry[0].state = MPS_ENTRY_USED;
+       t->free_idx = 1;
+
+       return t;
+}
+
+void t4_cleanup_mpstcam(struct adapter *adap)
+{
+       if (adap->mpstcam) {
+               t4_os_free(adap->mpstcam->entry);
+               t4_os_free(adap->mpstcam);
+       }
+}
 
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#ifndef _CXGBE_MPSTCAM_H_
+#define _CXGBE_MPSTCAM_H_
+
+#include "common.h"
+
+enum {
+       MPS_ENTRY_UNUSED,       /* Keep this first so memset 0 renders
+                                * the correct state. Other states can
+                                * be added in future like MPS_ENTRY_BUSY
+                                * to reduce contention while mboxing
+                                * the request to f/w or to denote attributes
+                                * for a specific entry
+                                */
+       MPS_ENTRY_USED,
+};
+
+struct mps_tcam_entry {
+       u8 state;
+       u16 idx;
+
+       /* add data here which uniquely defines an entry */
+       u8 eth_addr[ETHER_ADDR_LEN];
+       u8 mask[ETHER_ADDR_LEN];
+
+       struct mpstcam_table *mpstcam; /* backptr */
+       rte_atomic32_t refcnt;
+};
+
+struct mpstcam_table {
+       u16 size;
+       rte_rwlock_t lock;
+       u16 free_idx;   /* next free index */
+       bool full;      /* since free index can be present
+                        * anywhere in the table, size and
+                        * free_idx cannot alone determine
+                        * if the table is full
+                        */
+       struct mps_tcam_entry entry[0];
+};
+
+struct mpstcam_table *t4_init_mpstcam(struct adapter *adap);
+void t4_cleanup_mpstcam(struct adapter *adap);
+int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *mac, const u8 *mask);
+int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx);
+int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr);
+
+#endif /* _CXGBE_MPSTCAM_H_ */