RVU_DEBUG_SEQ_FOPS(npc_rx_miss_act, npc_rx_miss_stats_display, NULL);
 
+#define RVU_DBG_PRINT_MPLS_TTL(pkt, mask)                                     \
+do {                                                                         \
+       seq_printf(s, "%ld ", FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL, pkt));     \
+       seq_printf(s, "mask 0x%lx\n",                                         \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL, mask));               \
+} while (0)                                                                   \
+
+#define RVU_DBG_PRINT_MPLS_LBTCBOS(_pkt, _mask)                               \
+do {                                                                         \
+       typeof(_pkt) (pkt) = (_pkt);                                          \
+       typeof(_mask) (mask) = (_mask);                                       \
+       seq_printf(s, "%ld %ld %ld\n",                                        \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_LB, pkt),                  \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_TC, pkt),                  \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_BOS, pkt));                \
+       seq_printf(s, "\tmask 0x%lx 0x%lx 0x%lx\n",                           \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_LB, mask),                 \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_TC, mask),                 \
+                  FIELD_GET(OTX2_FLOWER_MASK_MPLS_BOS, mask));               \
+} while (0)                                                                   \
+
 static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s,
                                        struct rvu_npc_mcam_rule *rule)
 {
                        seq_printf(s, "0x%x ", ntohl(rule->packet.spi));
                        seq_printf(s, "mask 0x%x\n", ntohl(rule->mask.spi));
                        break;
+               case NPC_MPLS1_LBTCBOS:
+                       RVU_DBG_PRINT_MPLS_LBTCBOS(rule->packet.mpls_lse[0],
+                                                  rule->mask.mpls_lse[0]);
+                       break;
+               case NPC_MPLS1_TTL:
+                       RVU_DBG_PRINT_MPLS_TTL(rule->packet.mpls_lse[0],
+                                              rule->mask.mpls_lse[0]);
+                       break;
+               case NPC_MPLS2_LBTCBOS:
+                       RVU_DBG_PRINT_MPLS_LBTCBOS(rule->packet.mpls_lse[1],
+                                                  rule->mask.mpls_lse[1]);
+                       break;
+               case NPC_MPLS2_TTL:
+                       RVU_DBG_PRINT_MPLS_TTL(rule->packet.mpls_lse[1],
+                                              rule->mask.mpls_lse[1]);
+                       break;
+               case NPC_MPLS3_LBTCBOS:
+                       RVU_DBG_PRINT_MPLS_LBTCBOS(rule->packet.mpls_lse[2],
+                                                  rule->mask.mpls_lse[2]);
+                       break;
+               case NPC_MPLS3_TTL:
+                       RVU_DBG_PRINT_MPLS_TTL(rule->packet.mpls_lse[2],
+                                              rule->mask.mpls_lse[2]);
+                       break;
+               case NPC_MPLS4_LBTCBOS:
+                       RVU_DBG_PRINT_MPLS_LBTCBOS(rule->packet.mpls_lse[3],
+                                                  rule->mask.mpls_lse[3]);
+                       break;
+               case NPC_MPLS4_TTL:
+                       RVU_DBG_PRINT_MPLS_TTL(rule->packet.mpls_lse[3],
+                                              rule->mask.mpls_lse[3]);
+                       break;
                default:
                        seq_puts(s, "\n");
                        break;
 
        [NPC_DPORT_SCTP] = "sctp destination port",
        [NPC_LXMB]      = "Mcast/Bcast header ",
        [NPC_IPSEC_SPI] = "SPI ",
+       [NPC_MPLS1_LBTCBOS] = "lse depth 1 label tc bos",
+       [NPC_MPLS1_TTL]     = "lse depth 1 ttl",
+       [NPC_MPLS2_LBTCBOS] = "lse depth 2 label tc bos",
+       [NPC_MPLS2_TTL]     = "lse depth 2 ttl",
+       [NPC_MPLS3_LBTCBOS] = "lse depth 3 label tc bos",
+       [NPC_MPLS3_TTL]     = "lse depth 3 ttl",
+       [NPC_MPLS4_LBTCBOS] = "lse depth 4 label tc bos",
+       [NPC_MPLS4_TTL]     = "lse depth 4",
        [NPC_UNKNOWN]   = "unknown",
 };
 
 
        NPC_SCAN_HDR(NPC_IPSEC_SPI, NPC_LID_LD, NPC_LT_LD_AH, 4, 4);
        NPC_SCAN_HDR(NPC_IPSEC_SPI, NPC_LID_LE, NPC_LT_LE_ESP, 0, 4);
+       NPC_SCAN_HDR(NPC_MPLS1_LBTCBOS, NPC_LID_LC, NPC_LT_LC_MPLS, 0, 3);
+       NPC_SCAN_HDR(NPC_MPLS1_TTL, NPC_LID_LC, NPC_LT_LC_MPLS, 3, 1);
+       NPC_SCAN_HDR(NPC_MPLS2_LBTCBOS, NPC_LID_LC, NPC_LT_LC_MPLS, 4, 3);
+       NPC_SCAN_HDR(NPC_MPLS2_TTL, NPC_LID_LC, NPC_LT_LC_MPLS, 7, 1);
+       NPC_SCAN_HDR(NPC_MPLS3_LBTCBOS, NPC_LID_LC, NPC_LT_LC_MPLS, 8, 3);
+       NPC_SCAN_HDR(NPC_MPLS3_TTL, NPC_LID_LC, NPC_LT_LC_MPLS, 11, 1);
+       NPC_SCAN_HDR(NPC_MPLS4_LBTCBOS, NPC_LID_LC, NPC_LT_LC_MPLS, 12, 3);
+       NPC_SCAN_HDR(NPC_MPLS4_TTL, NPC_LID_LC, NPC_LT_LC_MPLS, 15, 1);
 
        /* SMAC follows the DMAC(which is 6 bytes) */
        NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start + 6, 6);
        /* for L2M/L2B/L3M/L3B, check if the type is present in the key */
        if (npc_check_field(rvu, blkaddr, NPC_LXMB, intf))
                *features |= BIT_ULL(NPC_LXMB);
