octeontx2-pf: cleanup transmit link deriving logic
[linux-2.6-microblaze.git] / drivers / net / ethernet / marvell / octeontx2 / af / rvu_nix.c
index 4bfbbdf..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);
@@ -192,6 +192,47 @@ struct nix_hw *get_nix_hw(struct rvu_hwinfo *hw, int blkaddr)
        return NULL;
 }
 
+u32 convert_dwrr_mtu_to_bytes(u8 dwrr_mtu)
+{
+       dwrr_mtu &= 0x1FULL;
+
+       /* MTU used for DWRR calculation is in power of 2 up until 64K bytes.
+        * Value of 4 is reserved for MTU value of 9728 bytes.
+        * Value of 5 is reserved for MTU value of 10240 bytes.
+        */
+       switch (dwrr_mtu) {
+       case 4:
+               return 9728;
+       case 5:
+               return 10240;
+       default:
+               return BIT_ULL(dwrr_mtu);
+       }
+
+       return 0;
+}
+
+u32 convert_bytes_to_dwrr_mtu(u32 bytes)
+{
+       /* MTU used for DWRR calculation is in power of 2 up until 64K bytes.
+        * Value of 4 is reserved for MTU value of 9728 bytes.
+        * Value of 5 is reserved for MTU value of 10240 bytes.
+        */
+       if (bytes > BIT_ULL(16))
+               return 0;
+
+       switch (bytes) {
+       case 9728:
+               return 4;
+       case 10240:
+               return 5;
+       default:
+               return ilog2(bytes);
+       }
+
+       return 0;
+}
+
 static void nix_rx_sync(struct rvu *rvu, int blkaddr)
 {
        int err;
@@ -249,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;
@@ -276,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);
 
@@ -309,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,
@@ -630,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)
@@ -648,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),
@@ -943,7 +993,7 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
 
        nix_hw =  get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        return rvu_nix_blk_aq_enq_inst(rvu, nix_hw, req, rsp);
 }
@@ -1200,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;
 
@@ -1258,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;
 
@@ -1364,7 +1415,7 @@ int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        cfg = (((u32)req->offset & 0x7) << 16) |
              (((u32)req->y_mask & 0xF) << 12) |
@@ -1382,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;
 
@@ -1408,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)
@@ -1425,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)
@@ -1434,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 */
@@ -1443,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;
@@ -1620,19 +1815,18 @@ 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)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        mutex_lock(&rvu->rsrc_lock);
 
@@ -1677,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++) {
@@ -1686,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);
                }
        }
 
@@ -1703,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;
@@ -1739,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)
@@ -1747,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)
@@ -1754,25 +1950,42 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
        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];
@@ -1818,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)
@@ -1825,7 +2039,7 @@ static int nix_txschq_free_one(struct rvu *rvu,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
        if (nixlf < 0)
@@ -1842,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);
@@ -1858,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,
@@ -1941,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;
 }
@@ -1958,12 +2187,48 @@ static void nix_tl1_default_cfg(struct rvu *rvu, struct nix_hw *nix_hw,
                return;
        rvu_write64(rvu, blkaddr, NIX_AF_TL1X_TOPOLOGY(schq),
                    (TXSCH_TL1_DFLT_RR_PRIO << 1));
-       rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq),
-                   TXSCH_TL1_DFLT_RR_QTM);
+
+       /* On OcteonTx2 the config was in bytes and newer silcons
+        * it's changed to weight.
+        */
+       if (!rvu->hw->cap.nix_common_dwrr_mtu)
+               rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq),
+                           TXSCH_TL1_DFLT_RR_QTM);
+       else
+               rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SCHEDULE(schq),
+                           CN10K_MAX_DWRR_WEIGHT);
+
        rvu_write64(rvu, blkaddr, NIX_AF_TL1X_CIR(schq), 0x00);
        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)
 {
@@ -1995,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;
@@ -2016,7 +2281,10 @@ int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               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;
@@ -2032,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))
@@ -2043,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],
@@ -2083,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;
 }
 
@@ -2114,8 +2392,12 @@ 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;
+       struct nix_txvlan *vlan;
+
+       if (!nix_hw)
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
+       vlan = &nix_hw->txvlan;
        if (vlan->entry2pfvf_map[index] != pcifunc)
                return NIX_AF_ERR_PARAM;
 
@@ -2156,10 +2438,15 @@ 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;
+       struct nix_txvlan *vlan;
        u64 regval;
        int index;
 
+       if (!nix_hw)
+               return NIX_AF_ERR_INVALID_NIXBLK;
+
+       vlan = &nix_hw->txvlan;
+
        mutex_lock(&vlan->rsrc_lock);
 
        index = rvu_alloc_rsrc(&vlan->rsrc);
@@ -2184,12 +2471,16 @@ 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;
+       struct nix_txvlan *vlan;
        int err = 0;
 
+       if (!nix_hw)
+               return NIX_AF_ERR_INVALID_NIXBLK;
+
+       vlan = &nix_hw->txvlan;
        if (req->tx.free_vtag0 && req->tx.free_vtag1)
                if (vlan->entry2pfvf_map[idx0] != pcifunc ||
                    vlan->entry2pfvf_map[idx1] != pcifunc)
