if (dev->configured == 1) {
                otx2_nix_rxchan_bpid_cfg(eth_dev, false);
                otx2_nix_vlan_fini(eth_dev);
+               otx2_nix_mc_addr_list_uninstall(eth_dev);
                otx2_flow_free_all_resources(dev);
                oxt2_nix_unregister_queue_irqs(eth_dev);
                if (eth_dev->data->dev_conf.intr_conf.rxq)
                goto q_irq_fini;
        }
 
+       rc = otx2_nix_mc_addr_list_install(eth_dev);
+       if (rc < 0) {
+               otx2_err("Failed to install mc address list rc=%d", rc);
+               goto cq_fini;
+       }
+
        /*
         * Restore queue config when reconfigure followed by
         * reconfigure and no queue configure invoked from application case.
        if (dev->configured == 1) {
                rc = nix_restore_queue_cfg(eth_dev);
                if (rc)
-                       goto cq_fini;
+                       goto uninstall_mc_list;
        }
 
        /* Update the mac address */
        dev->configured_nb_tx_qs = data->nb_tx_queues;
        return 0;
 
+uninstall_mc_list:
+       otx2_nix_mc_addr_list_uninstall(eth_dev);
 cq_fini:
        oxt2_nix_unregister_cq_irqs(eth_dev);
 q_irq_fini:
        .mac_addr_add             = otx2_nix_mac_addr_add,
        .mac_addr_remove          = otx2_nix_mac_addr_del,
        .mac_addr_set             = otx2_nix_mac_addr_set,
+       .set_mc_addr_list         = otx2_nix_set_mc_addr_list,
        .promiscuous_enable       = otx2_nix_promisc_enable,
        .promiscuous_disable      = otx2_nix_promisc_disable,
        .allmulticast_enable      = otx2_nix_allmulticast_enable,
        if (rc)
                goto free_mac_addrs;
 
