octeontx2-pf: cleanup transmit link deriving logic
[linux-2.6-microblaze.git] / drivers / net / ethernet / marvell / octeontx2 / af / rvu_nix.c
index 22039d9..0cac0f3 100644 (file)
@@ -25,7 +25,7 @@ static int nix_update_mce_rule(struct rvu *rvu, u16 pcifunc,
                               int type, bool add);
 static int nix_setup_ipolicers(struct rvu *rvu,
                               struct nix_hw *nix_hw, int blkaddr);
-static void nix_ipolicer_freemem(struct nix_hw *nix_hw);
+static void nix_ipolicer_freemem(struct rvu *rvu, struct nix_hw *nix_hw);
 static int nix_verify_bandprof(struct nix_cn10k_aq_enq_req *req,
                               struct nix_hw *nix_hw, u16 pcifunc);
 static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);
@@ -290,9 +290,11 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
        return true;
 }
 
-static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
+static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
+                             struct nix_lf_alloc_rsp *rsp)
 {
        struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct rvu_hwinfo *hw = rvu->hw;
        struct mac_ops *mac_ops;
        int pkind, pf, vf, lbkid;
        u8 cgx_id, lmac_id;
@@ -317,6 +319,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
                pfvf->tx_chan_base = pfvf->rx_chan_base;
                pfvf->rx_chan_cnt = 1;
                pfvf->tx_chan_cnt = 1;
+               rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id;
+
                cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
                rvu_npc_set_pkind(rvu, pkind, pfvf);
 
@@ -350,6 +354,7 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
                                        rvu_nix_chan_lbk(rvu, lbkid, vf + 1);
                pfvf->rx_chan_cnt = 1;
                pfvf->tx_chan_cnt = 1;
+               rsp->tx_link = hw->cgx_links + lbkid;
                rvu_npc_set_pkind(rvu, NPC_RX_LBK_PKIND, pfvf);
                rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
                                              pfvf->rx_chan_base,
@@ -671,9 +676,10 @@ static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
 static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
                              struct rvu_pfvf *pfvf, int nixlf,
                              int rss_sz, int rss_grps, int hwctx_size,
-                             u64 way_mask)
+                             u64 way_mask, bool tag_lsb_as_adder)
 {
        int err, grp, num_indices;
+       u64 val;
 
        /* RSS is not requested for this NIXLF */
        if (!rss_sz)
@@ -689,10 +695,13 @@ static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
                    (u64)pfvf->rss_ctx->iova);
 
        /* Config full RSS table size, enable RSS and caching */
-       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_CFG(nixlf),
-                   BIT_ULL(36) | BIT_ULL(4) |
-                   ilog2(num_indices / MAX_RSS_INDIR_TBL_SIZE) |
-                   way_mask << 20);
+       val = BIT_ULL(36) | BIT_ULL(4) | way_mask << 20 |
+                       ilog2(num_indices / MAX_RSS_INDIR_TBL_SIZE);
+
+       if (tag_lsb_as_adder)
+               val |= BIT_ULL(5);
+
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_CFG(nixlf), val);
        /* Config RSS group offset and sizes */
        for (grp = 0; grp < rss_grps; grp++)
                rvu_write64(rvu, blkaddr, NIX_AF_LFX_RSS_GRPX(nixlf, grp),
@@ -1241,7 +1250,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
        /* Initialize receive side scaling (RSS) */
        hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF);
        err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
-                                req->rss_grps, hwctx_size, req->way_mask);
+                                req->rss_grps, hwctx_size, req->way_mask,
+                                !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER));
        if (err)
                goto free_mem;
 
@@ -1299,7 +1309,7 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
 
        intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX;
-       err = nix_interface_init(rvu, pcifunc, intf, nixlf);
+       err = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp);
        if (err)
                goto free_mem;
 
@@ -1423,12 +1433,104 @@ int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
        return 0;
 }
 