@@ -2216,9 +2507,13 @@ static int nix_tx_vtag_cfg(struct rvu *rvu, int blkaddr,
                           struct nix_vtag_config_rsp *rsp)
 {
        struct nix_hw *nix_hw = get_nix_hw(rvu->hw, blkaddr);
-       struct nix_txvlan *vlan = &nix_hw->txvlan;
+       struct nix_txvlan *vlan;
        u16 pcifunc = req->hdr.pcifunc;
 
+       if (!nix_hw)
+               return NIX_AF_ERR_INVALID_NIXBLK;
+
+       vlan = &nix_hw->txvlan;
        if (req->tx.cfg_vtag0) {
                rsp->vtag0_idx =
                        nix_tx_vtag_alloc(rvu, blkaddr,
@@ -2667,6 +2962,15 @@ static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
                for (schq = 0; schq < txsch->schq.max; schq++)
                        txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE);
        }
+
+       /* Setup a default value of 8192 as DWRR MTU */
+       if (rvu->hw->cap.nix_common_dwrr_mtu) {
+               rvu_write64(rvu, blkaddr, NIX_AF_DWRR_RPM_MTU,
+                           convert_bytes_to_dwrr_mtu(8192));
+               rvu_write64(rvu, blkaddr, NIX_AF_DWRR_SDP_MTU,
+                           convert_bytes_to_dwrr_mtu(8192));
+       }
+
        return 0;
 }
 
@@ -2743,6 +3047,7 @@ int rvu_mbox_handler_nix_get_hw_info(struct rvu *rvu, struct msg_req *req,
                                     struct nix_hw_info *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
+       u64 dwrr_mtu;
        int blkaddr;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
@@ -2755,6 +3060,20 @@ int rvu_mbox_handler_nix_get_hw_info(struct rvu *rvu, struct msg_req *req,
                rvu_get_lmac_link_max_frs(rvu, &rsp->max_mtu);
 
        rsp->min_mtu = NIC_HW_MIN_FRS;
+
+       if (!rvu->hw->cap.nix_common_dwrr_mtu) {
+               /* Return '1' on OTx2 */
+               rsp->rpm_dwrr_mtu = 1;
+               rsp->sdp_dwrr_mtu = 1;
+               return 0;
+       }
+
+       dwrr_mtu = rvu_read64(rvu, BLKADDR_NIX0, NIX_AF_DWRR_RPM_MTU);
+       rsp->rpm_dwrr_mtu = convert_dwrr_mtu_to_bytes(dwrr_mtu);
+
+       dwrr_mtu = rvu_read64(rvu, BLKADDR_NIX0, NIX_AF_DWRR_SDP_MTU);
+       rsp->sdp_dwrr_mtu = convert_dwrr_mtu_to_bytes(dwrr_mtu);
+
        return 0;
 }
 
@@ -3068,7 +3387,7 @@ static int reserve_flowkey_alg_idx(struct rvu *rvu, int blkaddr, u32 flow_cfg)
 
        hw = get_nix_hw(rvu->hw, blkaddr);
        if (!hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        /* No room to add new flow hash algoritham */
        if (hw->flowkey.in_use >= NIX_FLOW_KEY_ALG_MAX)
@@ -3108,7 +3427,7 @@ int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        alg_idx = get_flowkey_alg_idx(nix_hw, req->flowkey_cfg);
        /* Failed to get algo index from the exiting list, reserve new  */
@@ -3366,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)
 {
@@ -3385,7 +3775,7 @@ int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        if (is_afvf(pcifunc))
                rvu_get_lbk_link_max_frs(rvu, &max_mtu);
@@ -3454,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,
@@ -3502,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);
@@ -3538,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);
                }
        }
 
@@ -3554,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,
@@ -3647,6 +4039,28 @@ static int nix_aq_init(struct rvu *rvu, struct rvu_block *block)
        return 0;
 }
 
+static void rvu_nix_setup_capabilities(struct rvu *rvu, int blkaddr)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u64 hw_const;
+
+       hw_const = rvu_read64(rvu, blkaddr, NIX_AF_CONST1);
+
+       /* On OcteonTx2 DWRR quantum is directly configured into each of
+        * the transmit scheduler queues. And PF/VF drivers were free to
+        * config any value upto 2^24.
+        * On CN10K, HW is modified, the quantum configuration at scheduler
+        * queues is in terms of weight. And SW needs to setup a base DWRR MTU
+        * at NIX_AF_DWRR_RPM_MTU / NIX_AF_DWRR_SDP_MTU. HW will do
+        * 'DWRR MTU * weight' to get the quantum.
+        *
+        * Check if HW uses a common MTU for all DWRR quantum configs.
+        * On OcteonTx2 this register field is '0'.
+        */
+       if (((hw_const >> 56) & 0x10) == 0x10)
+               hw->cap.nix_common_dwrr_mtu = true;
+}
+
 static int rvu_nix_block_init(struct rvu *rvu, struct nix_hw *nix_hw)
 {
        const struct npc_lt_def_cfg *ltdefs;
@@ -3684,6 +4098,9 @@ static int rvu_nix_block_init(struct rvu *rvu, struct nix_hw *nix_hw)
        if (err)
                return err;
 
+       /* Setup capabilities of the NIX block */
+       rvu_nix_setup_capabilities(rvu, blkaddr);
+
        /* Initialize admin queue */
        err = nix_aq_init(rvu, block);
        if (err)
@@ -3792,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));
@@ -3849,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);
@@ -4027,7 +4451,7 @@ int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu,
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
-               return -EINVAL;
+               return NIX_AF_ERR_INVALID_NIXBLK;
 
        /* Find existing matching LSO format, if any */
        for (idx = 0; idx < nix_hw->lso.in_use; idx++) {
@@ -4225,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];