diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
index 86600c780c95..e26efa451d4a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
@@ -60,6 +60,7 @@ struct mlxsw_sp_acl_erp_core {
 
 struct mlxsw_sp_acl_erp_key {
 	char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
+	bool ctcam;
 };
 
 struct mlxsw_sp_acl_erp {
@@ -90,6 +91,7 @@ struct mlxsw_sp_acl_erp_table {
 	unsigned long base_index;
 	unsigned int num_atcam_erps;
 	unsigned int num_max_atcam_erps;
+	unsigned int num_ctcam_erps;
 };
 
 static const struct rhashtable_params mlxsw_sp_acl_erp_ht_params = {
@@ -448,13 +450,14 @@ static void mlxsw_sp_acl_erp_table_erp_del(struct mlxsw_sp_acl_erp *erp)
 }
 
 static int
-mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table *erp_table)
+mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table *erp_table,
+			      bool ctcam_le)
 {
 	struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
 	struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
 	char pererp_pl[MLXSW_REG_PERERP_LEN];
 
-	mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
+	mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
 			      erp_table->base_index, 0);
 	mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
 					 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
@@ -471,11 +474,12 @@ mlxsw_sp_acl_erp_table_disable(struct mlxsw_sp_acl_erp_table *erp_table)
 	struct mlxsw_sp_acl_erp *master_rp;
 
 	master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
-	if (!master_rp)
-		return;
-
+	/* It is possible we do not have a master RP when we disable the
+	 * table when there are no rules in the A-TCAM and the last C-TCAM
+	 * rule is deleted
+	 */
 	mlxsw_reg_pererp_pack(pererp_pl, region->id, false, false, 0, 0,
-			      master_rp->id);
+			      master_rp ? master_rp->id : 0);
 	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
 }
 
@@ -506,6 +510,7 @@ mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table *erp_table)
 	unsigned int num_erps, old_num_erps = erp_table->num_max_atcam_erps;
 	struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
 	unsigned long old_base_index = erp_table->base_index;
+	bool ctcam_le = erp_table->num_ctcam_erps > 0;
 	int err;
 
 	if (erp_table->num_atcam_erps < erp_table->num_max_atcam_erps)
@@ -526,7 +531,7 @@ mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table *erp_table)
 	if (err)
 		goto err_table_relocate;
 
-	err = mlxsw_sp_acl_erp_table_enable(erp_table);
+	err = mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le);
 	if (err)
 		goto err_table_enable;
 
@@ -579,7 +584,7 @@ mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
 	if (err)
 		goto err_table_master_rp_add;
 
-	err = mlxsw_sp_acl_erp_table_enable(erp_table);
+	err = mlxsw_sp_acl_erp_table_enable(erp_table, false);
 	if (err)
 		goto err_table_enable;
 
@@ -619,9 +624,10 @@ mlxsw_sp_acl_erp_region_erp_add(struct mlxsw_sp_acl_erp_table *erp_table,
 {
 	struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
 	struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
+	bool ctcam_le = erp_table->num_ctcam_erps > 0;
 	char pererp_pl[MLXSW_REG_PERERP_LEN];
 
-	mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
+	mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
 			      erp_table->base_index, 0);
 	mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
 					 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
@@ -635,9 +641,10 @@ static void mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp *erp)
 	struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
 	struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
 	struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
+	bool ctcam_le = erp_table->num_ctcam_erps > 0;
 	char pererp_pl[MLXSW_REG_PERERP_LEN];
 
-	mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
+	mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
 			      erp_table->base_index, 0);
 	mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
 					 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
@@ -646,6 +653,161 @@ static void mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp *erp)
 	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
 }
 
