M(NIX_TXSCH_FREE,      0x8005, nix_txsch_free, nix_txsch_free_req, msg_rsp) \
 M(NIX_TXSCHQ_CFG,      0x8006, nix_txschq_cfg, nix_txschq_config, msg_rsp)  \
 M(NIX_STATS_RST,       0x8007, nix_stats_rst, msg_req, msg_rsp)        \
-M(NIX_VTAG_CFG,                0x8008, nix_vtag_cfg, nix_vtag_config, msg_rsp) \
+M(NIX_VTAG_CFG,                0x8008, nix_vtag_cfg, nix_vtag_config,          \
+                                nix_vtag_config_rsp)                   \
 M(NIX_RSS_FLOWKEY_CFG,  0x8009, nix_rss_flowkey_cfg,                   \
                                 nix_rss_flowkey_cfg,                   \
                                 nix_rss_flowkey_cfg_rsp)               \
        NIX_AF_ERR_LSO_CFG_FAIL     = -418,
        NIX_AF_INVAL_NPA_PF_FUNC    = -419,
        NIX_AF_INVAL_SSO_PF_FUNC    = -420,
+       NIX_AF_ERR_TX_VTAG_NOSPC    = -421,
 };
 
 /* For NIX LF context alloc and init */
 
 struct nix_lf_free_req {
        struct mbox_msghdr hdr;
-#define NIX_LF_DISABLE_FLOWS           BIT_ULL(0)
+#define NIX_LF_DISABLE_FLOWS           BIT_ULL(0)
+#define NIX_LF_DONT_FREE_TX_VTAG       BIT_ULL(1)
        u64 flags;
 };
 
        union {
                /* valid when cfg_type is '0' */
                struct {
-                       /* tx vlan0 tag(C-VLAN) */
-                       u64 vlan0;
-                       /* tx vlan1 tag(S-VLAN) */
-                       u64 vlan1;
-                       /* insert tx vlan tag */
-                       u8 insert_vlan :1;
-                       /* insert tx double vlan tag */
-                       u8 double_vlan :1;
+                       u64 vtag0;
+                       u64 vtag1;
+
+                       /* cfg_vtag0 & cfg_vtag1 fields are valid
+                        * when free_vtag0 & free_vtag1 are '0's.
+                        */
+                       /* cfg_vtag0 = 1 to configure vtag0 */
+                       u8 cfg_vtag0 :1;
+                       /* cfg_vtag1 = 1 to configure vtag1 */
+                       u8 cfg_vtag1 :1;
+
+                       /* vtag0_idx & vtag1_idx are only valid when
+                        * both cfg_vtag0 & cfg_vtag1 are '0's,
+                        * these fields are used along with free_vtag0
+                        * & free_vtag1 to free the nix lf's tx_vlan
+                        * configuration.
+                        *
+                        * Denotes the indices of tx_vtag def registers
+                        * that needs to be cleared and freed.
+                        */
+                       int vtag0_idx;
+                       int vtag1_idx;
+
+                       /* free_vtag0 & free_vtag1 fields are valid
+                        * when cfg_vtag0 & cfg_vtag1 are '0's.
+                        */
+                       /* free_vtag0 = 1 clears vtag0 configuration
+                        * vtag0_idx denotes the index to be cleared.
+                        */
+                       u8 free_vtag0 :1;
+                       /* free_vtag1 = 1 clears vtag1 configuration
+                        * vtag1_idx denotes the index to be cleared.
+                        */
+                       u8 free_vtag1 :1;
                } tx;
 
                /* valid when cfg_type is '1' */
        };
 };
 
+struct nix_vtag_config_rsp {
+       struct mbox_msghdr hdr;
+       int vtag0_idx;
+       int vtag1_idx;
+       /* Indices of tx_vtag def registers used to configure
+        * tx vtag0 & vtag1 headers, these indices are valid
+        * when nix_vtag_config mbox requested for vtag0 and/
+        * or vtag1 configuration.
+        */
+};
+
 struct nix_rss_flowkey_cfg {
        struct mbox_msghdr hdr;
        int     mcam_index;  /* MCAM entry index to modify */
 
 #include "npc.h"
 #include "cgx.h"
 
+static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc);
 static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req,
                            int type, int chan_id);
 
        else
                rvu_npc_free_mcam_entries(rvu, pcifunc, nixlf);
 
