Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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 "vcap_api_debugfs.h"
 11#include "sparx5_main_regs.h"
 12#include "sparx5_main.h"
 13#include "sparx5_vcap_impl.h"
 14#include "sparx5_vcap_ag_api.h"
 15#include "sparx5_vcap_debugfs.h"
 16
 17#define SUPER_VCAP_BLK_SIZE 3072 /* addresses per Super VCAP block */
 18#define STREAMSIZE (64 * 4)  /* bytes in the VCAP cache area */
 19
 20#define SPARX5_IS2_LOOKUPS 4
 21#define VCAP_IS2_KEYSEL(_ena, _noneth, _v4_mc, _v4_uc, _v6_mc, _v6_uc, _arp) \
 22	(ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(_ena) | \
 23	 ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(_noneth) | \
 24	 ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(_v4_mc) | \
 25	 ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(_v4_uc) | \
 26	 ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(_v6_mc) | \
 27	 ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(_v6_uc) | \
 28	 ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(_arp))
 29
 30static struct sparx5_vcap_inst {
 31	enum vcap_type vtype; /* type of vcap */
 32	int vinst; /* instance number within the same type */
 33	int lookups; /* number of lookups in this vcap type */
 34	int lookups_per_instance; /* number of lookups in this instance */
 35	int first_cid; /* first chain id in this vcap */
 36	int last_cid; /* last chain id in this vcap */
 37	int count; /* number of available addresses, not in super vcap */
 38	int map_id; /* id in the super vcap block mapping (if applicable) */
 39	int blockno; /* starting block in super vcap (if applicable) */
 40	int blocks; /* number of blocks in super vcap (if applicable) */
 41} sparx5_vcap_inst_cfg[] = {
 42	{
 43		.vtype = VCAP_TYPE_IS2, /* IS2-0 */
 44		.vinst = 0,
 45		.map_id = 4,
 46		.lookups = SPARX5_IS2_LOOKUPS,
 47		.lookups_per_instance = SPARX5_IS2_LOOKUPS / 2,
 48		.first_cid = SPARX5_VCAP_CID_IS2_L0,
 49		.last_cid = SPARX5_VCAP_CID_IS2_L2 - 1,
 50		.blockno = 0, /* Maps block 0-1 */
 51		.blocks = 2,
 52	},
 53	{
 54		.vtype = VCAP_TYPE_IS2, /* IS2-1 */
 55		.vinst = 1,
 56		.map_id = 5,
 57		.lookups = SPARX5_IS2_LOOKUPS,
 58		.lookups_per_instance = SPARX5_IS2_LOOKUPS / 2,
 59		.first_cid = SPARX5_VCAP_CID_IS2_L2,
 60		.last_cid = SPARX5_VCAP_CID_IS2_MAX,
 61		.blockno = 2, /* Maps block 2-3 */
 62		.blocks = 2,
 63	},
 64};
 65
 66/* Await the super VCAP completion of the current operation */
 67static void sparx5_vcap_wait_super_update(struct sparx5 *sparx5)
 68{
 69	u32 value;
 70
 71	read_poll_timeout(spx5_rd, value,
 72			  !VCAP_SUPER_CTRL_UPDATE_SHOT_GET(value), 500, 10000,
 73			  false, sparx5, VCAP_SUPER_CTRL);
 74}
 75
 76/* Initializing a VCAP address range: only IS2 for now */
 77static void _sparx5_vcap_range_init(struct sparx5 *sparx5,
 78				    struct vcap_admin *admin,
 79				    u32 addr, u32 count)
 80{
 81	u32 size = count - 1;
 82
 83	spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) |
 84		VCAP_SUPER_CFG_MV_SIZE_SET(size),
 85		sparx5, VCAP_SUPER_CFG);
 86	spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) |
 87		VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) |
 88		VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) |
 89		VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) |
 90		VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
 91		VCAP_SUPER_CTRL_CLEAR_CACHE_SET(true) |
 92		VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
 93		sparx5, VCAP_SUPER_CTRL);
 94	sparx5_vcap_wait_super_update(sparx5);
 95}
 96
 97/* Initializing VCAP rule data area */
 98static void sparx5_vcap_block_init(struct sparx5 *sparx5,
 99				   struct vcap_admin *admin)
