diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c
index 748c618db7d8..b76593b40097 100644
--- a/drivers/net/ethernet/mscc/ocelot_ace.c
+++ b/drivers/net/ethernet/mscc/ocelot_ace.c
@@ -341,6 +341,8 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
 		vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
 		vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
 		break;
+	default:
+		break;
 	}
 }
 
@@ -644,9 +646,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
 }
 
 static void vcap_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule,
-			   int ix)
+			   int ix, int block_id)
 {
-	const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS2];
+	const struct vcap_props *vcap = &ocelot->vcap[block_id];
 	struct vcap_data data;
 	int row, count;
 	u32 cnt;
@@ -663,6 +665,19 @@ static void vcap_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule,
 	rule->stats.pkts = cnt;
 }
 
+static void vcap_entry_set(struct ocelot *ocelot, int ix,
+			   struct ocelot_ace_rule *ace,
+			   int block_id)
+{
+	switch (block_id) {
+	case VCAP_IS2:
+		is2_entry_set(ocelot, ix, ace);
+		break;
+	default:
+		break;
+	}
+}
+
 static void ocelot_ace_rule_add(struct ocelot *ocelot,
 				struct ocelot_acl_block *block,
 				struct ocelot_ace_rule *rule)
@@ -790,7 +805,7 @@ static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace)
 static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
 						 struct ocelot_ace_rule *ace)
 {
-	struct ocelot_acl_block *block = &ocelot->acl_block;
+	struct ocelot_acl_block *block = &ocelot->acl_block[VCAP_IS2];
 	struct ocelot_ace_rule *tmp;
 	unsigned long port;
 	int i;
@@ -824,15 +839,16 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
 	return true;
 }
 
-int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int block_id,
 				struct ocelot_ace_rule *rule,
 				struct netlink_ext_ack *extack)
 {
-	struct ocelot_acl_block *block = &ocelot->acl_block;
+	struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
 	struct ocelot_ace_rule *ace;
 	int i, index;
 
-	if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
+	if (block_id == VCAP_IS2 &&
+	    !ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
 		NL_SET_ERR_MSG_MOD(extack,
 				   "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
 		return -EBUSY;
@@ -847,11 +863,11 @@ int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
 	/* Move down the rules to make place for the new rule */
 	for (i = block->count - 1; i > index; i--) {
 		ace = ocelot_ace_rule_get_rule_index(block, i);
-		is2_entry_set(ocelot, i, ace);
+		vcap_entry_set(ocelot, i, ace, block_id);
 	}
 
 	/* Now insert the new rule */
-	is2_entry_set(ocelot, index, rule);
+	vcap_entry_set(ocelot, index, rule, block_id);
 	return 0;
 }
 
@@ -902,10 +918,10 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot,
 	block->count--;
 }
 
-int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int block_id,
 				struct ocelot_ace_rule *rule)
 {
-	struct ocelot_acl_block *block = &ocelot->acl_block;
+	struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
 	struct ocelot_ace_rule del_ace;
 	struct ocelot_ace_rule *ace;
 	int i, index;
@@ -921,29 +937,29 @@ int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
 	/* Move up all the blocks over the deleted rule */
 	for (i = index; i < block->count; i++) {
 		ace = ocelot_ace_rule_get_rule_index(block, i);
-		is2_entry_set(ocelot, i, ace);
+		vcap_entry_set(ocelot, i, ace, block_id);
 	}
 
 	/* Now delete the last rule, because it is duplicated */
-	is2_entry_set(ocelot, block->count, &del_ace);
+	vcap_entry_set(ocelot, block->count, &del_ace, block_id);
 
 	return 0;
 }
 
-int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
+int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int block_id,
 				 struct ocelot_ace_rule *rule)
 {
-	struct ocelot_acl_block *block = &ocelot->acl_block;
+	struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
 	struct ocelot_ace_rule *tmp;
 	int index;
 
 	index = ocelot_ace_rule_get_index_id(block, rule);
-	vcap_entry_get(ocelot, rule, index);
+	vcap_entry_get(ocelot, rule, index, block_id);
 
 	/* After we get the result we need to clear the counters */
 	tmp = ocelot_ace_rule_get_rule_index(block, index);
 	tmp->stats.pkts = 0;
-	is2_entry_set(ocelot, index, tmp);
+	vcap_entry_set(ocelot, index, tmp, block_id);
 
 	return 0;
 }
@@ -968,7 +984,7 @@ static void vcap_init(struct ocelot *ocelot, const struct vcap_props *vcap)
 
 int ocelot_ace_init(struct ocelot *ocelot)
 {
-	struct ocelot_acl_block *block = &ocelot->acl_block;
+	struct ocelot_acl_block *block;
 
 	vcap_init(ocelot, &ocelot->vcap[VCAP_IS2]);
 
@@ -987,6 +1003,7 @@ int ocelot_ace_init(struct ocelot *ocelot)
 	ocelot_write_gix(ocelot, 0x3fffff, ANA_POL_CIR_STATE,
 			 OCELOT_POLICER_DISCARD);
 
+	block = &ocelot->acl_block[VCAP_IS2];
 	block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
 	INIT_LIST_HEAD(&block->rules);
 
diff --git a/drivers/net/ethernet/mscc/ocelot_ace.h b/drivers/net/ethernet/mscc/ocelot_ace.h
index 099e177f2617..a9fd99401a65 100644
--- a/drivers/net/ethernet/mscc/ocelot_ace.h
+++ b/drivers/net/ethernet/mscc/ocelot_ace.h
@@ -175,6 +175,7 @@ struct ocelot_ace_frame_ipv6 {
 };
 
 enum ocelot_ace_action {
+	OCELOT_ACL_ACTION_NULL,
 	OCELOT_ACL_ACTION_DROP,
 	OCELOT_ACL_ACTION_TRAP,
 	OCELOT_ACL_ACTION_POLICE,
@@ -214,12 +215,12 @@ struct ocelot_ace_rule {
 	u32 pol_ix;
 };
 
-int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int block_id,
 				struct ocelot_ace_rule *rule,
 				struct netlink_ext_ack *extack);
-int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int block_id,
 				struct ocelot_ace_rule *rule);