+       /* Free any tx vtag def entries used by this NIX LF */
+       if (!(req->flags & NIX_LF_DONT_FREE_TX_VTAG))
+               nix_free_tx_vtag_entries(rvu, pcifunc);
+
        nix_interface_deinit(rvu, pcifunc, nixlf);
 
        /* Reset this NIX LF */
        return 0;
 }
 
+static int nix_tx_vtag_free(struct rvu *rvu, int blkaddr,
+                           u16 pcifunc, int index)
+{
+       struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       struct nix_txvlan *vlan = &nix_hw->txvlan;
+
+       if (vlan->entry2pfvf_map[index] != pcifunc)
+               return NIX_AF_ERR_PARAM;
+
+       rvu_write64(rvu, blkaddr,
+                   NIX_AF_TX_VTAG_DEFX_DATA(index), 0x0ull);
+       rvu_write64(rvu, blkaddr,
+                   NIX_AF_TX_VTAG_DEFX_CTL(index), 0x0ull);
+
+       vlan->entry2pfvf_map[index] = 0;
+       rvu_free_rsrc(&vlan->rsrc, index);
+
+       return 0;
+}
+
+static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc)
+{
+       struct nix_txvlan *vlan;
+       struct nix_hw *nix_hw;
+       int index, blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       vlan = &nix_hw->txvlan;
+
+       mutex_lock(&vlan->rsrc_lock);
+       /* Scan all the entries and free the ones mapped to 'pcifunc' */
+       for (index = 0; index < vlan->rsrc.max; index++) {
+               if (vlan->entry2pfvf_map[index] == pcifunc)
+                       nix_tx_vtag_free(rvu, blkaddr, pcifunc, index);
+       }
+       mutex_unlock(&vlan->rsrc_lock);
+}
+
+static int nix_tx_vtag_alloc(struct rvu *rvu, int blkaddr,
+                            u64 vtag, u8 size)
+{
+       struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       struct nix_txvlan *vlan = &nix_hw->txvlan;
+       u64 regval;
+       int index;
+
+       mutex_lock(&vlan->rsrc_lock);
+
+       index = rvu_alloc_rsrc(&vlan->rsrc);
+       if (index < 0) {
+               mutex_unlock(&vlan->rsrc_lock);
+               return index;
+       }
+
+       mutex_unlock(&vlan->rsrc_lock);
+
+       regval = size ? vtag : vtag << 32;
+
+       rvu_write64(rvu, blkaddr,
+                   NIX_AF_TX_VTAG_DEFX_DATA(index), regval);
+       rvu_write64(rvu, blkaddr,
+                   NIX_AF_TX_VTAG_DEFX_CTL(index), size);
+
+       return index;
+}
+
+static int nix_tx_vtag_decfg(struct rvu *rvu, int blkaddr,
+                            struct nix_vtag_config *req)
+{
+       struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       struct nix_txvlan *vlan = &nix_hw->txvlan;
+       u16 pcifunc = req->hdr.pcifunc;
+       int idx0 = req->tx.vtag0_idx;
+       int idx1 = req->tx.vtag1_idx;
+       int err;
+
+       if (req->tx.free_vtag0 && req->tx.free_vtag1)
+               if (vlan->entry2pfvf_map[idx0] != pcifunc ||
+                   vlan->entry2pfvf_map[idx1] != pcifunc)
+                       return NIX_AF_ERR_PARAM;
+
+       mutex_lock(&vlan->rsrc_lock);
+
+       if (req->tx.free_vtag0) {
+               err = nix_tx_vtag_free(rvu, blkaddr, pcifunc, idx0);
+               if (err)
+                       goto exit;
+       }
+
+       if (req->tx.free_vtag1)
+               err = nix_tx_vtag_free(rvu, blkaddr, pcifunc, idx1);
+
+exit:
+       mutex_unlock(&vlan->rsrc_lock);
+       return err;
+}
+
+static int nix_tx_vtag_cfg(struct rvu *rvu, int blkaddr,
+                          struct nix_vtag_config *req,
+                          struct nix_vtag_config_rsp *rsp)
+{
+       struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       struct nix_txvlan *vlan = &nix_hw->txvlan;
+       u16 pcifunc = req->hdr.pcifunc;
+
+       if (req->tx.cfg_vtag0) {
+               rsp->vtag0_idx =
+                       nix_tx_vtag_alloc(rvu, blkaddr,
+                                         req->tx.vtag0, req->vtag_size);
+
+               if (rsp->vtag0_idx < 0)
+                       return NIX_AF_ERR_TX_VTAG_NOSPC;
+
+               vlan->entry2pfvf_map[rsp->vtag0_idx] = pcifunc;
+       }
+
+       if (req->tx.cfg_vtag1) {
+               rsp->vtag1_idx =
+                       nix_tx_vtag_alloc(rvu, blkaddr,
+                                         req->tx.vtag1, req->vtag_size);
+
+               if (rsp->vtag1_idx < 0)
+                       goto err_free;
+
+               vlan->entry2pfvf_map[rsp->vtag1_idx] = pcifunc;
+       }
+
+       return 0;
+
+err_free:
+       if (req->tx.cfg_vtag0)
+               nix_tx_vtag_free(rvu, blkaddr, pcifunc, rsp->vtag0_idx);
+
+       return NIX_AF_ERR_TX_VTAG_NOSPC;
+}
+
 int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu,
                                  struct nix_vtag_config *req,