+static int
+mlxsw_sp_acl_erp_region_ctcam_enable(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+	/* No need to re-enable lookup in the C-TCAM */
+	if (erp_table->num_ctcam_erps > 1)
+		return 0;
+
+	return mlxsw_sp_acl_erp_table_enable(erp_table, true);
+}
+
+static void
+mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+	/* Only disable C-TCAM lookup when last C-TCAM eRP is deleted */
+	if (erp_table->num_ctcam_erps > 1)
+		return;
+
+	mlxsw_sp_acl_erp_table_enable(erp_table, false);
+}
+
+static void
+mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+	switch (erp_table->num_atcam_erps) {
+	case 2:
+		/* Keep using the eRP table, but correctly set the
+		 * operations pointer so that when an A-TCAM eRP is
+		 * deleted we will transition to use the master mask
+		 */
+		erp_table->ops = &erp_two_masks_ops;
+		break;
+	case 1:
+		/* We only kept the eRP table because we had C-TCAM
+		 * eRPs in use. Now that the last C-TCAM eRP is gone we
+		 * can stop using the table and transition to use the
+		 * master mask
+		 */
+		mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
+		erp_table->ops = &erp_single_mask_ops;
+		break;
+	case 0:
+		/* There are no more eRPs of any kind used by the region
+		 * so free its eRP table and transition to initial state
+		 */
+		mlxsw_sp_acl_erp_table_disable(erp_table);
+		mlxsw_sp_acl_erp_table_free(erp_table->erp_core,
+					    erp_table->num_max_atcam_erps,
+					    erp_table->aregion->type,
+					    erp_table->base_index);
+		erp_table->ops = &erp_no_mask_ops;
+		break;
+	default:
+		break;
+	}
+}
+
+static struct mlxsw_sp_acl_erp *
+__mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
+				     struct mlxsw_sp_acl_erp_key *key)
+{
+	struct mlxsw_sp_acl_erp *erp;
+	int err;
+
+	erp = kzalloc(sizeof(*erp), GFP_KERNEL);
+	if (!erp)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(&erp->key, key, sizeof(*key));
+	bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
+			  MLXSW_SP_ACL_TCAM_MASK_LEN);
+	refcount_set(&erp->refcnt, 1);
+	erp_table->num_ctcam_erps++;
+	erp->erp_table = erp_table;
+
+	err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
+	if (err)
+		goto err_master_mask_set;
+
+	err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
+				     mlxsw_sp_acl_erp_ht_params);
+	if (err)
+		goto err_rhashtable_insert;
+
+	err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table);
+	if (err)
+		goto err_erp_region_ctcam_enable;
+
+	/* When C-TCAM is used, the eRP table must be used */
+	erp_table->ops = &erp_multiple_masks_ops;
+
+	return erp;
+
+err_erp_region_ctcam_enable:
+	rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
+			       mlxsw_sp_acl_erp_ht_params);
+err_rhashtable_insert:
+	mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+err_master_mask_set:
+	erp_table->num_ctcam_erps--;
+	kfree(erp);
+	return ERR_PTR(err);
+}
+
+static struct mlxsw_sp_acl_erp *
+mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
+				   struct mlxsw_sp_acl_erp_key *key)
+{
+	struct mlxsw_sp_acl_erp *erp;
+	int err;
+
+	/* There is a special situation where we need to spill rules
+	 * into the C-TCAM, yet the region is still using a master
+	 * mask and thus not performing a lookup in the C-TCAM. This
+	 * can happen when two rules that only differ in priority - and
+	 * thus sharing the same key - are programmed. In this case
+	 * we transition the region to use an eRP table
+	 */
+	err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
+	if (err)
+		return ERR_PTR(err);
+
+	erp = __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
+	if (IS_ERR(erp)) {
+		err = PTR_ERR(erp);
+		goto err_erp_create;
+	}
+
+	return erp;
+
+err_erp_create:
+	mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
+	return ERR_PTR(err);
+}
+
+static void
+mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp)
+{
+	struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
+
+	mlxsw_sp_acl_erp_region_ctcam_disable(erp_table);
+	rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
+			       mlxsw_sp_acl_erp_ht_params);
+	mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+	erp_table->num_ctcam_erps--;
+	kfree(erp);
+
+	/* Once the last C-TCAM eRP was destroyed, the state we
+	 * transition to depends on the number of A-TCAM eRPs currently
+	 * in use
+	 */
+	if (erp_table->num_ctcam_erps > 0)
+		return;
+	mlxsw_sp_acl_erp_ctcam_table_ops_set(erp_table);
+}
+
 static struct mlxsw_sp_acl_erp *
 mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
 			     struct mlxsw_sp_acl_erp_key *key)
@@ -653,6 +815,9 @@ mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
 	struct mlxsw_sp_acl_erp *erp;
 	int err;
 
+	if (key->ctcam)
+		return __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
+
 	/* Expand the eRP table for the new eRP, if needed */
 	err = mlxsw_sp_acl_erp_table_expand(erp_table);
 	if (err)
@@ -691,12 +856,15 @@ static void
 mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
 			      struct mlxsw_sp_acl_erp *erp)
 {
+	if (erp->key.ctcam)
+		return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp);
+
 	mlxsw_sp_acl_erp_region_erp_del(erp);
 	mlxsw_sp_acl_erp_table_erp_del(erp);
 	mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
 	mlxsw_sp_acl_erp_generic_destroy(erp);
 
-	if (erp_table->num_atcam_erps == 2)
+	if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0)
 		erp_table->ops = &erp_two_masks_ops;
 }
 
@@ -707,6 +875,9 @@ mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
 	struct mlxsw_sp_acl_erp *erp;
 	int err;
 
+	if (key->ctcam)
+		return mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
+
 	/* Transition to use eRP table instead of master mask */
 	err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
 	if (err)
@@ -749,6 +920,9 @@ static void
 mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
 				     struct mlxsw_sp_acl_erp *erp)
 {
+	if (erp->key.ctcam)
+		return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp);
+
 	mlxsw_sp_acl_erp_region_erp_del(erp);
 	mlxsw_sp_acl_erp_table_erp_del(erp);
 	mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
@@ -765,6 +939,9 @@ mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
 {
 	struct mlxsw_sp_acl_erp *erp;
 
+	if (key->ctcam)
+		return ERR_PTR(-EINVAL);
+
 	erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
 	if (IS_ERR(erp))
 		return erp;
@@ -791,7 +968,7 @@ mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
 
 struct mlxsw_sp_acl_erp *
 mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-		     const char *mask)
+		     const char *mask, bool ctcam)
 {
 	struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
 	struct mlxsw_sp_acl_erp_key key;
@@ -803,6 +980,7 @@ mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
 	ASSERT_RTNL();
 
 	memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
+	key.ctcam = ctcam;
 	erp = rhashtable_lookup_fast(&erp_table->erp_ht, &key,
 				     mlxsw_sp_acl_erp_ht_params);
 	if (erp) {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
index 5c9a479ece25..d6af9920a930 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
@@ -178,7 +178,7 @@ struct mlxsw_sp_acl_erp;
 u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp);
 struct mlxsw_sp_acl_erp *
 mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-		     const char *mask);
+		     const char *mask, bool ctcam);
 void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
 			  struct mlxsw_sp_acl_erp *erp);
 int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion);