+/* Handle shaper update specially for few revisions */
+static bool
+handle_txschq_shaper_update(struct rvu *rvu, int blkaddr, int nixlf,
+                           int lvl, u64 reg, u64 regval)
+{
+       u64 regbase, oldval, sw_xoff = 0;
+       u64 dbgval, md_debug0 = 0;
+       unsigned long poll_tmo;
+       bool rate_reg = 0;
+       u32 schq;
+
+       regbase = reg & 0xFFFF;
+       schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT);
+
+       /* Check for rate register */
+       switch (lvl) {
+       case NIX_TXSCH_LVL_TL1:
+               md_debug0 = NIX_AF_TL1X_MD_DEBUG0(schq);
+               sw_xoff = NIX_AF_TL1X_SW_XOFF(schq);
+
+               rate_reg = !!(regbase == NIX_AF_TL1X_CIR(0));
+               break;
+       case NIX_TXSCH_LVL_TL2:
+               md_debug0 = NIX_AF_TL2X_MD_DEBUG0(schq);
+               sw_xoff = NIX_AF_TL2X_SW_XOFF(schq);
+
+               rate_reg = (regbase == NIX_AF_TL2X_CIR(0) ||
+                           regbase == NIX_AF_TL2X_PIR(0));
+               break;
+       case NIX_TXSCH_LVL_TL3:
+               md_debug0 = NIX_AF_TL3X_MD_DEBUG0(schq);
+               sw_xoff = NIX_AF_TL3X_SW_XOFF(schq);
+
+               rate_reg = (regbase == NIX_AF_TL3X_CIR(0) ||
+                           regbase == NIX_AF_TL3X_PIR(0));
+               break;
+       case NIX_TXSCH_LVL_TL4:
+               md_debug0 = NIX_AF_TL4X_MD_DEBUG0(schq);
+               sw_xoff = NIX_AF_TL4X_SW_XOFF(schq);
+
+               rate_reg = (regbase == NIX_AF_TL4X_CIR(0) ||
+                           regbase == NIX_AF_TL4X_PIR(0));
+               break;
+       case NIX_TXSCH_LVL_MDQ:
+               sw_xoff = NIX_AF_MDQX_SW_XOFF(schq);
+               rate_reg = (regbase == NIX_AF_MDQX_CIR(0) ||
+                           regbase == NIX_AF_MDQX_PIR(0));
+               break;
+       }
+
+       if (!rate_reg)
+               return false;
+
+       /* Nothing special to do when state is not toggled */
+       oldval = rvu_read64(rvu, blkaddr, reg);
+       if ((oldval & 0x1) == (regval & 0x1)) {
+               rvu_write64(rvu, blkaddr, reg, regval);
+               return true;
+       }
+
+       /* PIR/CIR disable */
+       if (!(regval & 0x1)) {
+               rvu_write64(rvu, blkaddr, sw_xoff, 1);
+               rvu_write64(rvu, blkaddr, reg, 0);
+               udelay(4);
+               rvu_write64(rvu, blkaddr, sw_xoff, 0);
+               return true;
+       }
+
+       /* PIR/CIR enable */
+       rvu_write64(rvu, blkaddr, sw_xoff, 1);
+       if (md_debug0) {
+               poll_tmo = jiffies + usecs_to_jiffies(10000);
+               /* Wait until VLD(bit32) == 1 or C_CON(bit48) == 0 */
+               do {
+                       if (time_after(jiffies, poll_tmo)) {
+                               dev_err(rvu->dev,
+                                       "NIXLF%d: TLX%u(lvl %u) CIR/PIR enable failed\n",
+                                       nixlf, schq, lvl);
+                               goto exit;
+                       }
+                       usleep_range(1, 5);
+                       dbgval = rvu_read64(rvu, blkaddr, md_debug0);
+               } while (!(dbgval & BIT_ULL(32)) && (dbgval & BIT_ULL(48)));
+       }
+       rvu_write64(rvu, blkaddr, reg, regval);
+exit:
+       rvu_write64(rvu, blkaddr, sw_xoff, 0);
+       return true;
+}
+
 /* Disable shaping of pkts by a scheduler queue
  * at a given scheduler level.
  */
 static void nix_reset_tx_shaping(struct rvu *rvu, int blkaddr,
-                                int lvl, int schq)
+                                int nixlf, int lvl, int schq)
 {
+       struct rvu_hwinfo *hw = rvu->hw;
        u64  cir_reg = 0, pir_reg = 0;
        u64  cfg;
 
@@ -1449,6 +1551,21 @@ static void nix_reset_tx_shaping(struct rvu *rvu, int blkaddr,
                cir_reg = NIX_AF_TL4X_CIR(schq);
                pir_reg = NIX_AF_TL4X_PIR(schq);
                break;
+       case NIX_TXSCH_LVL_MDQ:
+               cir_reg = NIX_AF_MDQX_CIR(schq);
+               pir_reg = NIX_AF_MDQX_PIR(schq);
+               break;
+       }
+
+       /* Shaper state toggle needs wait/poll */
+       if (hw->cap.nix_shaper_toggle_wait) {
+               if (cir_reg)
+                       handle_txschq_shaper_update(rvu, blkaddr, nixlf,
+                                                   lvl, cir_reg, 0);
+               if (pir_reg)
+                       handle_txschq_shaper_update(rvu, blkaddr, nixlf,
+                                                   lvl, pir_reg, 0);
+               return;
        }
 
        if (!cir_reg)
@@ -1466,6 +1583,7 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr,
                                 int lvl, int schq)
 {
        struct rvu_hwinfo *hw = rvu->hw;
+       int link_level;
        int link;
 
        if (lvl >= hw->cap.nix_tx_aggr_lvl)
@@ -1475,7 +1593,9 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr,
        if (lvl == NIX_TXSCH_LVL_TL4)
                rvu_write64(rvu, blkaddr, NIX_AF_TL4X_SDP_LINK_CFG(schq), 0x00);
 
-       if (lvl != NIX_TXSCH_LVL_TL2)
+       link_level = rvu_read64(rvu, blkaddr, NIX_AF_PSE_CHANNEL_LEVEL) & 0x01 ?
+                       NIX_TXSCH_LVL_TL3 : NIX_TXSCH_LVL_TL2;
+       if (lvl != link_level)
                return;
 
        /* Reset TL2's CGX or LBK link config */
@@ -1484,6 +1604,40 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr,
                            NIX_AF_TL3_TL2X_LINKX_CFG(schq, link), 0x00);
 }
 