-int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
+int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int block_id,
 				 struct ocelot_ace_rule *rule);
 
 int ocelot_ace_init(struct ocelot *ocelot);
diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c
index 891925f73cbc..a1f7b6b28170 100644
--- a/drivers/net/ethernet/mscc/ocelot_flower.c
+++ b/drivers/net/ethernet/mscc/ocelot_flower.c
@@ -9,13 +9,26 @@
 
 #include "ocelot_ace.h"
 
+static int ocelot_block_id_get(int chain, bool ingress)
+{
+	/* Select TCAM blocks by using chain index. Rules in chain 0 are
+	 * implemented on IS1, chain 1 are implemented on IS2, and egress
+	 * chain corresponds to ES0 block.
+	 */
+	if (ingress)
+		return chain ? VCAP_IS2 : VCAP_IS1;
+	else
+		return VCAP_ES0;
+}
+
 static int ocelot_flower_parse_action(struct flow_cls_offload *f,
 				      struct ocelot_ace_rule *ace)
 {
+	struct netlink_ext_ack *extack = f->common.extack;
 	const struct flow_action_entry *a;
+	int i, allowed_chain = 0;
 	s64 burst;
 	u64 rate;
-	int i;
 
 	if (!flow_offload_has_one_action(&f->rule->action))
 		return -EOPNOTSUPP;
@@ -28,9 +41,11 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
 		switch (a->id) {
 		case FLOW_ACTION_DROP:
 			ace->action = OCELOT_ACL_ACTION_DROP;
+			allowed_chain = 1;
 			break;
 		case FLOW_ACTION_TRAP:
 			ace->action = OCELOT_ACL_ACTION_TRAP;
+			allowed_chain = 1;
 			break;
 		case FLOW_ACTION_POLICE:
 			ace->action = OCELOT_ACL_ACTION_POLICE;
@@ -38,10 +53,23 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
 			ace->pol.rate = div_u64(rate, 1000) * 8;
 			burst = rate * PSCHED_NS2TICKS(a->police.burst);
 			ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
+			allowed_chain = 1;
+			break;
+		case FLOW_ACTION_GOTO:
+			if (a->chain_index != f->common.chain_index + 1) {
+				NL_SET_ERR_MSG_MOD(extack, "HW only support goto next chain\n");
+				return -EOPNOTSUPP;
+			}
+			ace->action = OCELOT_ACL_ACTION_NULL;
+			allowed_chain = f->common.chain_index;
 			break;
 		default:
 			return -EOPNOTSUPP;
 		}
+		if (f->common.chain_index != allowed_chain) {
+			NL_SET_ERR_MSG_MOD(extack, "Action is not supported on this chain\n");
+			return -EOPNOTSUPP;
+		}
 	}
 
 	return 0;
@@ -205,7 +233,7 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
 			      struct flow_cls_offload *f, bool ingress)
 {
 	struct ocelot_ace_rule *ace;
-	int ret;
+	int ret, block_id;
 
 	ace = ocelot_ace_rule_create(ocelot, port, f);
 	if (!ace)
@@ -216,8 +244,10 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
 		kfree(ace);
 		return ret;
 	}
+	block_id = ocelot_block_id_get(f->common.chain_index, ingress);
 
-	return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack);
+	return ocelot_ace_rule_offload_add(ocelot, block_id, ace,
+					   f->common.extack);
 }
 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
 
@@ -225,11 +255,13 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
 			      struct flow_cls_offload *f, bool ingress)
 {
 	struct ocelot_ace_rule ace;
+	int block_id;
 
 	ace.prio = f->common.prio;
 	ace.id = f->cookie;
+	block_id = ocelot_block_id_get(f->common.chain_index, ingress);
 
-	return ocelot_ace_rule_offload_del(ocelot, &ace);
+	return ocelot_ace_rule_offload_del(ocelot, block_id, &ace);
 }
 EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
 
@@ -237,11 +269,13 @@ int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
 			    struct flow_cls_offload *f, bool ingress)
 {
 	struct ocelot_ace_rule ace;
-	int ret;
+	int ret, block_id;
 
 	ace.prio = f->common.prio;
 	ace.id = f->cookie;
-	ret = ocelot_ace_rule_stats_update(ocelot, &ace);
+	block_id = ocelot_block_id_get(f->common.chain_index, ingress);
+
+	ret = ocelot_ace_rule_stats_update(ocelot, block_id, &ace);
 	if (ret)
 		return ret;
 
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 91357b1c8f31..4b2320bdc036 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -540,7 +540,7 @@ struct ocelot {
 
 	struct list_head		multicast;
 
-	struct ocelot_acl_block		acl_block;
+	struct ocelot_acl_block		acl_block[3];
 
 	const struct vcap_props		*vcap;
 
diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h
index 26d9384b3657..495847a40490 100644
--- a/include/soc/mscc/ocelot_vcap.h
+++ b/include/soc/mscc/ocelot_vcap.h
@@ -14,9 +14,9 @@
  */
 
 enum {
-	/* VCAP_IS1, */
+	VCAP_IS1,
 	VCAP_IS2,
-	/* VCAP_ES0, */
+	VCAP_ES0,
 };
 
 struct vcap_props {