100{
101	_sparx5_vcap_range_init(sparx5, admin, admin->first_valid_addr,
102				admin->last_valid_addr -
103					admin->first_valid_addr);
104}
105
106/* Get the keyset name from the sparx5 VCAP model */
107static const char *sparx5_vcap_keyset_name(struct net_device *ndev,
108					   enum vcap_keyfield_set keyset)
109{
110	struct sparx5_port *port = netdev_priv(ndev);
111
112	return vcap_keyset_name(port->sparx5->vcap_ctrl, keyset);
113}
114
115/* Check if this is the first lookup of IS2 */
116static bool sparx5_vcap_is2_is_first_chain(struct vcap_rule *rule)
117{
118	return (rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L0 &&
119		rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L1) ||
120		((rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L2 &&
121		  rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L3));
122}
123
124/* Set the narrow range ingress port mask on a rule */
125static void sparx5_vcap_add_range_port_mask(struct vcap_rule *rule,
126					    struct net_device *ndev)
127{
128	struct sparx5_port *port = netdev_priv(ndev);
129	u32 port_mask;
130	u32 range;
131
132	range = port->portno / BITS_PER_TYPE(u32);
133	/* Port bit set to match-any */
134	port_mask = ~BIT(port->portno % BITS_PER_TYPE(u32));
135	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_SEL, 0, 0xf);
136	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, range, 0xf);
137	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, port_mask);
138}
139
140/* Set the wide range ingress port mask on a rule */
141static void sparx5_vcap_add_wide_port_mask(struct vcap_rule *rule,
142					   struct net_device *ndev)
143{
144	struct sparx5_port *port = netdev_priv(ndev);
145	struct vcap_u72_key port_mask;
146	u32 range;
147
148	/* Port bit set to match-any */
149	memset(port_mask.value, 0, sizeof(port_mask.value));
150	memset(port_mask.mask, 0xff, sizeof(port_mask.mask));
151	range = port->portno / BITS_PER_BYTE;
152	port_mask.mask[range] = ~BIT(port->portno % BITS_PER_BYTE);
153	vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask);
154}
155
156/* Convert chain id to vcap lookup id */
157static int sparx5_vcap_cid_to_lookup(int cid)
158{
159	int lookup = 0;
160
161	/* For now only handle IS2 */
162	if (cid >= SPARX5_VCAP_CID_IS2_L1 && cid < SPARX5_VCAP_CID_IS2_L2)
163		lookup = 1;
164	else if (cid >= SPARX5_VCAP_CID_IS2_L2 && cid < SPARX5_VCAP_CID_IS2_L3)
165		lookup = 2;
166	else if (cid >= SPARX5_VCAP_CID_IS2_L3 && cid < SPARX5_VCAP_CID_IS2_MAX)
167		lookup = 3;
168
169	return lookup;
170}
171
172/* Return the list of keysets for the vcap port configuration */
173static int sparx5_vcap_is2_get_port_keysets(struct net_device *ndev,
174					    int lookup,
175					    struct vcap_keyset_list *keysetlist,
176					    u16 l3_proto)
177{
178	struct sparx5_port *port = netdev_priv(ndev);
179	struct sparx5 *sparx5 = port->sparx5;
180	int portno = port->portno;
181	u32 value;
182
183	/* Check if the port keyset selection is enabled */
184	value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup));
185	if (!ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(value))
186		return -ENOENT;
187
188	/* Collect all keysets for the port in a list */
189	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) {
190		switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) {
191		case VCAP_IS2_PS_ARP_MAC_ETYPE:
192			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
193			break;
194		case VCAP_IS2_PS_ARP_ARP:
195			vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP);
196			break;
197		}
198	}
199
200	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) {
201		switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) {
202		case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE:
203			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
204			break;
205		case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER:
206			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
207			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
208			break;
209		case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE:
210			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
211			break;
212		}
213
214		switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) {
215		case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE:
216			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
217			break;
218		case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER:
219			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
220			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
221			break;
222		case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE:
223			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
224			break;
225		}
226	}
227
228	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) {
229		switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) {
230		case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE:
231			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
232			break;
233		case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE:
234			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
235			break;
236		case VCAP_IS2_PS_IPV6_UC_IP6_STD:
237			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
238			break;
239		case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER:
240			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
241			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
242			break;
243		}
244
245		switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) {
246		case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE:
247			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
248			break;
249		case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE:
250			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
251			break;
252		case VCAP_IS2_PS_IPV6_MC_IP6_STD:
253			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
254			break;
255		case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER:
256			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
257			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
258			break;
259		case VCAP_IS2_PS_IPV6_MC_IP6_VID:
260			/* Not used */
261			break;
262		}
263	}
264
265	if (l3_proto != ETH_P_ARP && l3_proto != ETH_P_IP &&
266	    l3_proto != ETH_P_IPV6) {
267		switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) {
268		case VCAP_IS2_PS_NONETH_MAC_ETYPE:
269			/* IS2 non-classified frames generate MAC_ETYPE */
270			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
271			break;
272		}
273	}
274	return 0;
275}
276
277/* Get the port keyset for the vcap lookup */
278int sparx5_vcap_get_port_keyset(struct net_device *ndev,
279				struct vcap_admin *admin,
280				int cid,
281				u16 l3_proto,
282				struct vcap_keyset_list *kslist)
283{
284	int lookup;
285
286	lookup = sparx5_vcap_cid_to_lookup(cid);
287	return sparx5_vcap_is2_get_port_keysets(ndev, lookup, kslist, l3_proto);
288}
289
290/* API callback used for validating a field keyset (check the port keysets) */
291static enum vcap_keyfield_set
292sparx5_vcap_validate_keyset(struct net_device *ndev,
293			    struct vcap_admin *admin,
294			    struct vcap_rule *rule,
295			    struct vcap_keyset_list *kslist,
296			    u16 l3_proto)
297{
298	struct vcap_keyset_list keysetlist = {};
299	enum vcap_keyfield_set keysets[10] = {};
300	int idx, jdx, lookup;
301
302	if (!kslist || kslist->cnt == 0)
303		return VCAP_KFS_NO_VALUE;
304
305	/* Get a list of currently configured keysets in the lookups */
306	lookup = sparx5_vcap_cid_to_lookup(rule->vcap_chain_id);
307	keysetlist.max = ARRAY_SIZE(keysets);
308	keysetlist.keysets = keysets;
309	sparx5_vcap_is2_get_port_keysets(ndev, lookup, &keysetlist, l3_proto);
310
311	/* Check if there is a match and return the match */
312	for (idx = 0; idx < kslist->cnt; ++idx)
313		for (jdx = 0; jdx < keysetlist.cnt; ++jdx)
314			if (kslist->keysets[idx] == keysets[jdx])
315				return kslist->keysets[idx];
316
317	pr_err("%s:%d: %s not supported in port key selection\n",
318	       __func__, __LINE__,
319	       sparx5_vcap_keyset_name(ndev, kslist->keysets[0]));
320
321	return -ENOENT;
322}
323
324/* API callback used for adding default fields to a rule */
325static void sparx5_vcap_add_default_fields(struct net_device *ndev,
326					   struct vcap_admin *admin,
327					   struct vcap_rule *rule)
328{
329	const struct vcap_field *field;
330
331	field = vcap_lookup_keyfield(rule, VCAP_KF_IF_IGR_PORT_MASK);
332	if (field && field->width == SPX5_PORTS)
333		sparx5_vcap_add_wide_port_mask(rule, ndev);
334	else if (field && field->width == BITS_PER_TYPE(u32))
335		sparx5_vcap_add_range_port_mask(rule, ndev);
336	else
337		pr_err("%s:%d: %s: could not add an ingress port mask for: %s\n",
338		       __func__, __LINE__, netdev_name(ndev),
339		       sparx5_vcap_keyset_name(ndev, rule->keyset));
340	/* add the lookup bit */
341	if (sparx5_vcap_is2_is_first_chain(rule))
342		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1);
343	else
344		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0);
345}
346
347/* API callback used for erasing the vcap cache area (not the register area) */
348static void sparx5_vcap_cache_erase(struct vcap_admin *admin)
349{
350	memset(admin->cache.keystream, 0, STREAMSIZE);
351	memset(admin->cache.maskstream, 0, STREAMSIZE);
352	memset(admin->cache.actionstream, 0, STREAMSIZE);
353	memset(&admin->cache.counter, 0, sizeof(admin->cache.counter));
354}
355
356/* API callback used for writing to the VCAP cache */
357static void sparx5_vcap_cache_write(struct net_device *ndev,
358				    struct vcap_admin *admin,
359				    enum vcap_selection sel,
360				    u32 start,
361				    u32 count)
362{
363	struct sparx5_port *port = netdev_priv(ndev);
364	struct sparx5 *sparx5 = port->sparx5;
365	u32 *keystr, *mskstr, *actstr;
366	int idx;
367
368	keystr = &admin->cache.keystream[start];
369	mskstr = &admin->cache.maskstream[start];
370	actstr = &admin->cache.actionstream[start];
371	switch (sel) {
372	case VCAP_SEL_ENTRY:
373		for (idx = 0; idx < count; ++idx) {
374			/* Avoid 'match-off' by setting value & mask */
375			spx5_wr(keystr[idx] & mskstr[idx], sparx5,
376				VCAP_SUPER_VCAP_ENTRY_DAT(idx));
377			spx5_wr(~mskstr[idx], sparx5,
378				VCAP_SUPER_VCAP_MASK_DAT(idx));
379		}
380		break;
381	case VCAP_SEL_ACTION:
382		for (idx = 0; idx < count; ++idx)
383			spx5_wr(actstr[idx], sparx5,
384				VCAP_SUPER_VCAP_ACTION_DAT(idx));
385		break;
386	case VCAP_SEL_ALL:
387		pr_err("%s:%d: cannot write all streams at once\n",
388		       __func__, __LINE__);
389		break;
390	default:
391		break;
392	}
393	if (sel & VCAP_SEL_COUNTER) {
394		start = start & 0xfff; /* counter limit */
395		if (admin->vinst == 0)
396			spx5_wr(admin->cache.counter, sparx5,
397				ANA_ACL_CNT_A(start));
398		else
399			spx5_wr(admin->cache.counter, sparx5,
400				ANA_ACL_CNT_B(start));
401		spx5_wr(admin->cache.sticky, sparx5,
402			VCAP_SUPER_VCAP_CNT_DAT(0));
403	}
404}
405
406/* API callback used for reading from the VCAP into the VCAP cache */
407static void sparx5_vcap_cache_read(struct net_device *ndev,
408				   struct vcap_admin *admin,
409				   enum vcap_selection sel,
410				   u32 start,
411				   u32 count)
412{
413	struct sparx5_port *port = netdev_priv(ndev);
414	struct sparx5 *sparx5 = port->sparx5;
415	u32 *keystr, *mskstr, *actstr;
416	int idx;
417
418	keystr = &admin->cache.keystream[start];
419	mskstr = &admin->cache.maskstream[start];
420	actstr = &admin->cache.actionstream[start];
421	if (sel & VCAP_SEL_ENTRY) {
422		for (idx = 0; idx < count; ++idx) {
423			keystr[idx] = spx5_rd(sparx5,
424					      VCAP_SUPER_VCAP_ENTRY_DAT(idx));
425			mskstr[idx] = ~spx5_rd(sparx5,
426					       VCAP_SUPER_VCAP_MASK_DAT(idx));
427		}
428	}
429	if (sel & VCAP_SEL_ACTION) {
430		for (idx = 0; idx < count; ++idx)
431			actstr[idx] = spx5_rd(sparx5,
432					      VCAP_SUPER_VCAP_ACTION_DAT(idx));
433	}
434	if (sel & VCAP_SEL_COUNTER) {
435		start = start & 0xfff; /* counter limit */
436		if (admin->vinst == 0)
437			admin->cache.counter =
438				spx5_rd(sparx5, ANA_ACL_CNT_A(start));
439		else
440			admin->cache.counter =
441				spx5_rd(sparx5, ANA_ACL_CNT_B(start));
442		admin->cache.sticky =
443			spx5_rd(sparx5, VCAP_SUPER_VCAP_CNT_DAT(0));
444	}
445}
446
447/* API callback used for initializing a VCAP address range */
448static void sparx5_vcap_range_init(struct net_device *ndev,
449				   struct vcap_admin *admin, u32 addr,
450				   u32 count)
451{
452	struct sparx5_port *port = netdev_priv(ndev);
453	struct sparx5 *sparx5 = port->sparx5;
454
455	_sparx5_vcap_range_init(sparx5, admin, addr, count);
456}
457
458/* API callback used for updating the VCAP cache */
459static void sparx5_vcap_update(struct net_device *ndev,
460			       struct vcap_admin *admin, enum vcap_command cmd,
461			       enum vcap_selection sel, u32 addr)
462{
463	struct sparx5_port *port = netdev_priv(ndev);
464	struct sparx5 *sparx5 = port->sparx5;
465	bool clear;
466
467	clear = (cmd == VCAP_CMD_INITIALIZE);
468	spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) |
469		VCAP_SUPER_CFG_MV_SIZE_SET(0), sparx5, VCAP_SUPER_CFG);
470	spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) |
471		VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) |
472		VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) |
473		VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) |
474		VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
475		VCAP_SUPER_CTRL_CLEAR_CACHE_SET(clear) |
476		VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
477		sparx5, VCAP_SUPER_CTRL);
478	sparx5_vcap_wait_super_update(sparx5);
479}
480
481/* API callback used for moving a block of rules in the VCAP */
482static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin,
483			     u32 addr, int offset, int count)
484{
485	struct sparx5_port *port = netdev_priv(ndev);
486	struct sparx5 *sparx5 = port->sparx5;
487	enum vcap_command cmd;
488	u16 mv_num_pos;
489	u16 mv_size;
490
491	mv_size = count - 1;
492	if (offset > 0) {
493		mv_num_pos = offset - 1;
494		cmd = VCAP_CMD_MOVE_DOWN;
495	} else {
496		mv_num_pos = -offset - 1;
497		cmd = VCAP_CMD_MOVE_UP;
498	}
499	spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(mv_num_pos) |
500		VCAP_SUPER_CFG_MV_SIZE_SET(mv_size),
501		sparx5, VCAP_SUPER_CFG);
502	spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) |
503		VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) |
504		VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) |
505		VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) |
506		VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
507		VCAP_SUPER_CTRL_CLEAR_CACHE_SET(false) |
508		VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
509		sparx5, VCAP_SUPER_CTRL);
510	sparx5_vcap_wait_super_update(sparx5);
511}
512
513/* Enable all lookups in the VCAP instance */
514static int sparx5_vcap_enable(struct net_device *ndev,
515			      struct vcap_admin *admin,
516			      bool enable)
517{
518	struct sparx5_port *port = netdev_priv(ndev);
519	struct sparx5 *sparx5;
520	int portno;
521
522	sparx5 = port->sparx5;
523	portno = port->portno;
524
525	/* For now we only consider IS2 */
526	if (enable)
527		spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5,
528			ANA_ACL_VCAP_S2_CFG(portno));
529	else
530		spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), sparx5,
531			ANA_ACL_VCAP_S2_CFG(portno));
532	return 0;
533}
534
535/* API callback operations: only IS2 is supported for now */
536static struct vcap_operations sparx5_vcap_ops = {
537	.validate_keyset = sparx5_vcap_validate_keyset,
538	.add_default_fields = sparx5_vcap_add_default_fields,
539	.cache_erase = sparx5_vcap_cache_erase,
540	.cache_write = sparx5_vcap_cache_write,
541	.cache_read = sparx5_vcap_cache_read,
542	.init = sparx5_vcap_range_init,
543	.update = sparx5_vcap_update,
544	.move = sparx5_vcap_move,
545	.port_info = sparx5_port_info,
546	.enable = sparx5_vcap_enable,
547};
548
549/* Enable lookups per port and set the keyset generation: only IS2 for now */
550static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5,
551					   struct vcap_admin *admin)
552{
553	int portno, lookup;
554	u32 keysel;
555
556	/* all traffic types generate the MAC_ETYPE keyset for now in all
557	 * lookups on all ports
558	 */
559	keysel = VCAP_IS2_KEYSEL(true, VCAP_IS2_PS_NONETH_MAC_ETYPE,
560				 VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER,
561				 VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER,
562				 VCAP_IS2_PS_IPV6_MC_IP_7TUPLE,
563				 VCAP_IS2_PS_IPV6_UC_IP_7TUPLE,
564				 VCAP_IS2_PS_ARP_ARP);
565	for (lookup = 0; lookup < admin->lookups; ++lookup) {
566		for (portno = 0; portno < SPX5_PORTS; ++portno) {
567			spx5_wr(keysel, sparx5,
568				ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup));
569		}
570	}
571}
572
573/* Disable lookups per port and set the keyset generation: only IS2 for now */
574static void sparx5_vcap_port_key_deselection(struct sparx5 *sparx5,
575					     struct vcap_admin *admin)
576{
577	int portno;
578
579	for (portno = 0; portno < SPX5_PORTS; ++portno)
580		spx5_rmw(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0),
581			 ANA_ACL_VCAP_S2_CFG_SEC_ENA,
582			 sparx5,
583			 ANA_ACL_VCAP_S2_CFG(portno));
584}
585
586static void sparx5_vcap_admin_free(struct vcap_admin *admin)
587{
588	if (!admin)
589		return;
590	mutex_destroy(&admin->lock);
591	kfree(admin->cache.keystream);
592	kfree(admin->cache.maskstream);
593	kfree(admin->cache.actionstream);
594	kfree(admin);
595}
596
597/* Allocate a vcap instance with a rule list and a cache area */
598static struct vcap_admin *
599sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
600			const struct sparx5_vcap_inst *cfg)
601{
602	struct vcap_admin *admin;
603
604	admin = kzalloc(sizeof(*admin), GFP_KERNEL);
605	if (!admin)
606		return ERR_PTR(-ENOMEM);
607	INIT_LIST_HEAD(&admin->list);
608	INIT_LIST_HEAD(&admin->rules);
609	INIT_LIST_HEAD(&admin->enabled);
610	mutex_init(&admin->lock);
611	admin->vtype = cfg->vtype;
612	admin->vinst = cfg->vinst;
613	admin->lookups = cfg->lookups;
614	admin->lookups_per_instance = cfg->lookups_per_instance;
615	admin->first_cid = cfg->first_cid;
616	admin->last_cid = cfg->last_cid;
617	admin->cache.keystream =
618		kzalloc(STREAMSIZE, GFP_KERNEL);
619	admin->cache.maskstream =
620		kzalloc(STREAMSIZE, GFP_KERNEL);
621	admin->cache.actionstream =
622		kzalloc(STREAMSIZE, GFP_KERNEL);
623	if (!admin->cache.keystream || !admin->cache.maskstream ||
624	    !admin->cache.actionstream) {
625		sparx5_vcap_admin_free(admin);
626		return ERR_PTR(-ENOMEM);
627	}
628	return admin;
629}
630
631/* Do block allocations and provide addresses for VCAP instances */
632static void sparx5_vcap_block_alloc(struct sparx5 *sparx5,
633				    struct vcap_admin *admin,
634				    const struct sparx5_vcap_inst *cfg)
635{
636	int idx;
637
638	/* Super VCAP block mapping and address configuration. Block 0
639	 * is assigned addresses 0 through 3071, block 1 is assigned
640	 * addresses 3072 though 6143, and so on.
641	 */
642	for (idx = cfg->blockno; idx < cfg->blockno + cfg->blocks; ++idx) {
643		spx5_wr(VCAP_SUPER_IDX_CORE_IDX_SET(idx), sparx5,
644			VCAP_SUPER_IDX);
645		spx5_wr(VCAP_SUPER_MAP_CORE_MAP_SET(cfg->map_id), sparx5,
646			VCAP_SUPER_MAP);
647	}
648	admin->first_valid_addr = cfg->blockno * SUPER_VCAP_BLK_SIZE;
649	admin->last_used_addr = admin->first_valid_addr +
650		cfg->blocks * SUPER_VCAP_BLK_SIZE;
651	admin->last_valid_addr = admin->last_used_addr - 1;
652}
653
654/* Allocate a vcap control and vcap instances and configure the system */
655int sparx5_vcap_init(struct sparx5 *sparx5)
656{
657	const struct sparx5_vcap_inst *cfg;
658	struct vcap_control *ctrl;
659	struct vcap_admin *admin;
660	struct dentry *dir;
661	int err = 0, idx;
662
663	/* Create a VCAP control instance that owns the platform specific VCAP
664	 * model with VCAP instances and information about keysets, keys,
665	 * actionsets and actions
666	 * - Create administrative state for each available VCAP
667	 *   - Lists of rules
668	 *   - Address information
669	 *   - Initialize VCAP blocks
670	 *   - Configure port keysets
671	 */
672	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
673	if (!ctrl)
674		return -ENOMEM;
675
676	sparx5->vcap_ctrl = ctrl;
677	/* select the sparx5 VCAP model */
678	ctrl->vcaps = sparx5_vcaps;
679	ctrl->stats = &sparx5_vcap_stats;
680	/* Setup callbacks to allow the API to use the VCAP HW */
681	ctrl->ops = &sparx5_vcap_ops;
682
683	INIT_LIST_HEAD(&ctrl->list);
684	for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) {
685		cfg = &sparx5_vcap_inst_cfg[idx];
686		admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg);
687		if (IS_ERR(admin)) {
688			err = PTR_ERR(admin);
689			pr_err("%s:%d: vcap allocation failed: %d\n",
690			       __func__, __LINE__, err);
691			return err;
692		}
693		sparx5_vcap_block_alloc(sparx5, admin, cfg);
694		sparx5_vcap_block_init(sparx5, admin);
695		if (cfg->vinst == 0)
696			sparx5_vcap_port_key_selection(sparx5, admin);
697		list_add_tail(&admin->list, &ctrl->list);
698	}
699	dir = vcap_debugfs(sparx5->dev, sparx5->debugfs_root, ctrl);
700	for (idx = 0; idx < SPX5_PORTS; ++idx)
701		if (sparx5->ports[idx])
702			vcap_port_debugfs(sparx5->dev, dir, ctrl,
703					  sparx5->ports[idx]->ndev);
704
705	return err;
706}
707
708void sparx5_vcap_destroy(struct sparx5 *sparx5)
709{
710	struct vcap_control *ctrl = sparx5->vcap_ctrl;
711	struct vcap_admin *admin, *admin_next;
712
713	if (!ctrl)
714		return;
715
716	list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) {
717		sparx5_vcap_port_key_deselection(sparx5, admin);
718		vcap_del_rules(ctrl, admin);
719		list_del(&admin->list);
720		sparx5_vcap_admin_free(admin);
721	}
722	kfree(ctrl);
723}