+static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
+                             int lvl, int schq)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u64 reg;
+
+       /* Skip this if shaping is not supported */
+       if (!hw->cap.nix_shaping)
+               return;
+
+       /* Clear level specific SW_XOFF */
+       switch (lvl) {
+       case NIX_TXSCH_LVL_TL1:
+               reg = NIX_AF_TL1X_SW_XOFF(schq);
+               break;
+       case NIX_TXSCH_LVL_TL2:
+               reg = NIX_AF_TL2X_SW_XOFF(schq);
+               break;
+       case NIX_TXSCH_LVL_TL3:
+               reg = NIX_AF_TL3X_SW_XOFF(schq);
+               break;
+       case NIX_TXSCH_LVL_TL4:
+               reg = NIX_AF_TL4X_SW_XOFF(schq);
+               break;
+       case NIX_TXSCH_LVL_MDQ:
+               reg = NIX_AF_MDQX_SW_XOFF(schq);
+               break;
+       default:
+               return;
+       }
+
+       rvu_write64(rvu, blkaddr, reg, 0x0);
+}
+
 static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -1661,15 +1815,14 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
        int link, blkaddr, rc = 0;
        int lvl, idx, start, end;
        struct nix_txsch *txsch;
-       struct rvu_pfvf *pfvf;
        struct nix_hw *nix_hw;
        u32 *pfvf_map;
+       int nixlf;
        u16 schq;
 
-       pfvf = rvu_get_pfvf(rvu, pcifunc);
-       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
-       if (!pfvf->nixlf || blkaddr < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
+       rc = nix_get_nixlf(rvu, pcifunc, &nixlf, &blkaddr);
+       if (rc)
+               return rc;
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
@@ -1718,7 +1871,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                            NIX_TXSCHQ_CFG_DONE))
                                pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
-                       nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
+                       nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
                }
 
                for (idx = 0; idx < req->schq[lvl]; idx++) {
@@ -1727,7 +1880,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                            NIX_TXSCHQ_CFG_DONE))
                                pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
