1c9da1ac1SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
2c9da1ac1SSteen Hegelund /* Microchip VCAP API
3c9da1ac1SSteen Hegelund  *
4c9da1ac1SSteen Hegelund  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5c9da1ac1SSteen Hegelund  */
6c9da1ac1SSteen Hegelund 
7c9da1ac1SSteen Hegelund #include <net/tcp.h>
8c9da1ac1SSteen Hegelund 
9c9da1ac1SSteen Hegelund #include "sparx5_tc.h"
10c9da1ac1SSteen Hegelund #include "vcap_api.h"
11c9da1ac1SSteen Hegelund #include "vcap_api_client.h"
12c9da1ac1SSteen Hegelund #include "sparx5_main.h"
13c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h"
14c9da1ac1SSteen Hegelund 
15c9da1ac1SSteen Hegelund struct sparx5_tc_flower_parse_usage {
16c9da1ac1SSteen Hegelund 	struct flow_cls_offload *fco;
17c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
18c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
19d6c2964dSSteen Hegelund 	u16 l3_proto;
20d6c2964dSSteen Hegelund 	u8 l4_proto;
21c9da1ac1SSteen Hegelund 	unsigned int used_keys;
22c9da1ac1SSteen Hegelund };
23c9da1ac1SSteen Hegelund 
24d6c2964dSSteen Hegelund /* These protocols have dedicated keysets in IS2 and a TC dissector
25d6c2964dSSteen Hegelund  * ETH_P_ARP does not have a TC dissector
26d6c2964dSSteen Hegelund  */
27d6c2964dSSteen Hegelund static u16 sparx5_tc_known_etypes[] = {
28d6c2964dSSteen Hegelund 	ETH_P_ALL,
29d6c2964dSSteen Hegelund 	ETH_P_IP,
30d6c2964dSSteen Hegelund 	ETH_P_IPV6,
31d6c2964dSSteen Hegelund };
32d6c2964dSSteen Hegelund 
33d6c2964dSSteen Hegelund static bool sparx5_tc_is_known_etype(u16 etype)
34d6c2964dSSteen Hegelund {
35d6c2964dSSteen Hegelund 	int idx;
36d6c2964dSSteen Hegelund 
37d6c2964dSSteen Hegelund 	/* For now this only knows about IS2 traffic classification */
38d6c2964dSSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx)
39d6c2964dSSteen Hegelund 		if (sparx5_tc_known_etypes[idx] == etype)
40d6c2964dSSteen Hegelund 			return true;
41d6c2964dSSteen Hegelund 
42d6c2964dSSteen Hegelund 	return false;
43d6c2964dSSteen Hegelund }
44d6c2964dSSteen Hegelund 
45c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
46c9da1ac1SSteen Hegelund {
47c9da1ac1SSteen Hegelund 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
48c9da1ac1SSteen Hegelund 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
49c9da1ac1SSteen Hegelund 	struct flow_match_eth_addrs match;
50c9da1ac1SSteen Hegelund 	struct vcap_u48_key smac, dmac;
51c9da1ac1SSteen Hegelund 	int err = 0;
52c9da1ac1SSteen Hegelund 
53c9da1ac1SSteen Hegelund 	flow_rule_match_eth_addrs(st->frule, &match);
54c9da1ac1SSteen Hegelund 
55c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->src)) {
56c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
57c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
58c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
59c9da1ac1SSteen Hegelund 		if (err)
60c9da1ac1SSteen Hegelund 			goto out;
61c9da1ac1SSteen Hegelund 	}
62c9da1ac1SSteen Hegelund 
63c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->dst)) {
64c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
65c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
66c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
67c9da1ac1SSteen Hegelund 		if (err)
68c9da1ac1SSteen Hegelund 			goto out;
69c9da1ac1SSteen Hegelund 	}
70c9da1ac1SSteen Hegelund 
71c9da1ac1SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
72c9da1ac1SSteen Hegelund 
73c9da1ac1SSteen Hegelund 	return err;
74c9da1ac1SSteen Hegelund 
75c9da1ac1SSteen Hegelund out:
76c9da1ac1SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
77c9da1ac1SSteen Hegelund 	return err;
78c9da1ac1SSteen Hegelund }
79c9da1ac1SSteen Hegelund 
80d6c2964dSSteen Hegelund static int
81d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st)
82d6c2964dSSteen Hegelund {
83d6c2964dSSteen Hegelund 	int err = 0;
84d6c2964dSSteen Hegelund 
85d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IP) {
86d6c2964dSSteen Hegelund 		struct flow_match_ipv4_addrs mt;
87d6c2964dSSteen Hegelund 
88d6c2964dSSteen Hegelund 		flow_rule_match_ipv4_addrs(st->frule, &mt);
89d6c2964dSSteen Hegelund 		if (mt.mask->src) {
90d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
91d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_SIP,
92d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->src),
93d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->src));
94d6c2964dSSteen Hegelund 			if (err)
95d6c2964dSSteen Hegelund 				goto out;
96d6c2964dSSteen Hegelund 		}
97d6c2964dSSteen Hegelund 		if (mt.mask->dst) {
98d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
99d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_DIP,
100d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->dst),
101d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->dst));
102d6c2964dSSteen Hegelund 			if (err)
103d6c2964dSSteen Hegelund 				goto out;
104d6c2964dSSteen Hegelund 		}
105d6c2964dSSteen Hegelund 	}
106d6c2964dSSteen Hegelund 
107d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
108d6c2964dSSteen Hegelund 
109d6c2964dSSteen Hegelund 	return err;
110d6c2964dSSteen Hegelund 
111d6c2964dSSteen Hegelund out:
112d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error");
113d6c2964dSSteen Hegelund 	return err;
114d6c2964dSSteen Hegelund }
115d6c2964dSSteen Hegelund 
116d6c2964dSSteen Hegelund static int
117d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st)
118d6c2964dSSteen Hegelund {
119d6c2964dSSteen Hegelund 	int err = 0;
120d6c2964dSSteen Hegelund 
121d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IPV6) {
122d6c2964dSSteen Hegelund 		struct flow_match_ipv6_addrs mt;
123d6c2964dSSteen Hegelund 		struct vcap_u128_key sip;
124d6c2964dSSteen Hegelund 		struct vcap_u128_key dip;
125d6c2964dSSteen Hegelund 
126d6c2964dSSteen Hegelund 		flow_rule_match_ipv6_addrs(st->frule, &mt);
127d6c2964dSSteen Hegelund 		/* Check if address masks are non-zero */
128d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->src)) {
129d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16);
130d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16);
131d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
132d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_SIP, &sip);
133d6c2964dSSteen Hegelund 			if (err)
134d6c2964dSSteen Hegelund 				goto out;
135d6c2964dSSteen Hegelund 		}
136d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->dst)) {
137d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16);
138d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16);
139d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
140d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_DIP, &dip);
141d6c2964dSSteen Hegelund 			if (err)
142d6c2964dSSteen Hegelund 				goto out;
143d6c2964dSSteen Hegelund 		}
144d6c2964dSSteen Hegelund 	}
145d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
146d6c2964dSSteen Hegelund 	return err;
147d6c2964dSSteen Hegelund out:
148d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error");
149d6c2964dSSteen Hegelund 	return err;
150d6c2964dSSteen Hegelund }
151d6c2964dSSteen Hegelund 
152d6c2964dSSteen Hegelund static int
153d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st)
154d6c2964dSSteen Hegelund {
155d6c2964dSSteen Hegelund 	struct flow_match_control mt;
156d6c2964dSSteen Hegelund 	u32 value, mask;
157d6c2964dSSteen Hegelund 	int err = 0;
158d6c2964dSSteen Hegelund 
159d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
160d6c2964dSSteen Hegelund 
161d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
162d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
163d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
164d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
165d6c2964dSSteen Hegelund 				mask = 0x3;
166d6c2964dSSteen Hegelund 			} else {
167d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
168d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
169d6c2964dSSteen Hegelund 					mask = 0x3;
170d6c2964dSSteen Hegelund 				} else {
171d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
172d6c2964dSSteen Hegelund 					mask = 0x3;
173d6c2964dSSteen Hegelund 				}
174d6c2964dSSteen Hegelund 			}
175d6c2964dSSteen Hegelund 		} else {
176d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
177d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
178d6c2964dSSteen Hegelund 				mask = 0x3;
179d6c2964dSSteen Hegelund 			} else {
180d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
181d6c2964dSSteen Hegelund 				mask = 0x3;
182d6c2964dSSteen Hegelund 			}
183d6c2964dSSteen Hegelund 		}
184d6c2964dSSteen Hegelund 
185d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
186d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
187d6c2964dSSteen Hegelund 					    value, mask);
188d6c2964dSSteen Hegelund 		if (err)
189d6c2964dSSteen Hegelund 			goto out;
190d6c2964dSSteen Hegelund 	}
191d6c2964dSSteen Hegelund 
192d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
193d6c2964dSSteen Hegelund 
194d6c2964dSSteen Hegelund 	return err;
195d6c2964dSSteen Hegelund 
196d6c2964dSSteen Hegelund out:
197d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
198d6c2964dSSteen Hegelund 	return err;
199d6c2964dSSteen Hegelund }
200d6c2964dSSteen Hegelund 
201d6c2964dSSteen Hegelund static int
202d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st)
203d6c2964dSSteen Hegelund {
204d6c2964dSSteen Hegelund 	struct flow_match_ports mt;
205d6c2964dSSteen Hegelund 	u16 value, mask;
206d6c2964dSSteen Hegelund 	int err = 0;
207d6c2964dSSteen Hegelund 
208d6c2964dSSteen Hegelund 	flow_rule_match_ports(st->frule, &mt);
209d6c2964dSSteen Hegelund 
210d6c2964dSSteen Hegelund 	if (mt.mask->src) {
211d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->src);
212d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->src);
213d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value,
214d6c2964dSSteen Hegelund 					    mask);
215d6c2964dSSteen Hegelund 		if (err)
216d6c2964dSSteen Hegelund 			goto out;
217d6c2964dSSteen Hegelund 	}
218d6c2964dSSteen Hegelund 
219d6c2964dSSteen Hegelund 	if (mt.mask->dst) {
220d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->dst);
221d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->dst);
222d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value,
223d6c2964dSSteen Hegelund 					    mask);
224d6c2964dSSteen Hegelund 		if (err)
225d6c2964dSSteen Hegelund 			goto out;
226d6c2964dSSteen Hegelund 	}
227d6c2964dSSteen Hegelund 
228d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS);
229d6c2964dSSteen Hegelund 
230d6c2964dSSteen Hegelund 	return err;
231d6c2964dSSteen Hegelund 
232d6c2964dSSteen Hegelund out:
233d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error");
234d6c2964dSSteen Hegelund 	return err;
235d6c2964dSSteen Hegelund }
236d6c2964dSSteen Hegelund 
237d6c2964dSSteen Hegelund static int
238d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st)
239d6c2964dSSteen Hegelund {
240d6c2964dSSteen Hegelund 	struct flow_match_basic mt;
241d6c2964dSSteen Hegelund 	int err = 0;
242d6c2964dSSteen Hegelund 
243d6c2964dSSteen Hegelund 	flow_rule_match_basic(st->frule, &mt);
244d6c2964dSSteen Hegelund 
245d6c2964dSSteen Hegelund 	if (mt.mask->n_proto) {
246d6c2964dSSteen Hegelund 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
247d6c2964dSSteen Hegelund 		if (!sparx5_tc_is_known_etype(st->l3_proto)) {
248d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
249d6c2964dSSteen Hegelund 						    st->l3_proto, ~0);
250d6c2964dSSteen Hegelund 			if (err)
251d6c2964dSSteen Hegelund 				goto out;
252d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IP) {
253d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
254d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
255d6c2964dSSteen Hegelund 			if (err)
256d6c2964dSSteen Hegelund 				goto out;
257d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IPV6) {
258d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
259d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
260d6c2964dSSteen Hegelund 			if (err)
261d6c2964dSSteen Hegelund 				goto out;
262d6c2964dSSteen Hegelund 		}
263d6c2964dSSteen Hegelund 	}
264d6c2964dSSteen Hegelund 
265d6c2964dSSteen Hegelund 	if (mt.mask->ip_proto) {
266d6c2964dSSteen Hegelund 		st->l4_proto = mt.key->ip_proto;
267d6c2964dSSteen Hegelund 		if (st->l4_proto == IPPROTO_TCP) {
268d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
269d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
270d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
271d6c2964dSSteen Hegelund 			if (err)
272d6c2964dSSteen Hegelund 				goto out;
273d6c2964dSSteen Hegelund 		} else if (st->l4_proto == IPPROTO_UDP) {
274d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
275d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
276d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
277d6c2964dSSteen Hegelund 			if (err)
278d6c2964dSSteen Hegelund 				goto out;
279d6c2964dSSteen Hegelund 		} else {
280d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
281d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP_PROTO,
282d6c2964dSSteen Hegelund 						    st->l4_proto, ~0);
283d6c2964dSSteen Hegelund 			if (err)
284d6c2964dSSteen Hegelund 				goto out;
285d6c2964dSSteen Hegelund 		}
286d6c2964dSSteen Hegelund 	}
287d6c2964dSSteen Hegelund 
288d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
289d6c2964dSSteen Hegelund 
290d6c2964dSSteen Hegelund 	return err;
291d6c2964dSSteen Hegelund 
292d6c2964dSSteen Hegelund out:
293d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
294d6c2964dSSteen Hegelund 	return err;
295d6c2964dSSteen Hegelund }
296d6c2964dSSteen Hegelund 
297d6c2964dSSteen Hegelund static int
298d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st)
299d6c2964dSSteen Hegelund {
300d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
301d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
302d6c2964dSSteen Hegelund 	struct flow_match_vlan mt;
303d6c2964dSSteen Hegelund 	int err;
304d6c2964dSSteen Hegelund 
305d6c2964dSSteen Hegelund 	flow_rule_match_vlan(st->frule, &mt);
306d6c2964dSSteen Hegelund 
307d6c2964dSSteen Hegelund 	if (mt.mask->vlan_id) {
308d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
309d6c2964dSSteen Hegelund 					    mt.key->vlan_id,
310d6c2964dSSteen Hegelund 					    mt.mask->vlan_id);
311d6c2964dSSteen Hegelund 		if (err)
312d6c2964dSSteen Hegelund 			goto out;
313d6c2964dSSteen Hegelund 	}
314d6c2964dSSteen Hegelund 
315d6c2964dSSteen Hegelund 	if (mt.mask->vlan_priority) {
316d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
317d6c2964dSSteen Hegelund 					    mt.key->vlan_priority,
318d6c2964dSSteen Hegelund 					    mt.mask->vlan_priority);
319d6c2964dSSteen Hegelund 		if (err)
320d6c2964dSSteen Hegelund 			goto out;
321d6c2964dSSteen Hegelund 	}
322d6c2964dSSteen Hegelund 
323d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
324d6c2964dSSteen Hegelund 
325d6c2964dSSteen Hegelund 	return err;
326d6c2964dSSteen Hegelund out:
327d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error");
328d6c2964dSSteen Hegelund 	return err;
329d6c2964dSSteen Hegelund }
330d6c2964dSSteen Hegelund 
331d6c2964dSSteen Hegelund static int
332d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
333d6c2964dSSteen Hegelund {
334d6c2964dSSteen Hegelund 	struct flow_match_tcp mt;
335d6c2964dSSteen Hegelund 	u16 tcp_flags_mask;
336d6c2964dSSteen Hegelund 	u16 tcp_flags_key;
337d6c2964dSSteen Hegelund 	enum vcap_bit val;
338d6c2964dSSteen Hegelund 	int err = 0;
339d6c2964dSSteen Hegelund 
340d6c2964dSSteen Hegelund 	flow_rule_match_tcp(st->frule, &mt);
341d6c2964dSSteen Hegelund 	tcp_flags_key = be16_to_cpu(mt.key->flags);
342d6c2964dSSteen Hegelund 	tcp_flags_mask = be16_to_cpu(mt.mask->flags);
343d6c2964dSSteen Hegelund 
344d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_FIN) {
345d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
346d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_FIN)
347d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
348d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val);
349d6c2964dSSteen Hegelund 		if (err)
350d6c2964dSSteen Hegelund 			goto out;
351d6c2964dSSteen Hegelund 	}
352d6c2964dSSteen Hegelund 
353d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_SYN) {
354d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
355d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_SYN)
356d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
357d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val);
358d6c2964dSSteen Hegelund 		if (err)
359d6c2964dSSteen Hegelund 			goto out;
360d6c2964dSSteen Hegelund 	}
361d6c2964dSSteen Hegelund 
362d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_RST) {
363d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
364d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_RST)
365d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
366d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val);
367d6c2964dSSteen Hegelund 		if (err)
368d6c2964dSSteen Hegelund 			goto out;
369d6c2964dSSteen Hegelund 	}
370d6c2964dSSteen Hegelund 
371d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_PSH) {
372d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
373d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_PSH)
374d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
375d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val);
376d6c2964dSSteen Hegelund 		if (err)
377d6c2964dSSteen Hegelund 			goto out;
378d6c2964dSSteen Hegelund 	}
379d6c2964dSSteen Hegelund 
380d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_ACK) {
381d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
382d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_ACK)
383d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
384d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val);
385d6c2964dSSteen Hegelund 		if (err)
386d6c2964dSSteen Hegelund 			goto out;
387d6c2964dSSteen Hegelund 	}
388d6c2964dSSteen Hegelund 
389d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_URG) {
390d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
391d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_URG)
392d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
393d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val);
394d6c2964dSSteen Hegelund 		if (err)
395d6c2964dSSteen Hegelund 			goto out;
396d6c2964dSSteen Hegelund 	}
397d6c2964dSSteen Hegelund 
398d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP);
399d6c2964dSSteen Hegelund 
400d6c2964dSSteen Hegelund 	return err;
401d6c2964dSSteen Hegelund 
402d6c2964dSSteen Hegelund out:
403d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error");
404d6c2964dSSteen Hegelund 	return err;
405d6c2964dSSteen Hegelund }
406d6c2964dSSteen Hegelund 
407d6c2964dSSteen Hegelund static int
408d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
409d6c2964dSSteen Hegelund {
410d6c2964dSSteen Hegelund 	struct flow_match_ip mt;
411d6c2964dSSteen Hegelund 	int err = 0;
412d6c2964dSSteen Hegelund 
413d6c2964dSSteen Hegelund 	flow_rule_match_ip(st->frule, &mt);
414d6c2964dSSteen Hegelund 
415d6c2964dSSteen Hegelund 	if (mt.mask->tos) {
416d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS,
417d6c2964dSSteen Hegelund 					    mt.key->tos,
418d6c2964dSSteen Hegelund 					    mt.mask->tos);
419d6c2964dSSteen Hegelund 		if (err)
420d6c2964dSSteen Hegelund 			goto out;
421d6c2964dSSteen Hegelund 	}
422d6c2964dSSteen Hegelund 
423d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP);
424d6c2964dSSteen Hegelund 
425d6c2964dSSteen Hegelund 	return err;
426d6c2964dSSteen Hegelund 
427d6c2964dSSteen Hegelund out:
428d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error");
429d6c2964dSSteen Hegelund 	return err;
430d6c2964dSSteen Hegelund }
431d6c2964dSSteen Hegelund 
432c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
433c9da1ac1SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
434d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage,
435d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage,
436d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
437d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage,
438d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
439d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
440d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
441d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
442c9da1ac1SSteen Hegelund };
443c9da1ac1SSteen Hegelund 
444c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
445c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
446*abc4010dSSteen Hegelund 				    struct vcap_rule *vrule,
447*abc4010dSSteen Hegelund 				    u16 *l3_proto)
448c9da1ac1SSteen Hegelund {
449c9da1ac1SSteen Hegelund 	struct sparx5_tc_flower_parse_usage state = {
450c9da1ac1SSteen Hegelund 		.fco = fco,
451c9da1ac1SSteen Hegelund 		.vrule = vrule,
452*abc4010dSSteen Hegelund 		.l3_proto = ETH_P_ALL,
453c9da1ac1SSteen Hegelund 	};
454c9da1ac1SSteen Hegelund 	int idx, err = 0;
455c9da1ac1SSteen Hegelund 
456c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
457c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
458c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
459c9da1ac1SSteen Hegelund 			continue;
460c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
461c9da1ac1SSteen Hegelund 			continue;
462c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
463c9da1ac1SSteen Hegelund 		if (err)
464c9da1ac1SSteen Hegelund 			return err;
465c9da1ac1SSteen Hegelund 	}
466*abc4010dSSteen Hegelund 
467*abc4010dSSteen Hegelund 	if (state.frule->match.dissector->used_keys ^ state.used_keys) {
468*abc4010dSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
469*abc4010dSSteen Hegelund 				   "Unsupported match item");
470*abc4010dSSteen Hegelund 		return -ENOENT;
471*abc4010dSSteen Hegelund 	}
472*abc4010dSSteen Hegelund 
473*abc4010dSSteen Hegelund 	if (l3_proto)
474*abc4010dSSteen Hegelund 		*l3_proto = state.l3_proto;
475c9da1ac1SSteen Hegelund 	return err;
476c9da1ac1SSteen Hegelund }
477c9da1ac1SSteen Hegelund 
478392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
479392d0ab0SSteen Hegelund 					 struct flow_cls_offload *fco,
480392d0ab0SSteen Hegelund 					 struct vcap_admin *admin)
481392d0ab0SSteen Hegelund {
482392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
483392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
484392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
485392d0ab0SSteen Hegelund 	u64 action_mask = 0;
486392d0ab0SSteen Hegelund 	int idx;
487392d0ab0SSteen Hegelund 
488392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
489392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
490392d0ab0SSteen Hegelund 		return -EINVAL;
491392d0ab0SSteen Hegelund 	}
492392d0ab0SSteen Hegelund 
493392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
494392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
495392d0ab0SSteen Hegelund 
496392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
497392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
498392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
499392d0ab0SSteen Hegelund 					   "More actions of the same type");
500392d0ab0SSteen Hegelund 			return -EINVAL;
501392d0ab0SSteen Hegelund 		}
502392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
503392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
504392d0ab0SSteen Hegelund 	}
505392d0ab0SSteen Hegelund 
506392d0ab0SSteen Hegelund 	/* Check that last action is a goto */
507392d0ab0SSteen Hegelund 	if (last_actent->id != FLOW_ACTION_GOTO) {
508392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
509392d0ab0SSteen Hegelund 				   "Last action must be 'goto'");
510392d0ab0SSteen Hegelund 		return -EINVAL;
511392d0ab0SSteen Hegelund 	}
512392d0ab0SSteen Hegelund 
513392d0ab0SSteen Hegelund 	/* Check if the goto chain is in the next lookup */
514392d0ab0SSteen Hegelund 	if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
515392d0ab0SSteen Hegelund 				 last_actent->chain_index)) {
516392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
517392d0ab0SSteen Hegelund 				   "Invalid goto chain");
518392d0ab0SSteen Hegelund 		return -EINVAL;
519392d0ab0SSteen Hegelund 	}
520392d0ab0SSteen Hegelund 
521392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
522392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
523392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
524392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
525392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
526392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
527392d0ab0SSteen Hegelund 	}
528392d0ab0SSteen Hegelund 
529392d0ab0SSteen Hegelund 	return 0;
530392d0ab0SSteen Hegelund }
531392d0ab0SSteen Hegelund 
532c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
533c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
534c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
535c9da1ac1SSteen Hegelund {
536c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
537c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
538c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
539c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
540c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
541*abc4010dSSteen Hegelund 	u16 l3_proto;
542c9da1ac1SSteen Hegelund 	int err, idx;
543c9da1ac1SSteen Hegelund 
544c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
545392d0ab0SSteen Hegelund 
546392d0ab0SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, fco, admin);
547392d0ab0SSteen Hegelund 	if (err)
548392d0ab0SSteen Hegelund 		return err;
549392d0ab0SSteen Hegelund 
550c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
551c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
552c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
553c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
554c9da1ac1SSteen Hegelund 
555c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
556*abc4010dSSteen Hegelund 	sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
557392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
558c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
559c9da1ac1SSteen Hegelund 		switch (act->id) {
560c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
561c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_bit(vrule,
562c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_COPY_ENA,
563c9da1ac1SSteen Hegelund 						       VCAP_BIT_1);
564c9da1ac1SSteen Hegelund 			if (err)
565c9da1ac1SSteen Hegelund 				goto out;
566c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule,
567c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_QUEUE_NUM, 0);
568c9da1ac1SSteen Hegelund 			if (err)
569c9da1ac1SSteen Hegelund 				goto out;
570c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
571c9da1ac1SSteen Hegelund 						       SPX5_PMM_REPLACE_ALL);
572c9da1ac1SSteen Hegelund 			if (err)
573c9da1ac1SSteen Hegelund 				goto out;
574c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
575c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
576c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
577c9da1ac1SSteen Hegelund 			if (err)
578c9da1ac1SSteen Hegelund 				goto out;
579c9da1ac1SSteen Hegelund 			break;
580c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
581c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
582c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
583c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
584c9da1ac1SSteen Hegelund 			if (err)
585c9da1ac1SSteen Hegelund 				goto out;
586c9da1ac1SSteen Hegelund 			break;
587392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
588392d0ab0SSteen Hegelund 			/* Links between VCAPs will be added later */
589392d0ab0SSteen Hegelund 			break;
590c9da1ac1SSteen Hegelund 		default:
591c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
592c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
593c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
594c9da1ac1SSteen Hegelund 			goto out;
595c9da1ac1SSteen Hegelund 		}
596c9da1ac1SSteen Hegelund 	}
597*abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
598*abc4010dSSteen Hegelund 	err = vcap_val_rule(vrule, l3_proto);
599c9da1ac1SSteen Hegelund 	if (err) {
600c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
601c9da1ac1SSteen Hegelund 		goto out;
602c9da1ac1SSteen Hegelund 	}
603c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
604c9da1ac1SSteen Hegelund 	if (err)
605c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
606c9da1ac1SSteen Hegelund 				   "Could not add the filter");
607c9da1ac1SSteen Hegelund out:
608c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
609c9da1ac1SSteen Hegelund 	return err;
610c9da1ac1SSteen Hegelund }
611c9da1ac1SSteen Hegelund 
612c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
613c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
614c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
615c9da1ac1SSteen Hegelund {
616c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
617c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
618c9da1ac1SSteen Hegelund 	int err = -ENOENT, rule_id;
619c9da1ac1SSteen Hegelund 
620c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
621c9da1ac1SSteen Hegelund 	while (true) {
622c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
623c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
624c9da1ac1SSteen Hegelund 			break;
625c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
626c9da1ac1SSteen Hegelund 		if (err) {
627c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
628c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
629c9da1ac1SSteen Hegelund 			break;
630c9da1ac1SSteen Hegelund 		}
631c9da1ac1SSteen Hegelund 	}
632c9da1ac1SSteen Hegelund 	return err;
633c9da1ac1SSteen Hegelund }
634c9da1ac1SSteen Hegelund 
635c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
636c9da1ac1SSteen Hegelund 		     bool ingress)
637c9da1ac1SSteen Hegelund {
638c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
639c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
640c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
641c9da1ac1SSteen Hegelund 	int err = -EINVAL;
642c9da1ac1SSteen Hegelund 
643c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
644c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
645c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
646c9da1ac1SSteen Hegelund 	if (!admin) {
647c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
648c9da1ac1SSteen Hegelund 		return err;
649c9da1ac1SSteen Hegelund 	}
650c9da1ac1SSteen Hegelund 
651c9da1ac1SSteen Hegelund 	switch (fco->command) {
652c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
653c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin);
654c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
655c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
656c9da1ac1SSteen Hegelund 	default:
657c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
658c9da1ac1SSteen Hegelund 	}
659c9da1ac1SSteen Hegelund }
660