+       otx2_nix_mc_filter_init(dev);
+
        otx2_nix_dbg("Port=%d pf=%d vf=%d ver=%s msix_off=%d hwcap=0x%" PRIx64
                     " rxoffload_capa=0x%" PRIx64 " txoffload_capa=0x%" PRIx64,
                     eth_dev->data->port_id, dev->pf, dev->vf,
        /* Disable other rte_flow entries */
        otx2_flow_fini(dev);
 
+       /* Free multicast filter list */
+       otx2_nix_mc_filter_fini(dev);
+
        /* Disable PTP if already enabled */
        if (otx2_ethdev_is_ptp_en(dev))
                otx2_nix_timesync_disable(eth_dev);
 
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2019 Marvell International Ltd.
+ */
+
+#include "otx2_ethdev.h"
+
+static int
+nix_mc_addr_list_free(struct otx2_eth_dev *dev, uint32_t entry_count)
+{
+       struct npc_mcam_free_entry_req *req;
+       struct otx2_mbox *mbox = dev->mbox;
+       struct mcast_entry *entry;
+       int rc = 0;
+
+       if (entry_count == 0)
+               goto exit;
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next) {
+               req = otx2_mbox_alloc_msg_npc_mcam_free_entry(mbox);
+               req->entry = entry->mcam_index;
+
+               rc = otx2_mbox_process_msg(mbox, NULL);
+               if (rc < 0)
+                       goto exit;
+
+               TAILQ_REMOVE(&dev->mc_fltr_tbl, entry, next);
+               rte_free(entry);
+               entry_count--;
+
+               if (entry_count == 0)
+                       break;
+       }
+
+       if (entry == NULL)
+               dev->mc_tbl_set = false;
+
+exit:
+       return rc;
+}
+
+static int
+nix_hw_update_mc_addr_list(struct rte_eth_dev *eth_dev)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct otx2_npc_flow_info *npc = &dev->npc_flow;
+       volatile uint8_t *key_data, *key_mask;
+       struct npc_mcam_write_entry_req *req;
+       struct otx2_mbox *mbox = dev->mbox;
+       struct npc_xtract_info *x_info;
+       uint64_t mcam_data, mcam_mask;
+       struct mcast_entry *entry;
+       otx2_dxcfg_t *ld_cfg;
+       uint8_t *mac_addr;
+       uint64_t action;
+       int idx, rc = 0;
+
+       ld_cfg = &npc->prx_dxcfg;
+       /* Get ETH layer profile info for populating mcam entries */
+       x_info = &(*ld_cfg)[NPC_MCAM_RX][NPC_LID_LA][NPC_LT_LA_ETHER].xtract[0];
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next) {
+               req = otx2_mbox_alloc_msg_npc_mcam_write_entry(mbox);
+               if (req == NULL) {
+                       /* The mbox memory buffer can be full.
+                        * Flush it and retry
+                        */
+                       otx2_mbox_msg_send(mbox, 0);
+                       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+                       if (rc < 0)
+                               goto exit;
+
+                       req = otx2_mbox_alloc_msg_npc_mcam_write_entry(mbox);
+                       if (req == NULL) {
+                               rc = -ENOMEM;
+                               goto exit;
+                       }
+               }
+               req->entry = entry->mcam_index;
+               req->intf = NPC_MCAM_RX;
+               req->enable_entry = 1;
+
+               /* Channel base extracted to KW0[11:0] */
+               req->entry_data.kw[0] = dev->rx_chan_base;
+               req->entry_data.kw_mask[0] = RTE_LEN2MASK(12, uint64_t);
+
+               /* Update mcam address */
+               key_data = (volatile uint8_t *)req->entry_data.kw;
+               key_mask = (volatile uint8_t *)req->entry_data.kw_mask;
+
+               mcam_data = 0ull;
+               mcam_mask = RTE_LEN2MASK(48, uint64_t);
+               mac_addr = &entry->mcast_mac.addr_bytes[0];
+               for (idx = RTE_ETHER_ADDR_LEN - 1; idx >= 0; idx--)
+                       mcam_data |= ((uint64_t)*mac_addr++) << (8 * idx);
+
+               otx2_mbox_memcpy(key_data + x_info->key_off,
+                                &mcam_data, x_info->len);
+               otx2_mbox_memcpy(key_mask + x_info->key_off,
+                                &mcam_mask, x_info->len);
+
+               action = NIX_RX_ACTIONOP_UCAST;
+
+               if (eth_dev->data->dev_conf.rxmode.mq_mode == ETH_MQ_RX_RSS) {
+                       action = NIX_RX_ACTIONOP_RSS;
+                       action |= (uint64_t)(dev->rss_info.alg_idx) << 56;
+               }
+
+               action |= ((uint64_t)otx2_pfvf_func(dev->pf, dev->vf)) << 4;
+               req->entry_data.action = action;
+       }
+
+       otx2_mbox_msg_send(mbox, 0);
+       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+
+exit:
+       return rc;
+}
+
+int
+otx2_nix_mc_addr_list_install(struct rte_eth_dev *eth_dev)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct npc_mcam_alloc_entry_req *req;
+       struct npc_mcam_alloc_entry_rsp *rsp;
+       struct otx2_mbox *mbox = dev->mbox;
+       uint32_t entry_count = 0, idx  = 0;
+       struct mcast_entry *entry;
+       int rc = 0;
+
+       if (!dev->mc_tbl_set)
+               return 0;
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next)
+               entry_count++;
+
+       req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(mbox);
+       req->priority = NPC_MCAM_ANY_PRIO;
+       req->count = entry_count;
+
+       rc = otx2_mbox_process_msg(mbox, (void *)&rsp);
+       if (rc || rsp->count  < entry_count) {
+               otx2_err("Failed to allocate required mcam entries");
+               goto exit;
+       }
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next)
+               entry->mcam_index = rsp->entry_list[idx];
+
+       rc = nix_hw_update_mc_addr_list(eth_dev);
+
+exit:
+       return rc;
+}
+
+int
+otx2_nix_mc_addr_list_uninstall(struct rte_eth_dev *eth_dev)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct npc_mcam_free_entry_req *req;
+       struct otx2_mbox *mbox = dev->mbox;
+       struct mcast_entry *entry;
+       int rc = 0;
+
+       if (!dev->mc_tbl_set)
+               return 0;
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next) {
+               req = otx2_mbox_alloc_msg_npc_mcam_free_entry(mbox);
+               if (req == NULL) {
+                       otx2_mbox_msg_send(mbox, 0);
+                       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+                       if (rc < 0)
+                               goto exit;
+
+                       req = otx2_mbox_alloc_msg_npc_mcam_free_entry(mbox);
+                       if (req == NULL) {
+                               rc = -ENOMEM;
+                               goto exit;
+                       }
+               }
+               req->entry = entry->mcam_index;
+       }
+
+       otx2_mbox_msg_send(mbox, 0);
+       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+
+exit:
+       return rc;
+}
+
+static int
+nix_setup_mc_addr_list(struct otx2_eth_dev *dev,
+                      struct rte_ether_addr *mc_addr_set)
+{
+       struct npc_mcam_ena_dis_entry_req *req;
+       struct otx2_mbox *mbox = dev->mbox;
+       struct mcast_entry *entry;
+       uint32_t idx = 0;
+       int rc = 0;
+
+       /* Populate PMD's mcast list with given mcast mac addresses and
+        * disable all mcam entries pertaining to the mcast list.
+        */
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next) {
+               rte_memcpy(&entry->mcast_mac, &mc_addr_set[idx++],
+                          RTE_ETHER_ADDR_LEN);
+
+               req = otx2_mbox_alloc_msg_npc_mcam_dis_entry(mbox);
+               if (req == NULL) {
+                       otx2_mbox_msg_send(mbox, 0);
+                       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+                       if (rc < 0)
+                               goto exit;
+
+                       req = otx2_mbox_alloc_msg_npc_mcam_dis_entry(mbox);
+                       if (req == NULL) {
+                               rc = -ENOMEM;
+                               goto exit;
+                       }
+               }
+               req->entry = entry->mcam_index;
+       }
+
+       otx2_mbox_msg_send(mbox, 0);
+       rc = otx2_mbox_wait_for_rsp(mbox, 0);
+
+exit:
+       return rc;
+}
+
+int
+otx2_nix_set_mc_addr_list(struct rte_eth_dev *eth_dev,
+                         struct rte_ether_addr *mc_addr_set,
+                         uint32_t nb_mc_addr)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct npc_mcam_alloc_entry_req *req;
+       struct npc_mcam_alloc_entry_rsp *rsp;
+       struct otx2_mbox *mbox = dev->mbox;
+       uint32_t idx, priv_count = 0;
+       struct mcast_entry *entry;
+       int rc = 0;
+
+       if (otx2_dev_is_vf(dev))
+               return -ENOTSUP;
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next)
+               priv_count++;
+
+       if (nb_mc_addr == 0 || mc_addr_set == NULL) {
+               /* Free existing list if new list is null */
+               nb_mc_addr = priv_count;
+               goto exit;
+       }
+
+       for (idx = 0; idx < nb_mc_addr; idx++) {
+               if (!rte_is_multicast_ether_addr(&mc_addr_set[idx]))
+                       return -EINVAL;
+       }
+
+       /* New list is bigger than the existing list,
+        * allocate mcam entries for the extra entries.
+        */
+       if (nb_mc_addr > priv_count) {
+               req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(mbox);
+               req->priority = NPC_MCAM_ANY_PRIO;
+               req->count = nb_mc_addr - priv_count;
+
+               rc = otx2_mbox_process_msg(mbox, (void *)&rsp);
+               if (rc || (rsp->count + priv_count < nb_mc_addr)) {
+                       otx2_err("Failed to allocate required entries");
+                       nb_mc_addr = priv_count;
+                       goto exit;
+               }
+
+               /* Append new mcam entries to the existing mc list */
+               for (idx = 0; idx < rsp->count; idx++) {
+                       entry = rte_zmalloc("otx2_nix_mc_entry",
+                                           sizeof(struct mcast_entry), 0);
+                       if (!entry) {
+                               otx2_err("Failed to allocate memory");
+                               nb_mc_addr = priv_count;
+                               rc = -ENOMEM;
+                               goto exit;
+                       }
+                       entry->mcam_index = rsp->entry_list[idx];
+                       TAILQ_INSERT_HEAD(&dev->mc_fltr_tbl, entry, next);
+               }
+       } else {
+               /* Free the extra mcam entries if the new list is smaller
+                * than exiting list.
+                */
+               nix_mc_addr_list_free(dev, priv_count - nb_mc_addr);
+       }
+
+
+       /* Now mc_fltr_tbl has the required number of mcam entries,
+        * Traverse through it and add new multicast filter table entries.
+        */
+       rc = nix_setup_mc_addr_list(dev, mc_addr_set);
+       if (rc < 0)
+               goto exit;
+
+       rc = nix_hw_update_mc_addr_list(eth_dev);
+       if (rc < 0)
+               goto exit;
+
+       dev->mc_tbl_set = true;
+
+       return 0;
+
+exit:
+       nix_mc_addr_list_free(dev, nb_mc_addr);
+       return rc;
+}
+
+void
+otx2_nix_mc_filter_init(struct otx2_eth_dev *dev)
+{
+       if (otx2_dev_is_vf(dev))
+               return;
+
+       TAILQ_INIT(&dev->mc_fltr_tbl);
+}
+
+void
+otx2_nix_mc_filter_fini(struct otx2_eth_dev *dev)
+{
+       struct mcast_entry *entry;
+       uint32_t count = 0;
+
+       if (otx2_dev_is_vf(dev))
+               return;
+
+       TAILQ_FOREACH(entry, &dev->mc_fltr_tbl, next)
+               count++;
+
+       nix_mc_addr_list_free(dev, count);
+}