-                       nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
+                       nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
                }
        }
 
@@ -1744,8 +1897,8 @@ exit:
        return rc;
 }
 
-static void nix_smq_flush(struct rvu *rvu, int blkaddr,
-                         int smq, u16 pcifunc, int nixlf)
+static int nix_smq_flush(struct rvu *rvu, int blkaddr,
+                        int smq, u16 pcifunc, int nixlf)
 {
        int pf = rvu_get_pf(pcifunc);
        u8 cgx_id = 0, lmac_id = 0;
@@ -1780,6 +1933,7 @@ static void nix_smq_flush(struct rvu *rvu, int blkaddr,
        /* restore cgx tx state */
        if (restore_tx_en)
                cgx_lmac_tx_enable(rvu_cgx_pdata(cgx_id, rvu), lmac_id, false);
+       return err;
 }
 
 static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
@@ -1788,6 +1942,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        struct rvu_hwinfo *hw = rvu->hw;
        struct nix_txsch *txsch;
        struct nix_hw *nix_hw;
+       u16 map_func;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
        if (blkaddr < 0)
@@ -1801,19 +1956,36 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        if (nixlf < 0)
                return NIX_AF_ERR_AF_LF_INVALID;
 
-       /* Disable TL2/3 queue links before SMQ flush*/
+       /* Disable TL2/3 queue links and all XOFF's before SMQ flush*/
        mutex_lock(&rvu->rsrc_lock);
-       for (lvl = NIX_TXSCH_LVL_TL4; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
-               if (lvl != NIX_TXSCH_LVL_TL2 && lvl != NIX_TXSCH_LVL_TL4)
+       for (lvl = NIX_TXSCH_LVL_MDQ; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
+               txsch = &nix_hw->txsch[lvl];
+
+               if (lvl >= hw->cap.nix_tx_aggr_lvl)
                        continue;
 
-               txsch = &nix_hw->txsch[lvl];
                for (schq = 0; schq < txsch->schq.max; schq++) {
                        if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                                continue;
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
+                       nix_clear_tx_xoff(rvu, blkaddr, lvl, schq);
                }
        }
+       nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1,
+                         nix_get_tx_link(rvu, pcifunc));
+
+       /* On PF cleanup, clear cfg done flag as
+        * PF would have changed default config.
+        */
+       if (!(pcifunc & RVU_PFVF_FUNC_MASK)) {
+               txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1];
+               schq = nix_get_tx_link(rvu, pcifunc);
+               /* Do not clear pcifunc in txsch->pfvf_map[schq] because
+                * VF might be using this TL1 queue
+                */
+               map_func = TXSCH_MAP_FUNC(txsch->pfvf_map[schq]);
+               txsch->pfvf_map[schq] = TXSCH_SET_FLAG(map_func, 0x0);
+       }
 
        /* Flush SMQs */
        txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
@@ -1859,6 +2031,7 @@ static int nix_txschq_free_one(struct rvu *rvu,
        struct nix_txsch *txsch;
        struct nix_hw *nix_hw;
        u32 *pfvf_map;
+       int rc;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
        if (blkaddr < 0)
@@ -1883,15 +2056,24 @@ static int nix_txschq_free_one(struct rvu *rvu,
        mutex_lock(&rvu->rsrc_lock);
 
        if (TXSCH_MAP_FUNC(pfvf_map[schq]) != pcifunc) {
-               mutex_unlock(&rvu->rsrc_lock);
+               rc = NIX_AF_ERR_TLX_INVALID;
                goto err;
        }
 
+       /* Clear SW_XOFF of this resource only.
+        * For SMQ level, all path XOFF's
+        * need to be made clear by user
+        */
+       nix_clear_tx_xoff(rvu, blkaddr, lvl, schq);
+
        /* Flush if it is a SMQ. Onus of disabling
         * TL2/3 queue links before SMQ flush is on user
         */
-       if (lvl == NIX_TXSCH_LVL_SMQ)
-               nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf);
+       if (lvl == NIX_TXSCH_LVL_SMQ &&
+           nix_smq_flush(rvu, blkaddr, schq, pcifunc, nixlf)) {
+               rc = NIX_AF_SMQ_FLUSH_FAILED;
+               goto err;
+       }
 
        /* Free the resource */
        rvu_free_rsrc(&txsch->schq, schq);
@@ -1899,7 +2081,8 @@ static int nix_txschq_free_one(struct rvu *rvu,
        mutex_unlock(&rvu->rsrc_lock);
        return 0;
 err:
-       return NIX_AF_ERR_TLX_INVALID;
+       mutex_unlock(&rvu->rsrc_lock);
+       return rc;
 }
 
 int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu,
@@ -1982,6 +2165,11 @@ static bool is_txschq_shaping_valid(struct rvu_hwinfo *hw, int lvl, u64 reg)
                    regbase == NIX_AF_TL4X_PIR(0))
                        return false;
                break;
