1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver VCAP implementation
3  *
4  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5  *
6  * The Sparx5 Chip Register Model can be browsed at this location:
7  * https://github.com/microchip-ung/sparx-5_reginfo
8  */
9 
10 #include <linux/types.h>
11 #include <linux/list.h>
12 
13 #include "vcap_api.h"
14 #include "vcap_api_client.h"
15 #include "sparx5_main_regs.h"
16 #include "sparx5_main.h"
17 #include "sparx5_vcap_impl.h"
18 #include "sparx5_vcap_ag_api.h"
19 
20 #define SUPER_VCAP_BLK_SIZE 3072 /* addresses per Super VCAP block */
21 #define STREAMSIZE (64 * 4)  /* bytes in the VCAP cache area */
22 
23 #define SPARX5_IS2_LOOKUPS 4
24 
25 /* IS2 port keyset selection control */
26 
27 /* IS2 non-ethernet traffic type keyset generation */
28 enum vcap_is2_port_sel_noneth {
29 	VCAP_IS2_PS_NONETH_MAC_ETYPE,
30 	VCAP_IS2_PS_NONETH_CUSTOM_1,
31 	VCAP_IS2_PS_NONETH_CUSTOM_2,
32 	VCAP_IS2_PS_NONETH_NO_LOOKUP
33 };
34 
35 /* IS2 IPv4 unicast traffic type keyset generation */
36 enum vcap_is2_port_sel_ipv4_uc {
37 	VCAP_IS2_PS_IPV4_UC_MAC_ETYPE,
38 	VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER,
39 	VCAP_IS2_PS_IPV4_UC_IP_7TUPLE,
40 };
41 
42 /* IS2 IPv4 multicast traffic type keyset generation */
43 enum vcap_is2_port_sel_ipv4_mc {
44 	VCAP_IS2_PS_IPV4_MC_MAC_ETYPE,
45 	VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER,
46 	VCAP_IS2_PS_IPV4_MC_IP_7TUPLE,
47 	VCAP_IS2_PS_IPV4_MC_IP4_VID,
48 };
49 
50 /* IS2 IPv6 unicast traffic type keyset generation */
51 enum vcap_is2_port_sel_ipv6_uc {
52 	VCAP_IS2_PS_IPV6_UC_MAC_ETYPE,
53 	VCAP_IS2_PS_IPV6_UC_IP_7TUPLE,
54 	VCAP_IS2_PS_IPV6_UC_IP6_STD,
55 	VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER,
56 };
57 
58 /* IS2 IPv6 multicast traffic type keyset generation */
59 enum vcap_is2_port_sel_ipv6_mc {
60 	VCAP_IS2_PS_IPV6_MC_MAC_ETYPE,
61 	VCAP_IS2_PS_IPV6_MC_IP_7TUPLE,
62 	VCAP_IS2_PS_IPV6_MC_IP6_VID,
63 	VCAP_IS2_PS_IPV6_MC_IP6_STD,
64 	VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER,
65 };
66 
67 /* IS2 ARP traffic type keyset generation */
68 enum vcap_is2_port_sel_arp {
69 	VCAP_IS2_PS_ARP_MAC_ETYPE,
70 	VCAP_IS2_PS_ARP_ARP,
71 };
72 
73 static struct sparx5_vcap_inst {
74 	enum vcap_type vtype; /* type of vcap */
75 	int vinst; /* instance number within the same type */
76 	int lookups; /* number of lookups in this vcap type */
77 	int lookups_per_instance; /* number of lookups in this instance */
78 	int first_cid; /* first chain id in this vcap */
79 	int last_cid; /* last chain id in this vcap */
80 	int count; /* number of available addresses, not in super vcap */
81 	int map_id; /* id in the super vcap block mapping (if applicable) */
82 	int blockno; /* starting block in super vcap (if applicable) */
83 	int blocks; /* number of blocks in super vcap (if applicable) */
84 } sparx5_vcap_inst_cfg[] = {
85 	{
86 		.vtype = VCAP_TYPE_IS2, /* IS2-0 */
87 		.vinst = 0,
88 		.map_id = 4,
89 		.lookups = SPARX5_IS2_LOOKUPS,
90 		.lookups_per_instance = SPARX5_IS2_LOOKUPS / 2,
91 		.first_cid = SPARX5_VCAP_CID_IS2_L0,
92 		.last_cid = SPARX5_VCAP_CID_IS2_L2 - 1,
93 		.blockno = 0, /* Maps block 0-1 */
94 		.blocks = 2,
95 	},
96 	{
97 		.vtype = VCAP_TYPE_IS2, /* IS2-1 */
98 		.vinst = 1,
99 		.map_id = 5,
100 		.lookups = SPARX5_IS2_LOOKUPS,
101 		.lookups_per_instance = SPARX5_IS2_LOOKUPS / 2,
102 		.first_cid = SPARX5_VCAP_CID_IS2_L2,
103 		.last_cid = SPARX5_VCAP_CID_IS2_MAX,
104 		.blockno = 2, /* Maps block 2-3 */
105 		.blocks = 2,
106 	},
107 };
108 
109 /* Await the super VCAP completion of the current operation */
110 static void sparx5_vcap_wait_super_update(struct sparx5 *sparx5)
111 {
112 	u32 value;
113 
114 	read_poll_timeout(spx5_rd, value,
115 			  !VCAP_SUPER_CTRL_UPDATE_SHOT_GET(value), 500, 10000,
116 			  false, sparx5, VCAP_SUPER_CTRL);
117 }
118 
119 /* Initializing a VCAP address range: only IS2 for now */
120 static void _sparx5_vcap_range_init(struct sparx5 *sparx5,
121 				    struct vcap_admin *admin,
122 				    u32 addr, u32 count)
123 {
124 	u32 size = count - 1;
125 
126 	spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) |
127 		VCAP_SUPER_CFG_MV_SIZE_SET(size),
128 		sparx5, VCAP_SUPER_CFG);
129 	spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) |
130 		VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) |
131 		VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) |
132 		VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) |
133 		VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
134 		VCAP_SUPER_CTRL_CLEAR_CACHE_SET(true) |
135 		VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
136 		sparx5, VCAP_SUPER_CTRL);
137 	sparx5_vcap_wait_super_update(sparx5);
138 }
139 
140 /* Initializing VCAP rule data area */
141 static void sparx5_vcap_block_init(struct sparx5 *sparx5,
142 				   struct vcap_admin *admin)
143 {
144 	_sparx5_vcap_range_init(sparx5, admin, admin->first_valid_addr,
145 				admin->last_valid_addr -
146 					admin->first_valid_addr);
147 }
148 
149 /* Get the keyset name from the sparx5 VCAP model */
150 static const char *sparx5_vcap_keyset_name(struct net_device *ndev,
151 					   enum vcap_keyfield_set keyset)
152 {
153 	struct sparx5_port *port = netdev_priv(ndev);
154 
155 	return port->sparx5->vcap_ctrl->stats->keyfield_set_names[keyset];
156 }
157 
158 /* Check if this is the first lookup of IS2 */
159 static bool sparx5_vcap_is2_is_first_chain(struct vcap_rule *rule)
160 {
161 	return (rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L0 &&
162 		rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L1) ||
163 		((rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L2 &&
164 		  rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L3));
165 }
166 
167 /* Set the narrow range ingress port mask on a rule */
168 static void sparx5_vcap_add_range_port_mask(struct vcap_rule *rule,
169 					    struct net_device *ndev)
170 {
171 	struct sparx5_port *port = netdev_priv(ndev);
172 	u32 port_mask;
173 	u32 range;
174 
175 	range = port->portno / BITS_PER_TYPE(u32);
176 	/* Port bit set to match-any */
177 	port_mask = ~BIT(port->portno % BITS_PER_TYPE(u32));
178 	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_SEL, 0, 0xf);
179 	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, range, 0xf);
180 	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, port_mask);
181 }
182 
183 /* Set the wide range ingress port mask on a rule */
184 static void sparx5_vcap_add_wide_port_mask(struct vcap_rule *rule,
185 					   struct net_device *ndev)
186 {
187 	struct sparx5_port *port = netdev_priv(ndev);
188 	struct vcap_u72_key port_mask;
189 	u32 range;
190 
191 	/* Port bit set to match-any */
192 	memset(port_mask.value, 0, sizeof(port_mask.value));
193 	memset(port_mask.mask, 0xff, sizeof(port_mask.mask));
194 	range = port->portno / BITS_PER_BYTE;
195 	port_mask.mask[range] = ~BIT(port->portno % BITS_PER_BYTE);
196 	vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask);
197 }
198 
199 /* API callback used for validating a field keyset (check the port keysets) */
200 static enum vcap_keyfield_set
201 sparx5_vcap_validate_keyset(struct net_device *ndev,
202 			    struct vcap_admin *admin,
203 			    struct vcap_rule *rule,
204 			    struct vcap_keyset_list *kslist,
205 			    u16 l3_proto)
206 {
207 	if (!kslist || kslist->cnt == 0)
208 		return VCAP_KFS_NO_VALUE;
209 	/* for now just return whatever the API suggests */
210 	return kslist->keysets[0];
211 }
212 
213 /* API callback used for adding default fields to a rule */
214 static void sparx5_vcap_add_default_fields(struct net_device *ndev,
215 					   struct vcap_admin *admin,
216 					   struct vcap_rule *rule)
217 {
218 	const struct vcap_field *field;
219 
220 	field = vcap_lookup_keyfield(rule, VCAP_KF_IF_IGR_PORT_MASK);
221 	if (field && field->width == SPX5_PORTS)
222 		sparx5_vcap_add_wide_port_mask(rule, ndev);
223 	else if (field && field->width == BITS_PER_TYPE(u32))
224 		sparx5_vcap_add_range_port_mask(rule, ndev);
225 	else
226 		pr_err("%s:%d: %s: could not add an ingress port mask for: %s\n",
227 		       __func__, __LINE__, netdev_name(ndev),
228 		       sparx5_vcap_keyset_name(ndev, rule->keyset));
229 	/* add the lookup bit */
230 	if (sparx5_vcap_is2_is_first_chain(rule))
231 		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1);
232 	else
233 		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0);
234 }
235 
236 /* API callback used for erasing the vcap cache area (not the register area) */
237 static void sparx5_vcap_cache_erase(struct vcap_admin *admin)
238 {
239 	memset(admin->cache.keystream, 0, STREAMSIZE);
240 	memset(admin->cache.maskstream, 0, STREAMSIZE);
241 	memset(admin->cache.actionstream, 0, STREAMSIZE);
242 	memset(&admin->cache.counter, 0, sizeof(admin->cache.counter));
243 }
244 
245 /* API callback used for writing to the VCAP cache */
246 static void sparx5_vcap_cache_write(struct net_device *ndev,
247 				    struct vcap_admin *admin,
248 				    enum vcap_selection sel,
249 				    u32 start,
250 				    u32 count)
251 {
252 	struct sparx5_port *port = netdev_priv(ndev);
253 	struct sparx5 *sparx5 = port->sparx5;
254 	u32 *keystr, *mskstr, *actstr;
255 	int idx;
256 
257 	keystr = &admin->cache.keystream[start];
258 	mskstr = &admin->cache.maskstream[start];
259 	actstr = &admin->cache.actionstream[start];
260 	switch (sel) {
261 	case VCAP_SEL_ENTRY:
262 		for (idx = 0; idx < count; ++idx) {
263 			/* Avoid 'match-off' by setting value & mask */
264 			spx5_wr(keystr[idx] & mskstr[idx], sparx5,
265 				VCAP_SUPER_VCAP_ENTRY_DAT(idx));
266 			spx5_wr(~mskstr[idx], sparx5,
267 				VCAP_SUPER_VCAP_MASK_DAT(idx));
268 		}
269 		break;
270 	case VCAP_SEL_ACTION:
271 		for (idx = 0; idx < count; ++idx)
272 			spx5_wr(actstr[idx], sparx5,
273 				VCAP_SUPER_VCAP_ACTION_DAT(idx));
274 		break;
275 	case VCAP_SEL_ALL:
276 		pr_err("%s:%d: cannot write all streams at once\n",
277 		       __func__, __LINE__);
278 		break;
279 	default:
280 		break;
281 	}
282 }
283 
284 /* API callback used for reading from the VCAP into the VCAP cache */
285 static void sparx5_vcap_cache_read(struct net_device *ndev,
286 				   struct vcap_admin *admin,
287 				   enum vcap_selection sel, u32 start,
288 				   u32 count)
289 {
290 	/* this will be added later */
291 }
292 
293 /* API callback used for initializing a VCAP address range */
294 static void sparx5_vcap_range_init(struct net_device *ndev,
295 				   struct vcap_admin *admin, u32 addr,
296 				   u32 count)
297 {
298 	struct sparx5_port *port = netdev_priv(ndev);
299 	struct sparx5 *sparx5 = port->sparx5;
300 
301 	_sparx5_vcap_range_init(sparx5, admin, addr, count);
302 }
303 
304 /* API callback used for updating the VCAP cache */
305 static void sparx5_vcap_update(struct net_device *ndev,
306 			       struct vcap_admin *admin, enum vcap_command cmd,
307 			       enum vcap_selection sel, u32 addr)
308 {
309 	struct sparx5_port *port = netdev_priv(ndev);
310 	struct sparx5 *sparx5 = port->sparx5;
311 	bool clear;
312 
313 	clear = (cmd == VCAP_CMD_INITIALIZE);
314 	spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) |
315 		VCAP_SUPER_CFG_MV_SIZE_SET(0), sparx5, VCAP_SUPER_CFG);
316 	spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) |
317 		VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) |
318 		VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) |
319 		VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) |
320 		VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
321 		VCAP_SUPER_CTRL_CLEAR_CACHE_SET(clear) |
322 		VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
323 		sparx5, VCAP_SUPER_CTRL);
324 	sparx5_vcap_wait_super_update(sparx5);
325 }
326 
327 /* API callback used for moving a block of rules in the VCAP */
328 static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin,
329 			     u32 addr, int offset, int count)
330 {
331 	/* this will be added later */
332 }
333 
334 /* Provide port information via a callback interface */
335 static int sparx5_port_info(struct net_device *ndev, enum vcap_type vtype,
336 			    int (*pf)(void *out, int arg, const char *fmt, ...),
337 			    void *out, int arg)
338 {
339 	/* this will be added later */
340 	return 0;
341 }
342 
343 /* API callback operations: only IS2 is supported for now */
344 static struct vcap_operations sparx5_vcap_ops = {
345 	.validate_keyset = sparx5_vcap_validate_keyset,
346 	.add_default_fields = sparx5_vcap_add_default_fields,
347 	.cache_erase = sparx5_vcap_cache_erase,
348 	.cache_write = sparx5_vcap_cache_write,
349 	.cache_read = sparx5_vcap_cache_read,
350 	.init = sparx5_vcap_range_init,
351 	.update = sparx5_vcap_update,
352 	.move = sparx5_vcap_move,
353 	.port_info = sparx5_port_info,
354 };
355 
356 /* Enable lookups per port and set the keyset generation: only IS2 for now */
357 static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5,
358 					   struct vcap_admin *admin)
359 {
360 	int portno, lookup;
361 	u32 keysel;
362 
363 	/* enable all 4 lookups on all ports */
364 	for (portno = 0; portno < SPX5_PORTS; ++portno)
365 		spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5,
366 			ANA_ACL_VCAP_S2_CFG(portno));
367 
368 	/* all traffic types generate the MAC_ETYPE keyset for now in all
369 	 * lookups on all ports
370 	 */
371 	keysel = ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(true) |
372 		ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(VCAP_IS2_PS_NONETH_MAC_ETYPE) |
373 		ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_MC_MAC_ETYPE) |
374 		ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_UC_MAC_ETYPE) |
375 		ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_MC_MAC_ETYPE) |
376 		ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_UC_MAC_ETYPE) |
377 		ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(VCAP_IS2_PS_ARP_MAC_ETYPE);
378 	for (lookup = 0; lookup < admin->lookups; ++lookup) {
379 		for (portno = 0; portno < SPX5_PORTS; ++portno) {
380 			spx5_wr(keysel, sparx5,
381 				ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup));
382 		}
383 	}
384 }
385 
386 /* Disable lookups per port and set the keyset generation: only IS2 for now */
387 static void sparx5_vcap_port_key_deselection(struct sparx5 *sparx5,
388 					     struct vcap_admin *admin)
389 {
390 	int portno;
391 
392 	for (portno = 0; portno < SPX5_PORTS; ++portno)
393 		spx5_rmw(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0),
394 			 ANA_ACL_VCAP_S2_CFG_SEC_ENA,
395 			 sparx5,
396 			 ANA_ACL_VCAP_S2_CFG(portno));
397 }
398 
399 static void sparx5_vcap_admin_free(struct vcap_admin *admin)
400 {
401 	if (!admin)
402 		return;
403 	kfree(admin->cache.keystream);
404 	kfree(admin->cache.maskstream);
405 	kfree(admin->cache.actionstream);
406 	kfree(admin);
407 }
408 
409 /* Allocate a vcap instance with a rule list and a cache area */
410 static struct vcap_admin *
411 sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
412 			const struct sparx5_vcap_inst *cfg)
413 {
414 	struct vcap_admin *admin;
415 
416 	admin = kzalloc(sizeof(*admin), GFP_KERNEL);
417 	if (!admin)
418 		return ERR_PTR(-ENOMEM);
419 	INIT_LIST_HEAD(&admin->list);
420 	INIT_LIST_HEAD(&admin->rules);
421 	admin->vtype = cfg->vtype;
422 	admin->vinst = cfg->vinst;
423 	admin->lookups = cfg->lookups;
424 	admin->lookups_per_instance = cfg->lookups_per_instance;
425 	admin->first_cid = cfg->first_cid;
426 	admin->last_cid = cfg->last_cid;
427 	admin->cache.keystream =
428 		kzalloc(STREAMSIZE, GFP_KERNEL);
429 	admin->cache.maskstream =
430 		kzalloc(STREAMSIZE, GFP_KERNEL);
431 	admin->cache.actionstream =
432 		kzalloc(STREAMSIZE, GFP_KERNEL);
433 	if (!admin->cache.keystream || !admin->cache.maskstream ||
434 	    !admin->cache.actionstream) {
435 		sparx5_vcap_admin_free(admin);
436 		return ERR_PTR(-ENOMEM);
437 	}
438 	return admin;
439 }
440 
441 /* Do block allocations and provide addresses for VCAP instances */
442 static void sparx5_vcap_block_alloc(struct sparx5 *sparx5,
443 				    struct vcap_admin *admin,
444 				    const struct sparx5_vcap_inst *cfg)
445 {
446 	int idx;
447 
448 	/* Super VCAP block mapping and address configuration. Block 0
449 	 * is assigned addresses 0 through 3071, block 1 is assigned
450 	 * addresses 3072 though 6143, and so on.
451 	 */
452 	for (idx = cfg->blockno; idx < cfg->blockno + cfg->blocks; ++idx) {
453 		spx5_wr(VCAP_SUPER_IDX_CORE_IDX_SET(idx), sparx5,
454 			VCAP_SUPER_IDX);
455 		spx5_wr(VCAP_SUPER_MAP_CORE_MAP_SET(cfg->map_id), sparx5,
456 			VCAP_SUPER_MAP);
457 	}
458 	admin->first_valid_addr = cfg->blockno * SUPER_VCAP_BLK_SIZE;
459 	admin->last_used_addr = admin->first_valid_addr +
460 		cfg->blocks * SUPER_VCAP_BLK_SIZE;
461 	admin->last_valid_addr = admin->last_used_addr - 1;
462 }
463 
464 /* Allocate a vcap control and vcap instances and configure the system */
465 int sparx5_vcap_init(struct sparx5 *sparx5)
466 {
467 	const struct sparx5_vcap_inst *cfg;
468 	struct vcap_control *ctrl;
469 	struct vcap_admin *admin;
470 	int err = 0, idx;
471 
472 	/* Create a VCAP control instance that owns the platform specific VCAP
473 	 * model with VCAP instances and information about keysets, keys,
474 	 * actionsets and actions
475 	 * - Create administrative state for each available VCAP
476 	 *   - Lists of rules
477 	 *   - Address information
478 	 *   - Initialize VCAP blocks
479 	 *   - Configure port keysets
480 	 */
481 	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
482 	if (!ctrl)
483 		return -ENOMEM;
484 
485 	sparx5->vcap_ctrl = ctrl;
486 	/* select the sparx5 VCAP model */
487 	ctrl->vcaps = sparx5_vcaps;
488 	ctrl->stats = &sparx5_vcap_stats;
489 	/* Setup callbacks to allow the API to use the VCAP HW */
490 	ctrl->ops = &sparx5_vcap_ops;
491 
492 	INIT_LIST_HEAD(&ctrl->list);
493 	for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) {
494 		cfg = &sparx5_vcap_inst_cfg[idx];
495 		admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg);
496 		if (IS_ERR(admin)) {
497 			err = PTR_ERR(admin);
498 			pr_err("%s:%d: vcap allocation failed: %d\n",
499 			       __func__, __LINE__, err);
500 			return err;
501 		}
502 		sparx5_vcap_block_alloc(sparx5, admin, cfg);
503 		sparx5_vcap_block_init(sparx5, admin);
504 		if (cfg->vinst == 0)
505 			sparx5_vcap_port_key_selection(sparx5, admin);
506 		list_add_tail(&admin->list, &ctrl->list);
507 	}
508 
509 	return err;
510 }
511 
512 void sparx5_vcap_destroy(struct sparx5 *sparx5)
513 {
514 	struct vcap_control *ctrl = sparx5->vcap_ctrl;
515 	struct vcap_admin *admin, *admin_next;
516 
517 	if (!ctrl)
518 		return;
519 
520 	list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) {
521 		sparx5_vcap_port_key_deselection(sparx5, admin);
522 		vcap_del_rules(ctrl, admin);
523 		list_del(&admin->list);
524 		sparx5_vcap_admin_free(admin);
525 	}
526 	kfree(ctrl);
527 }
528