+
+       for (hdr = NPC_MPLS1_LBTCBOS; hdr <= NPC_MPLS4_TTL; hdr++) {
+               if (npc_check_field(rvu, blkaddr, hdr, intf))
+                       *features |= BIT_ULL(hdr);
+       }
 }
 
 /* Scan key extraction profile and record how fields of our interest
        NPC_WRITE_FLOW(NPC_INNER_VID, vlan_itci, ntohs(pkt->vlan_itci), 0,
                       ntohs(mask->vlan_itci), 0);
 
+       NPC_WRITE_FLOW(NPC_MPLS1_LBTCBOS, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                pkt->mpls_lse[0]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                mask->mpls_lse[0]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS1_TTL, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                pkt->mpls_lse[0]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                mask->mpls_lse[0]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS2_LBTCBOS, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                pkt->mpls_lse[1]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                mask->mpls_lse[1]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS2_TTL, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                pkt->mpls_lse[1]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                mask->mpls_lse[1]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS3_LBTCBOS, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                pkt->mpls_lse[2]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                mask->mpls_lse[2]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS3_TTL, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                pkt->mpls_lse[2]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                mask->mpls_lse[2]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS4_LBTCBOS, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                pkt->mpls_lse[3]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_NON_TTL,
+                                mask->mpls_lse[3]), 0);
+       NPC_WRITE_FLOW(NPC_MPLS4_TTL, mpls_lse,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                pkt->mpls_lse[3]), 0,
+                      FIELD_GET(OTX2_FLOWER_MASK_MPLS_TTL,
+                                mask->mpls_lse[3]), 0);
+
        NPC_WRITE_FLOW(NPC_IPFRAG_IPV6, next_header, pkt->next_header, 0,
                       mask->next_header, 0);
        npc_update_ipv6_flow(rvu, entry, features, pkt, mask, output, intf);
 
 #define CN10K_TLX_BURST_MANTISSA       GENMASK_ULL(43, 29)
 #define CN10K_TLX_BURST_EXPONENT       GENMASK_ULL(47, 44)
 
+#define OTX2_UNSUPP_LSE_DEPTH          GENMASK(6, 4)
+
 struct otx2_tc_flow_stats {
        u64 bytes;
        u64 pkts;
              BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
              BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
              BIT(FLOW_DISSECTOR_KEY_IPSEC) |
+             BIT_ULL(FLOW_DISSECTOR_KEY_MPLS) |
              BIT_ULL(FLOW_DISSECTOR_KEY_IP))))  {
                netdev_info(nic->netdev, "unsupported flow used key 0x%llx",
                            dissector->used_keys);
                }
        }
 
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_MPLS)) {
+               struct flow_match_mpls match;
+               u8 bit;
+
+               flow_rule_match_mpls(rule, &match);
+
+               if (match.mask->used_lses & OTX2_UNSUPP_LSE_DEPTH) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "unsupported LSE depth for MPLS match offload");
+                       return -EOPNOTSUPP;
+               }
+
+               for_each_set_bit(bit, (unsigned long *)&match.mask->used_lses,
+                                FLOW_DIS_MPLS_MAX)  {
+                       /* check if any of the fields LABEL,TC,BOS are set */
+                       if (*((u32 *)&match.mask->ls[bit]) &
+                           OTX2_FLOWER_MASK_MPLS_NON_TTL) {
+                               /* Hardware will capture 4 byte MPLS header into
+                                * two fields NPC_MPLSX_LBTCBOS and NPC_MPLSX_TTL.
+                                * Derive the associated NPC key based on header
+                                * index and offset.
+                                */
+
+                               req->features |= BIT_ULL(NPC_MPLS1_LBTCBOS +
+                                                        2 * bit);
+                               flow_spec->mpls_lse[bit] =
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_LB,
+                                                  match.key->ls[bit].mpls_label) |
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_TC,
+                                                  match.key->ls[bit].mpls_tc) |
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_BOS,
+                                                  match.key->ls[bit].mpls_bos);
+
+                               flow_mask->mpls_lse[bit] =
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_LB,
+                                                  match.mask->ls[bit].mpls_label) |
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_TC,
+                                                  match.mask->ls[bit].mpls_tc) |
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_BOS,
+                                                  match.mask->ls[bit].mpls_bos);
+                       }
+
+                       if (match.mask->ls[bit].mpls_ttl) {
+                               req->features |= BIT_ULL(NPC_MPLS1_TTL +
+                                                        2 * bit);
+                               flow_spec->mpls_lse[bit] |=
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_TTL,
+                                                  match.key->ls[bit].mpls_ttl);
+                               flow_mask->mpls_lse[bit] |=
+                                       FIELD_PREP(OTX2_FLOWER_MASK_MPLS_TTL,
+                                                  match.mask->ls[bit].mpls_ttl);
+                       }
+               }
+       }
+
        return otx2_tc_parse_actions(nic, &rule->action, req, f, node);
 }