+       case NIX_TXSCH_LVL_MDQ:
+               if (regbase == NIX_AF_MDQX_CIR(0) ||
+                   regbase == NIX_AF_MDQX_PIR(0))
+                       return false;
+               break;
        }
        return true;
 }
@@ -2014,6 +2202,33 @@ static void nix_tl1_default_cfg(struct rvu *rvu, struct nix_hw *nix_hw,
        pfvf_map[schq] = TXSCH_SET_FLAG(pfvf_map[schq], NIX_TXSCHQ_CFG_DONE);
 }
 
+/* Register offset - [15:0]
+ * Scheduler Queue number - [25:16]
+ */
+#define NIX_TX_SCHQ_MASK       GENMASK_ULL(25, 0)
+
+static int nix_txschq_cfg_read(struct rvu *rvu, struct nix_hw *nix_hw,
+                              int blkaddr, struct nix_txschq_config *req,
+                              struct nix_txschq_config *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       int idx, schq;
+       u64 reg;
+
+       for (idx = 0; idx < req->num_regs; idx++) {
+               reg = req->reg[idx];
+               reg &= NIX_TX_SCHQ_MASK;
+               schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT);
+               if (!rvu_check_valid_reg(TXSCHQ_HWREGMAP, req->lvl, reg) ||
+                   !is_valid_txschq(rvu, blkaddr, req->lvl, pcifunc, schq))
+                       return NIX_AF_INVAL_TXSCHQ_CFG;
+               rsp->regval[idx] = rvu_read64(rvu, blkaddr, reg);
+       }
+       rsp->lvl = req->lvl;
+       rsp->num_regs = req->num_regs;
+       return 0;
+}
+
 static void rvu_nix_tx_tl2_cfg(struct rvu *rvu, int blkaddr,
                               u16 pcifunc, struct nix_txsch *txsch)
 {
@@ -2045,11 +2260,11 @@ static void rvu_nix_tx_tl2_cfg(struct rvu *rvu, int blkaddr,
 
 int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                                    struct nix_txschq_config *req,
-                                   struct msg_rsp *rsp)
+                                   struct nix_txschq_config *rsp)
 {
+       u64 reg, val, regval, schq_regbase, val_mask;
        struct rvu_hwinfo *hw = rvu->hw;
        u16 pcifunc = req->hdr.pcifunc;
-       u64 reg, regval, schq_regbase;
        struct nix_txsch *txsch;
        struct nix_hw *nix_hw;
        int blkaddr, idx, err;
@@ -2068,6 +2283,9 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
        if (!nix_hw)
                return NIX_AF_ERR_INVALID_NIXBLK;
 
+       if (req->read)
+               return nix_txschq_cfg_read(rvu, nix_hw, blkaddr, req, rsp);
+
        txsch = &nix_hw->txsch[req->lvl];
        pfvf_map = txsch->pfvf_map;
 
@@ -2082,8 +2300,10 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
 
        for (idx = 0; idx < req->num_regs; idx++) {
                reg = req->reg[idx];
+               reg &= NIX_TX_SCHQ_MASK;
                regval = req->regval[idx];
                schq_regbase = reg & 0xFFFF;
+               val_mask = req->regval_mask[idx];
 
                if (!is_txschq_hierarchy_valid(rvu, pcifunc, blkaddr,
                                               txsch->lvl, reg, regval))
@@ -2093,6 +2313,15 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                if (!is_txschq_shaping_valid(hw, req->lvl, reg))
                        continue;
 
+               val = rvu_read64(rvu, blkaddr, reg);
+               regval = (val & val_mask) | (regval & ~val_mask);
+
+               /* Handle shaping state toggle specially */
+               if (hw->cap.nix_shaper_toggle_wait &&
+                   handle_txschq_shaper_update(rvu, blkaddr, nixlf,
+                                               req->lvl, reg, regval))
+                       continue;
+
                /* Replace PF/VF visible NIXLF slot with HW NIXLF id */
                if (schq_regbase == NIX_AF_SMQX_CFG(0)) {
                        nixlf = rvu_get_lf(rvu, &hw->block[blkaddr],
@@ -2133,7 +2362,6 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
 
        rvu_nix_tx_tl2_cfg(rvu, blkaddr, pcifunc,
                           &nix_hw->txsch[NIX_TXSCH_LVL_TL2]);
-
        return 0;
 }
 
@@ -3457,6 +3685,77 @@ static void nix_find_link_frs(struct rvu *rvu,
                req->minlen = minlen;
 }
 
+static int
+nix_config_link_credits(struct rvu *rvu, int blkaddr, int link,
+                       u16 pcifunc, u64 tx_credits)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       int pf = rvu_get_pf(pcifunc);
+       u8 cgx_id = 0, lmac_id = 0;
+       unsigned long poll_tmo;
+       bool restore_tx_en = 0;
+       struct nix_hw *nix_hw;
+       u64 cfg, sw_xoff = 0;
+       u32 schq = 0;
+       u32 credits;
+       int rc;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return NIX_AF_ERR_INVALID_NIXBLK;
+
+       if (tx_credits == nix_hw->tx_credits[link])
+               return 0;
+
+       /* Enable cgx tx if disabled for credits to be back */
+       if (is_pf_cgxmapped(rvu, pf)) {
+               rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
+               restore_tx_en = !cgx_lmac_tx_enable(rvu_cgx_pdata(cgx_id, rvu),
+                                                   lmac_id, true);
+       }
+
+       mutex_lock(&rvu->rsrc_lock);
+       /* Disable new traffic to link */
+       if (hw->cap.nix_shaping) {
+               schq = nix_get_tx_link(rvu, pcifunc);
+               sw_xoff = rvu_read64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq));
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_TL1X_SW_XOFF(schq), BIT_ULL(0));
+       }
+
+       rc = -EBUSY;
+       poll_tmo = jiffies + usecs_to_jiffies(10000);
+       /* Wait for credits to return */
+       do {
+               if (time_after(jiffies, poll_tmo))
+                       goto exit;
+               usleep_range(100, 200);
+
+               cfg = rvu_read64(rvu, blkaddr,
+                                NIX_AF_TX_LINKX_NORM_CREDIT(link));
+               credits = (cfg >> 12) & 0xFFFFFULL;
+       } while (credits != nix_hw->tx_credits[link]);
+
+       cfg &= ~(0xFFFFFULL << 12);
+       cfg |= (tx_credits << 12);
+       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
+       rc = 0;
+
+       nix_hw->tx_credits[link] = tx_credits;
+
+exit:
+       /* Enable traffic back */
+       if (hw->cap.nix_shaping && !sw_xoff)
+               rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq), 0);
+
+       /* Restore state of cgx tx */
+       if (restore_tx_en)
+               cgx_lmac_tx_enable(rvu_cgx_pdata(cgx_id, rvu), lmac_id, false);
+
+       mutex_unlock(&rvu->rsrc_lock);
+       return rc;
+}
+
 int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
                                    struct msg_rsp *rsp)
 {
@@ -3545,11 +3844,8 @@ linkcfg:
        lmac_fifo_len =
                rvu_cgx_get_fifolen(rvu) /
                cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
-       cfg = rvu_read64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link));
-       cfg &= ~(0xFFFFFULL << 12);
-       cfg |=  ((lmac_fifo_len - req->maxlen) / 16) << 12;
-       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
-       return 0;
+       return nix_config_link_credits(rvu, blkaddr, link, pcifunc,
+                                      (lmac_fifo_len - req->maxlen) / 16);
 }
 
 int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