-                                 struct msg_rsp *rsp)
+                                 struct nix_vtag_config_rsp *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
        int blkaddr, nixlf, err;
                return err;
 
        if (req->cfg_type) {
+               /* rx vtag configuration */
                err = nix_rx_vtag_cfg(rvu, nixlf, blkaddr, req);
                if (err)
                        return NIX_AF_ERR_PARAM;
        } else {
-               /* TODO: handle tx vtag configuration */
-               return 0;
+               /* tx vtag configuration */
+               if ((req->tx.cfg_vtag0 || req->tx.cfg_vtag1) &&
+                   (req->tx.free_vtag0 || req->tx.free_vtag1))
+                       return NIX_AF_ERR_PARAM;
+
+               if (req->tx.cfg_vtag0 || req->tx.cfg_vtag1)
+                       return nix_tx_vtag_cfg(rvu, blkaddr, req, rsp);
+
+               if (req->tx.free_vtag0 || req->tx.free_vtag1)
+                       return nix_tx_vtag_decfg(rvu, blkaddr, req);
        }
 
        return 0;
        return nix_setup_bcast_tables(rvu, nix_hw);
 }
 
+static int nix_setup_txvlan(struct rvu *rvu, struct nix_hw *nix_hw)
+{
+       struct nix_txvlan *vlan = &nix_hw->txvlan;
+       int err;
+
+       /* Allocate resource bimap for tx vtag def registers*/
+       vlan->rsrc.max = NIX_TX_VTAG_DEF_MAX;
+       err = rvu_alloc_bitmap(&vlan->rsrc);
+       if (err)
+               return -ENOMEM;
+
+       /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */
+       vlan->entry2pfvf_map = devm_kcalloc(rvu->dev, vlan->rsrc.max,
+                                           sizeof(u16), GFP_KERNEL);
+       if (!vlan->entry2pfvf_map)
+               goto free_mem;
+
+       mutex_init(&vlan->rsrc_lock);
+       return 0;
+
+free_mem:
+       kfree(vlan->rsrc.bmap);
+       return -ENOMEM;
+}
+
 static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
 {
        struct nix_txsch *txsch;
                if (err)
                        return err;
 
+               err = nix_setup_txvlan(rvu, nix_hw);
+               if (err)
+                       return err;
+
                /* Configure segmentation offload formats */
                nix_setup_lso(rvu, nix_hw, blkaddr);
 
 {
        struct nix_txsch *txsch;
        struct nix_mcast *mcast;
+       struct nix_txvlan *vlan;
        struct nix_hw *nix_hw;
        int lvl;
 
                        kfree(txsch->schq.bmap);
                }
 
+               vlan = &nix_hw->txvlan;
+               kfree(vlan->rsrc.bmap);
+               mutex_destroy(&vlan->rsrc_lock);
+               devm_kfree(rvu->dev, vlan->entry2pfvf_map);
+
                mcast = &nix_hw->mcast;
                qmem_free(rvu->dev, mcast->mce_ctx);
                qmem_free(rvu->dev, mcast->mcast_buf);