@@ -3593,12 +3889,13 @@ static u64 rvu_get_lbk_link_credits(struct rvu *rvu, u16 lbk_max_frs)
        return 1600; /* 16 * max LBK datarate = 16 * 100Gbps */
 }
 
-static void nix_link_config(struct rvu *rvu, int blkaddr)
+static void nix_link_config(struct rvu *rvu, int blkaddr,
+                           struct nix_hw *nix_hw)
 {
        struct rvu_hwinfo *hw = rvu->hw;
        int cgx, lmac_cnt, slink, link;
        u16 lbk_max_frs, lmac_max_frs;
-       u64 tx_credits;
+       u64 tx_credits, cfg;
 
        rvu_get_lbk_link_max_frs(rvu, &lbk_max_frs);
        rvu_get_lmac_link_max_frs(rvu, &lmac_max_frs);
@@ -3629,15 +3926,18 @@ static void nix_link_config(struct rvu *rvu, int blkaddr)
         */
        for (cgx = 0; cgx < hw->cgx; cgx++) {
                lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
+               /* Skip when cgx is not available or lmac cnt is zero */
+               if (lmac_cnt <= 0)
+                       continue;
                tx_credits = ((rvu_cgx_get_fifolen(rvu) / lmac_cnt) -
                               lmac_max_frs) / 16;
                /* Enable credits and set credit pkt count to max allowed */
-               tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
+               cfg =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
                slink = cgx * hw->lmac_per_cgx;
                for (link = slink; link < (slink + lmac_cnt); link++) {
+                       nix_hw->tx_credits[link] = tx_credits;
                        rvu_write64(rvu, blkaddr,
-                                   NIX_AF_TX_LINKX_NORM_CREDIT(link),
-                                   tx_credits);
+                                   NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
                }
        }
 
@@ -3645,6 +3945,7 @@ static void nix_link_config(struct rvu *rvu, int blkaddr)
        slink = hw->cgx_links;
        for (link = slink; link < (slink + hw->lbk_links); link++) {
                tx_credits = rvu_get_lbk_link_credits(rvu, lbk_max_frs);
+               nix_hw->tx_credits[link] = tx_credits;
                /* Enable credits and set credit pkt count to max allowed */
                tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
                rvu_write64(rvu, blkaddr,
@@ -3908,8 +4209,13 @@ static int rvu_nix_block_init(struct rvu *rvu, struct nix_hw *nix_hw)
                if (err)
                        return err;
 
+               nix_hw->tx_credits = kcalloc(hw->cgx_links + hw->lbk_links,
+                                            sizeof(u64), GFP_KERNEL);
+               if (!nix_hw->tx_credits)
+                       return -ENOMEM;
+
                /* Initialize CGX/LBK/SDP link credits, min/max pkt lengths */
-               nix_link_config(rvu, blkaddr);
+               nix_link_config(rvu, blkaddr, nix_hw);
 
                /* Enable Channel backpressure */
                rvu_write64(rvu, blkaddr, NIX_AF_RX_CFG, BIT_ULL(0));
@@ -3965,7 +4271,9 @@ static void rvu_nix_block_freemem(struct rvu *rvu, int blkaddr,
                        kfree(txsch->schq.bmap);
                }
 
-               nix_ipolicer_freemem(nix_hw);
+               kfree(nix_hw->tx_credits);
+
+               nix_ipolicer_freemem(rvu, nix_hw);
 
                vlan = &nix_hw->txvlan;
                kfree(vlan->rsrc.bmap);
@@ -4341,11 +4649,14 @@ static int nix_setup_ipolicers(struct rvu *rvu,
        return 0;
 }
 
-static void nix_ipolicer_freemem(struct nix_hw *nix_hw)
+static void nix_ipolicer_freemem(struct rvu *rvu, struct nix_hw *nix_hw)
 {
        struct nix_ipolicer *ipolicer;
        int layer;
 
+       if (!rvu->hw->cap.ipolicer)
+               return;
+
        for (layer = 0; layer < BAND_PROF_NUM_LAYERS; layer++) {
                ipolicer = &nix_hw->ipolicer[layer];