xref: /linux/drivers/net/dsa/microchip/ksz9477_acl.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
3 
4 /* Access Control List (ACL) structure:
5  *
6  * There are multiple groups of registers involved in ACL configuration:
7  *
8  * - Matching Rules: These registers define the criteria for matching incoming
9  *   packets based on their header information (Layer 2 MAC, Layer 3 IP, or
10  *   Layer 4 TCP/UDP). Different register settings are used depending on the
11  *   matching rule mode (MD) and the Enable (ENB) settings.
12  *
13  * - Action Rules: These registers define how the ACL should modify the packet's
14  *   priority, VLAN tag priority, and forwarding map once a matching rule has
15  *   been triggered. The settings vary depending on whether the matching rule is
16  *   in Count Mode (MD = 01 and ENB = 00) or not.
17  *
18  * - Processing Rules: These registers control the overall behavior of the ACL,
19  *   such as selecting which matching rule to apply first, enabling/disabling
20  *   specific rules, or specifying actions for matched packets.
21  *
22  * ACL Structure:
23  *                             +----------------------+
24  * +----------------------+    |    (optional)        |
25  * |    Matching Rules    |    |    Matching Rules    |
26  * |    (Layer 2, 3, 4)   |    |    (Layer 2, 3, 4)   |
27  * +----------------------+    +----------------------+
28  *             |                            |
29  *             \___________________________/
30  *                          v
31  *               +----------------------+
32  *               |   Processing Rules   |
33  *               | (action idx,         |
34  *               | matching rule set)   |
35  *               +----------------------+
36  *                          |
37  *                          v
38  *               +----------------------+
39  *               |    Action Rules      |
40  *               | (Modify Priority,    |
41  *               |  Forwarding Map,     |
42  *               |  VLAN tag, etc)      |
43  *               +----------------------+
44  */
45 
46 #include <linux/bitops.h>
47 
48 #include "ksz9477.h"
49 #include "ksz9477_reg.h"
50 #include "ksz_common.h"
51 
52 #define KSZ9477_PORT_ACL_0		0x600
53 
54 enum ksz9477_acl_port_access {
55 	KSZ9477_ACL_PORT_ACCESS_0  = 0x00,
56 	KSZ9477_ACL_PORT_ACCESS_1  = 0x01,
57 	KSZ9477_ACL_PORT_ACCESS_2  = 0x02,
58 	KSZ9477_ACL_PORT_ACCESS_3  = 0x03,
59 	KSZ9477_ACL_PORT_ACCESS_4  = 0x04,
60 	KSZ9477_ACL_PORT_ACCESS_5  = 0x05,
61 	KSZ9477_ACL_PORT_ACCESS_6  = 0x06,
62 	KSZ9477_ACL_PORT_ACCESS_7  = 0x07,
63 	KSZ9477_ACL_PORT_ACCESS_8  = 0x08,
64 	KSZ9477_ACL_PORT_ACCESS_9  = 0x09,
65 	KSZ9477_ACL_PORT_ACCESS_A  = 0x0A,
66 	KSZ9477_ACL_PORT_ACCESS_B  = 0x0B,
67 	KSZ9477_ACL_PORT_ACCESS_C  = 0x0C,
68 	KSZ9477_ACL_PORT_ACCESS_D  = 0x0D,
69 	KSZ9477_ACL_PORT_ACCESS_E  = 0x0E,
70 	KSZ9477_ACL_PORT_ACCESS_F  = 0x0F,
71 	KSZ9477_ACL_PORT_ACCESS_10 = 0x10,
72 	KSZ9477_ACL_PORT_ACCESS_11 = 0x11
73 };
74 
75 #define KSZ9477_ACL_MD_MASK			GENMASK(5, 4)
76 #define KSZ9477_ACL_MD_DISABLE			0
77 #define KSZ9477_ACL_MD_L2_MAC			1
78 #define KSZ9477_ACL_MD_L3_IP			2
79 #define KSZ9477_ACL_MD_L4_TCP_UDP		3
80 
81 #define KSZ9477_ACL_ENB_MASK			GENMASK(3, 2)
82 #define KSZ9477_ACL_ENB_L2_COUNTER		0
83 #define KSZ9477_ACL_ENB_L2_TYPE			1
84 #define KSZ9477_ACL_ENB_L2_MAC			2
85 #define KSZ9477_ACL_ENB_L2_MAC_TYPE		3
86 
87 /* only IPv4 src or dst can be used with mask */
88 #define KSZ9477_ACL_ENB_L3_IPV4_ADDR_MASK	1
89 /* only IPv4 src and dst can be used without mask */
90 #define KSZ9477_ACL_ENB_L3_IPV4_ADDR_SRC_DST	2
91 
92 #define KSZ9477_ACL_ENB_L4_IP_PROTO	        0
93 #define KSZ9477_ACL_ENB_L4_TCP_SRC_DST_PORT	1
94 #define KSZ9477_ACL_ENB_L4_UDP_SRC_DST_PORT	2
95 #define KSZ9477_ACL_ENB_L4_TCP_SEQ_NUMBER	3
96 
97 #define KSZ9477_ACL_SD_SRC			BIT(1)
98 #define KSZ9477_ACL_SD_DST			0
99 #define KSZ9477_ACL_EQ_EQUAL			BIT(0)
100 #define KSZ9477_ACL_EQ_NOT_EQUAL		0
101 
102 #define KSZ9477_ACL_PM_M			GENMASK(7, 6)
103 #define KSZ9477_ACL_PM_DISABLE			0
104 #define KSZ9477_ACL_PM_HIGHER			1
105 #define KSZ9477_ACL_PM_LOWER			2
106 #define KSZ9477_ACL_PM_REPLACE			3
107 #define KSZ9477_ACL_P_M				GENMASK(5, 3)
108 
109 #define KSZ9477_PORT_ACL_CTRL_0			0x0612
110 
111 #define KSZ9477_ACL_WRITE_DONE			BIT(6)
112 #define KSZ9477_ACL_READ_DONE			BIT(5)
113 #define KSZ9477_ACL_WRITE			BIT(4)
114 #define KSZ9477_ACL_INDEX_M			GENMASK(3, 0)
115 
116 /**
117  * ksz9477_dump_acl_index - Print the ACL entry at the specified index
118  *
119  * @dev: Pointer to the ksz9477 device structure.
120  * @acle: Pointer to the ACL entry array.
121  * @index: The index of the ACL entry to print.
122  *
123  * This function prints the details of an ACL entry, located at a particular
124  * index within the ksz9477 device's ACL table. It omits printing entries that
125  * are empty.
126  *
127  * Return: 1 if the entry is non-empty and printed, 0 otherwise.
128  */
ksz9477_dump_acl_index(struct ksz_device * dev,struct ksz9477_acl_entry * acle,int index)129 static int ksz9477_dump_acl_index(struct ksz_device *dev,
130 				  struct ksz9477_acl_entry *acle, int index)
131 {
132 	bool empty = true;
133 	char buf[64];
134 	u8 *entry;
135 	int i;
136 
137 	entry = &acle[index].entry[0];
138 	for (i = 0; i <= KSZ9477_ACL_PORT_ACCESS_11; i++) {
139 		if (entry[i])
140 			empty = false;
141 
142 		sprintf(buf + (i * 3), "%02x ", entry[i]);
143 	}
144 
145 	/* no need to print empty entries */
146 	if (empty)
147 		return 0;
148 
149 	dev_err(dev->dev, " Entry %02d, prio: %02d : %s", index,
150 		acle[index].prio, buf);
151 
152 	return 1;
153 }
154 
155 /**
156  * ksz9477_dump_acl - Print ACL entries
157  *
158  * @dev: Pointer to the device structure.
159  * @acle: Pointer to the ACL entry array.
160  */
ksz9477_dump_acl(struct ksz_device * dev,struct ksz9477_acl_entry * acle)161 static void ksz9477_dump_acl(struct ksz_device *dev,
162 			     struct ksz9477_acl_entry *acle)
163 {
164 	int count = 0;
165 	int i;
166 
167 	for (i = 0; i < KSZ9477_ACL_MAX_ENTRIES; i++)
168 		count += ksz9477_dump_acl_index(dev, acle, i);
169 
170 	if (count != KSZ9477_ACL_MAX_ENTRIES - 1)
171 		dev_err(dev->dev, " Empty ACL entries were skipped\n");
172 }
173 
174 /**
175  * ksz9477_acl_is_valid_matching_rule - Check if an ACL entry contains a valid
176  *					matching rule.
177  *
178  * @entry: Pointer to ACL entry buffer
179  *
180  * This function checks if the given ACL entry buffer contains a valid
181  * matching rule by inspecting the Mode (MD) and Enable (ENB) fields.
182  *
183  * Returns: True if it's a valid matching rule, false otherwise.
184  */
ksz9477_acl_is_valid_matching_rule(u8 * entry)185 static bool ksz9477_acl_is_valid_matching_rule(u8 *entry)
186 {
187 	u8 val1, md, enb;
188 
189 	val1 = entry[KSZ9477_ACL_PORT_ACCESS_1];
190 
191 	md = FIELD_GET(KSZ9477_ACL_MD_MASK, val1);
192 	if (md == KSZ9477_ACL_MD_DISABLE)
193 		return false;
194 
195 	if (md == KSZ9477_ACL_MD_L2_MAC) {
196 		/* L2 counter is not support, so it is not valid rule for now */
197 		enb = FIELD_GET(KSZ9477_ACL_ENB_MASK, val1);
198 		if (enb == KSZ9477_ACL_ENB_L2_COUNTER)
199 			return false;
200 	}
201 
202 	return true;
203 }
204 
205 /**
206  * ksz9477_acl_get_cont_entr - Get count of contiguous ACL entries and validate
207  *                             the matching rules.
208  * @dev: Pointer to the KSZ9477 device structure.
209  * @port: Port number.
210  * @index: Index of the starting ACL entry.
211  *
212  * Based on the KSZ9477 switch's Access Control List (ACL) system, the RuleSet
213  * in an ACL entry indicates which entries contain Matching rules linked to it.
214  * This RuleSet is represented by two registers: KSZ9477_ACL_PORT_ACCESS_E and
215  * KSZ9477_ACL_PORT_ACCESS_F. Each bit set in these registers corresponds to
216  * an entry containing a Matching rule for this RuleSet.
217  *
218  * For a single Matching rule linked, only one bit is set. However, when an
219  * entry links multiple Matching rules, forming what's termed a 'complex rule',
220  * multiple bits are set in these registers.
221  *
222  * This function checks that, for complex rules, the entries containing the
223  * linked Matching rules are contiguous in terms of their indices. It calculates
224  * and returns the number of these contiguous entries.
225  *
226  * Returns:
227  *    - 0 if the entry is empty and can be safely overwritten
228  *    - 1 if the entry represents a simple rule
229  *    - The number of contiguous entries if it is the root entry of a complex
230  *      rule
231  *    - -ENOTEMPTY if the entry is part of a complex rule but not the root
232  *      entry
233  *    - -EINVAL if the validation fails
234  */
ksz9477_acl_get_cont_entr(struct ksz_device * dev,int port,int index)235 static int ksz9477_acl_get_cont_entr(struct ksz_device *dev, int port,
236 				     int index)
237 {
238 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
239 	struct ksz9477_acl_entries *acles = &acl->acles;
240 	int start_idx, end_idx, contiguous_count;
241 	unsigned long val;
242 	u8 vale, valf;
243 	u8 *entry;
244 	int i;
245 
246 	entry = &acles->entries[index].entry[0];
247 	vale = entry[KSZ9477_ACL_PORT_ACCESS_E];
248 	valf = entry[KSZ9477_ACL_PORT_ACCESS_F];
249 
250 	val = (vale << 8) | valf;
251 
252 	/* If no bits are set, return an appropriate value or error */
253 	if (!val) {
254 		if (ksz9477_acl_is_valid_matching_rule(entry)) {
255 			/* Looks like we are about to corrupt some complex rule.
256 			 * Do not print an error here, as this is a normal case
257 			 * when we are trying to find a free or starting entry.
258 			 */
259 			dev_dbg(dev->dev, "ACL: entry %d starting with a valid matching rule, but no bits set in RuleSet\n",
260 				index);
261 			return -ENOTEMPTY;
262 		}
263 
264 		/* This entry does not contain a valid matching rule */
265 		return 0;
266 	}
267 
268 	start_idx = find_first_bit((unsigned long *)&val, 16);
269 	end_idx = find_last_bit((unsigned long *)&val, 16);
270 
271 	/* Calculate the contiguous count */
272 	contiguous_count = end_idx - start_idx + 1;
273 
274 	/* Check if the number of bits set in val matches our calculated count */
275 	if (contiguous_count != hweight16(val)) {
276 		/* Probably we have a fragmented complex rule, which is not
277 		 * supported by this driver.
278 		 */
279 		dev_err(dev->dev, "ACL: number of bits set in RuleSet does not match calculated count\n");
280 		return -EINVAL;
281 	}
282 
283 	/* loop over the contiguous entries and check for valid matching rules */
284 	for (i = start_idx; i <= end_idx; i++) {
285 		u8 *current_entry = &acles->entries[i].entry[0];
286 
287 		if (!ksz9477_acl_is_valid_matching_rule(current_entry)) {
288 			/* we have something linked without a valid matching
289 			 * rule. ACL table?
290 			 */
291 			dev_err(dev->dev, "ACL: entry %d does not contain a valid matching rule\n",
292 				i);
293 			return -EINVAL;
294 		}
295 
296 		if (i > start_idx) {
297 			vale = current_entry[KSZ9477_ACL_PORT_ACCESS_E];
298 			valf = current_entry[KSZ9477_ACL_PORT_ACCESS_F];
299 			/* Following entry should have empty linkage list */
300 			if (vale || valf) {
301 				dev_err(dev->dev, "ACL: entry %d has non-empty RuleSet linkage\n",
302 					i);
303 				return -EINVAL;
304 			}
305 		}
306 	}
307 
308 	return contiguous_count;
309 }
310 
311 /**
312  * ksz9477_acl_update_linkage - Update the RuleSet linkage for an ACL entry
313  *                              after a move operation.
314  *
315  * @dev: Pointer to the ksz_device.
316  * @entry:   Pointer to the ACL entry array.
317  * @old_idx: The original index of the ACL entry before moving.
318  * @new_idx: The new index of the ACL entry after moving.
319  *
320  * This function updates the RuleSet linkage bits for an ACL entry when
321  * it's moved from one position to another in the ACL table. The RuleSet
322  * linkage is represented by two 8-bit registers, which are combined
323  * into a 16-bit value for easier manipulation. The linkage bits are shifted
324  * based on the difference between the old and new index. If any bits are lost
325  * during the shift operation, an error is returned.
326  *
327  * Note: Fragmentation within a RuleSet is not supported. Hence, entries must
328  * be moved as complete blocks, maintaining the integrity of the RuleSet.
329  *
330  * Returns: 0 on success, or -EINVAL if any RuleSet linkage bits are lost
331  * during the move.
332  */
ksz9477_acl_update_linkage(struct ksz_device * dev,u8 * entry,u16 old_idx,u16 new_idx)333 static int ksz9477_acl_update_linkage(struct ksz_device *dev, u8 *entry,
334 				      u16 old_idx, u16 new_idx)
335 {
336 	unsigned int original_bit_count;
337 	unsigned long rule_linkage;
338 	u8 vale, valf, val0;
339 	int shift;
340 
341 	val0 = entry[KSZ9477_ACL_PORT_ACCESS_0];
342 	vale = entry[KSZ9477_ACL_PORT_ACCESS_E];
343 	valf = entry[KSZ9477_ACL_PORT_ACCESS_F];
344 
345 	/* Combine the two u8 values into one u16 for easier manipulation */
346 	rule_linkage = (vale << 8) | valf;
347 	original_bit_count = hweight16(rule_linkage);
348 
349 	/* Even if HW is able to handle fragmented RuleSet, we don't support it.
350 	 * RuleSet is filled only for the first entry of the set.
351 	 */
352 	if (!rule_linkage)
353 		return 0;
354 
355 	if (val0 != old_idx) {
356 		dev_err(dev->dev, "ACL: entry %d has unexpected ActionRule linkage: %d\n",
357 			old_idx, val0);
358 		return -EINVAL;
359 	}
360 
361 	val0 = new_idx;
362 
363 	/* Calculate the number of positions to shift */
364 	shift = new_idx - old_idx;
365 
366 	/* Shift the RuleSet */
367 	if (shift > 0)
368 		rule_linkage <<= shift;
369 	else
370 		rule_linkage >>= -shift;
371 
372 	/* Check that no bits were lost in the process */
373 	if (original_bit_count != hweight16(rule_linkage)) {
374 		dev_err(dev->dev, "ACL RuleSet linkage bits lost during move\n");
375 		return -EINVAL;
376 	}
377 
378 	entry[KSZ9477_ACL_PORT_ACCESS_0] = val0;
379 
380 	/* Update the RuleSet bitfields in the entry */
381 	entry[KSZ9477_ACL_PORT_ACCESS_E] = (rule_linkage >> 8) & 0xFF;
382 	entry[KSZ9477_ACL_PORT_ACCESS_F] = rule_linkage & 0xFF;
383 
384 	return 0;
385 }
386 
387 /**
388  * ksz9477_validate_and_get_src_count - Validate source and destination indices
389  *					and determine the source entry count.
390  * @dev: Pointer to the KSZ device structure.
391  * @port: Port number on the KSZ device where the ACL entries reside.
392  * @src_idx: Index of the starting ACL entry that needs to be validated.
393  * @dst_idx: Index of the destination where the source entries are intended to
394  *	     be moved.
395  * @src_count: Pointer to the variable that will hold the number of contiguous
396  *	     source entries if the validation passes.
397  * @dst_count: Pointer to the variable that will hold the number of contiguous
398  *	     destination entries if the validation passes.
399  *
400  * This function performs validation on the source and destination indices
401  * provided for ACL entries. It checks if the indices are within the valid
402  * range, and if the source entries are contiguous. Additionally, the function
403  * ensures that there's adequate space at the destination for the source entries
404  * and that the destination index isn't in the middle of a RuleSet. If all
405  * validations pass, the function returns the number of contiguous source and
406  * destination entries.
407  *
408  * Return: 0 on success, otherwise returns a negative error code if any
409  * validation check fails.
410  */
ksz9477_validate_and_get_src_count(struct ksz_device * dev,int port,int src_idx,int dst_idx,int * src_count,int * dst_count)411 static int ksz9477_validate_and_get_src_count(struct ksz_device *dev, int port,
412 					      int src_idx, int dst_idx,
413 					      int *src_count, int *dst_count)
414 {
415 	int ret;
416 
417 	if (src_idx >= KSZ9477_ACL_MAX_ENTRIES ||
418 	    dst_idx >= KSZ9477_ACL_MAX_ENTRIES) {
419 		dev_err(dev->dev, "ACL: invalid entry index\n");
420 		return -EINVAL;
421 	}
422 
423 	/* Validate if the source entries are contiguous */
424 	ret = ksz9477_acl_get_cont_entr(dev, port, src_idx);
425 	if (ret < 0)
426 		return ret;
427 	*src_count = ret;
428 
429 	if (!*src_count) {
430 		dev_err(dev->dev, "ACL: source entry is empty\n");
431 		return -EINVAL;
432 	}
433 
434 	if (dst_idx + *src_count >= KSZ9477_ACL_MAX_ENTRIES) {
435 		dev_err(dev->dev, "ACL: Not enough space at the destination. Move operation will fail.\n");
436 		return -EINVAL;
437 	}
438 
439 	/* Validate if the destination entry is empty or not in the middle of
440 	 * a RuleSet.
441 	 */
442 	ret = ksz9477_acl_get_cont_entr(dev, port, dst_idx);
443 	if (ret < 0)
444 		return ret;
445 	*dst_count = ret;
446 
447 	return 0;
448 }
449 
450 /**
451  * ksz9477_move_entries_downwards - Move a range of ACL entries downwards in
452  *				    the list.
453  * @dev: Pointer to the KSZ device structure.
454  * @acles: Pointer to the structure encapsulating all the ACL entries.
455  * @start_idx: Starting index of the entries to be relocated.
456  * @num_entries_to_move: Number of consecutive entries to be relocated.
457  * @end_idx: Destination index where the first entry should be situated post
458  *           relocation.
459  *
460  * This function is responsible for rearranging a specific block of ACL entries
461  * by shifting them downwards in the list based on the supplied source and
462  * destination indices. It ensures that the linkage between the ACL entries is
463  * maintained accurately after the relocation.
464  *
465  * Return: 0 on successful relocation of entries, otherwise returns a negative
466  * error code.
467  */
ksz9477_move_entries_downwards(struct ksz_device * dev,struct ksz9477_acl_entries * acles,u16 start_idx,u16 num_entries_to_move,u16 end_idx)468 static int ksz9477_move_entries_downwards(struct ksz_device *dev,
469 					  struct ksz9477_acl_entries *acles,
470 					  u16 start_idx,
471 					  u16 num_entries_to_move,
472 					  u16 end_idx)
473 {
474 	struct ksz9477_acl_entry *e;
475 	int ret, i;
476 
477 	for (i = start_idx; i < end_idx; i++) {
478 		e = &acles->entries[i];
479 		*e = acles->entries[i + num_entries_to_move];
480 
481 		ret = ksz9477_acl_update_linkage(dev, &e->entry[0],
482 						 i + num_entries_to_move, i);
483 		if (ret < 0)
484 			return ret;
485 	}
486 
487 	return 0;
488 }
489 
490 /**
491  * ksz9477_move_entries_upwards - Move a range of ACL entries upwards in the
492  *				  list.
493  * @dev: Pointer to the KSZ device structure.
494  * @acles: Pointer to the structure holding all the ACL entries.
495  * @start_idx: The starting index of the entries to be moved.
496  * @num_entries_to_move: Number of contiguous entries to be moved.
497  * @target_idx: The destination index where the first entry should be placed
498  *		after moving.
499  *
500  * This function rearranges a chunk of ACL entries by moving them upwards
501  * in the list based on the given source and destination indices. The reordering
502  * process preserves the linkage between entries by updating it accordingly.
503  *
504  * Return: 0 if the entries were successfully moved, otherwise a negative error
505  * code.
506  */
ksz9477_move_entries_upwards(struct ksz_device * dev,struct ksz9477_acl_entries * acles,u16 start_idx,u16 num_entries_to_move,u16 target_idx)507 static int ksz9477_move_entries_upwards(struct ksz_device *dev,
508 					struct ksz9477_acl_entries *acles,
509 					u16 start_idx, u16 num_entries_to_move,
510 					u16 target_idx)
511 {
512 	struct ksz9477_acl_entry *e;
513 	int ret, i, b;
514 
515 	for (i = start_idx; i > target_idx; i--) {
516 		b = i + num_entries_to_move - 1;
517 
518 		e = &acles->entries[b];
519 		*e = acles->entries[i - 1];
520 
521 		ret = ksz9477_acl_update_linkage(dev, &e->entry[0], i - 1, b);
522 		if (ret < 0)
523 			return ret;
524 	}
525 
526 	return 0;
527 }
528 
529 /**
530  * ksz9477_acl_move_entries - Move a block of contiguous ACL entries from a
531  *			      source to a destination index.
532  * @dev: Pointer to the KSZ9477 device structure.
533  * @port: Port number.
534  * @src_idx: Index of the starting source ACL entry.
535  * @dst_idx: Index of the starting destination ACL entry.
536  *
537  * This function aims to move a block of contiguous ACL entries from the source
538  * index to the destination index while ensuring the integrity and validity of
539  * the ACL table.
540  *
541  * In case of any errors during the adjustments or copying, the function will
542  * restore the ACL entries to their original state from the backup.
543  *
544  * Return: 0 if the move operation is successful. Returns -EINVAL for validation
545  * errors or other error codes based on specific failure conditions.
546  */
ksz9477_acl_move_entries(struct ksz_device * dev,int port,u16 src_idx,u16 dst_idx)547 static int ksz9477_acl_move_entries(struct ksz_device *dev, int port,
548 				    u16 src_idx, u16 dst_idx)
549 {
550 	struct ksz9477_acl_entry buffer[KSZ9477_ACL_MAX_ENTRIES];
551 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
552 	struct ksz9477_acl_entries *acles = &acl->acles;
553 	int src_count, ret, dst_count;
554 
555 	/* Nothing to do */
556 	if (src_idx == dst_idx)
557 		return 0;
558 
559 	ret = ksz9477_validate_and_get_src_count(dev, port, src_idx, dst_idx,
560 						 &src_count, &dst_count);
561 	if (ret)
562 		return ret;
563 
564 	/* In case dst_index is greater than src_index, we need to adjust the
565 	 * destination index to account for the entries that will be moved
566 	 * downwards and the size of the entry located at dst_idx.
567 	 */
568 	if (dst_idx > src_idx)
569 		dst_idx = dst_idx + dst_count - src_count;
570 
571 	/* Copy source block to buffer and update its linkage */
572 	for (int i = 0; i < src_count; i++) {
573 		buffer[i] = acles->entries[src_idx + i];
574 		ret = ksz9477_acl_update_linkage(dev, &buffer[i].entry[0],
575 						 src_idx + i, dst_idx + i);
576 		if (ret < 0)
577 			return ret;
578 	}
579 
580 	/* Adjust other entries and their linkage based on destination */
581 	if (dst_idx > src_idx) {
582 		ret = ksz9477_move_entries_downwards(dev, acles, src_idx,
583 						     src_count, dst_idx);
584 	} else {
585 		ret = ksz9477_move_entries_upwards(dev, acles, src_idx,
586 						   src_count, dst_idx);
587 	}
588 	if (ret < 0)
589 		return ret;
590 
591 	/* Copy buffer to destination block */
592 	for (int i = 0; i < src_count; i++)
593 		acles->entries[dst_idx + i] = buffer[i];
594 
595 	return 0;
596 }
597 
598 /**
599  * ksz9477_get_next_block_start - Identify the starting index of the next ACL
600  *				  block.
601  * @dev: Pointer to the device structure.
602  * @port: The port number on which the ACL entries are being checked.
603  * @start: The starting index from which the search begins.
604  *
605  * This function looks for the next valid ACL block starting from the provided
606  * 'start' index and returns the beginning index of that block. If the block is
607  * invalid or if it reaches the end of the ACL entries without finding another
608  * block, it returns the maximum ACL entries count.
609  *
610  * Returns:
611  *  - The starting index of the next valid ACL block.
612  *  - KSZ9477_ACL_MAX_ENTRIES if no other valid blocks are found after 'start'.
613  *  - A negative error code if an error occurs while checking.
614  */
ksz9477_get_next_block_start(struct ksz_device * dev,int port,int start)615 static int ksz9477_get_next_block_start(struct ksz_device *dev, int port,
616 					int start)
617 {
618 	int block_size;
619 
620 	for (int i = start; i < KSZ9477_ACL_MAX_ENTRIES;) {
621 		block_size = ksz9477_acl_get_cont_entr(dev, port, i);
622 		if (block_size < 0 && block_size != -ENOTEMPTY)
623 			return block_size;
624 
625 		if (block_size > 0)
626 			return i;
627 
628 		i++;
629 	}
630 	return KSZ9477_ACL_MAX_ENTRIES;
631 }
632 
633 /**
634  * ksz9477_swap_acl_blocks - Swap two ACL blocks
635  * @dev: Pointer to the device structure.
636  * @port: The port number on which the ACL blocks are to be swapped.
637  * @i: The starting index of the first ACL block.
638  * @j: The starting index of the second ACL block.
639  *
640  * This function is used to swap two ACL blocks present at given indices. The
641  * main purpose is to aid in the sorting and reordering of ACL blocks based on
642  * certain criteria, e.g., priority. It checks the validity of the block at
643  * index 'i', ensuring it's not an empty block, and then proceeds to swap it
644  * with the block at index 'j'.
645  *
646  * Returns:
647  *  - 0 on successful swapping of blocks.
648  *  - -EINVAL if the block at index 'i' is empty.
649  *  - A negative error code if any other error occurs during the swap.
650  */
ksz9477_swap_acl_blocks(struct ksz_device * dev,int port,int i,int j)651 static int ksz9477_swap_acl_blocks(struct ksz_device *dev, int port, int i,
652 				   int j)
653 {
654 	int ret, current_block_size;
655 
656 	current_block_size = ksz9477_acl_get_cont_entr(dev, port, i);
657 	if (current_block_size < 0)
658 		return current_block_size;
659 
660 	if (!current_block_size) {
661 		dev_err(dev->dev, "ACL: swapping empty entry %d\n", i);
662 		return -EINVAL;
663 	}
664 
665 	ret = ksz9477_acl_move_entries(dev, port, i, j);
666 	if (ret)
667 		return ret;
668 
669 	ret = ksz9477_acl_move_entries(dev, port, j - current_block_size, i);
670 	if (ret)
671 		return ret;
672 
673 	return 0;
674 }
675 
676 /**
677  * ksz9477_sort_acl_entr_no_back - Sort ACL entries for a given port based on
678  *			           priority without backing up entries.
679  * @dev: Pointer to the device structure.
680  * @port: The port number whose ACL entries need to be sorted.
681  *
682  * This function sorts ACL entries of the specified port using a variant of the
683  * bubble sort algorithm. It operates on blocks of ACL entries rather than
684  * individual entries. Each block's starting point is identified and then
685  * compared with subsequent blocks based on their priority. If the current
686  * block has a lower priority than the subsequent block, the two blocks are
687  * swapped.
688  *
689  * This is done in order to maintain an organized order of ACL entries based on
690  * priority, ensuring efficient and predictable ACL rule application.
691  *
692  * Returns:
693  *  - 0 on successful sorting of entries.
694  *  - A negative error code if any issue arises during sorting, e.g.,
695  *    if the function is unable to get the next block start.
696  */
ksz9477_sort_acl_entr_no_back(struct ksz_device * dev,int port)697 static int ksz9477_sort_acl_entr_no_back(struct ksz_device *dev, int port)
698 {
699 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
700 	struct ksz9477_acl_entries *acles = &acl->acles;
701 	struct ksz9477_acl_entry *curr, *next;
702 	int i, j, ret;
703 
704 	/* Bubble sort */
705 	for (i = 0; i < KSZ9477_ACL_MAX_ENTRIES;) {
706 		curr = &acles->entries[i];
707 
708 		j = ksz9477_get_next_block_start(dev, port, i + 1);
709 		if (j < 0)
710 			return j;
711 
712 		while (j < KSZ9477_ACL_MAX_ENTRIES) {
713 			next = &acles->entries[j];
714 
715 			if (curr->prio > next->prio) {
716 				ret = ksz9477_swap_acl_blocks(dev, port, i, j);
717 				if (ret)
718 					return ret;
719 			}
720 
721 			j = ksz9477_get_next_block_start(dev, port, j + 1);
722 			if (j < 0)
723 				return j;
724 		}
725 
726 		i = ksz9477_get_next_block_start(dev, port, i + 1);
727 		if (i < 0)
728 			return i;
729 	}
730 
731 	return 0;
732 }
733 
734 /**
735  * ksz9477_sort_acl_entries - Sort the ACL entries for a given port.
736  * @dev: Pointer to the KSZ device.
737  * @port: Port number.
738  *
739  * This function sorts the Access Control List (ACL) entries for a specified
740  * port. Before sorting, a backup of the original entries is created. If the
741  * sorting process fails, the function will log error messages displaying both
742  * the original and attempted sorted entries, and then restore the original
743  * entries from the backup.
744  *
745  * Return: 0 if the sorting succeeds, otherwise a negative error code.
746  */
ksz9477_sort_acl_entries(struct ksz_device * dev,int port)747 int ksz9477_sort_acl_entries(struct ksz_device *dev, int port)
748 {
749 	struct ksz9477_acl_entry backup[KSZ9477_ACL_MAX_ENTRIES];
750 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
751 	struct ksz9477_acl_entries *acles = &acl->acles;
752 	int ret;
753 
754 	/* create a backup of the ACL entries, if something goes wrong
755 	 * we can restore the ACL entries.
756 	 */
757 	memcpy(backup, acles->entries, sizeof(backup));
758 
759 	ret = ksz9477_sort_acl_entr_no_back(dev, port);
760 	if (ret) {
761 		dev_err(dev->dev, "ACL: failed to sort entries for port %d\n",
762 			port);
763 		dev_err(dev->dev, "ACL dump before sorting:\n");
764 		ksz9477_dump_acl(dev, backup);
765 		dev_err(dev->dev, "ACL dump after sorting:\n");
766 		ksz9477_dump_acl(dev, acles->entries);
767 		/* Restore the original entries */
768 		memcpy(acles->entries, backup, sizeof(backup));
769 	}
770 
771 	return ret;
772 }
773 
774 /**
775  * ksz9477_acl_wait_ready - Waits for the ACL operation to complete on a given
776  *			    port.
777  * @dev: The ksz_device instance.
778  * @port: The port number to wait for.
779  *
780  * This function checks if the ACL write or read operation is completed by
781  * polling the specified register.
782  *
783  * Returns: 0 if the operation is successful, or a negative error code if an
784  * error occurs.
785  */
ksz9477_acl_wait_ready(struct ksz_device * dev,int port)786 static int ksz9477_acl_wait_ready(struct ksz_device *dev, int port)
787 {
788 	unsigned int wr_mask = KSZ9477_ACL_WRITE_DONE | KSZ9477_ACL_READ_DONE;
789 	unsigned int val, reg;
790 	int ret;
791 
792 	reg = dev->dev_ops->get_port_addr(port, KSZ9477_PORT_ACL_CTRL_0);
793 
794 	ret = regmap_read_poll_timeout(dev->regmap[0], reg, val,
795 				       (val & wr_mask) == wr_mask, 1000, 10000);
796 	if (ret)
797 		dev_err(dev->dev, "Failed to read/write ACL table\n");
798 
799 	return ret;
800 }
801 
802 /**
803  * ksz9477_acl_entry_write - Writes an ACL entry to a given port at the
804  *			     specified index.
805  * @dev: The ksz_device instance.
806  * @port: The port number to write the ACL entry to.
807  * @entry: A pointer to the ACL entry data.
808  * @idx: The index at which to write the ACL entry.
809  *
810  * This function writes the provided ACL entry to the specified port at the
811  * given index.
812  *
813  * Returns: 0 if the operation is successful, or a negative error code if an
814  * error occurs.
815  */
ksz9477_acl_entry_write(struct ksz_device * dev,int port,u8 * entry,int idx)816 static int ksz9477_acl_entry_write(struct ksz_device *dev, int port, u8 *entry,
817 				   int idx)
818 {
819 	int ret, i;
820 	u8 val;
821 
822 	for (i = 0; i < KSZ9477_ACL_ENTRY_SIZE; i++) {
823 		ret = ksz_pwrite8(dev, port, KSZ9477_PORT_ACL_0 + i, entry[i]);
824 		if (ret) {
825 			dev_err(dev->dev, "Failed to write ACL entry %d\n", i);
826 			return ret;
827 		}
828 	}
829 
830 	/* write everything down */
831 	val = FIELD_PREP(KSZ9477_ACL_INDEX_M, idx) | KSZ9477_ACL_WRITE;
832 	ret = ksz_pwrite8(dev, port, KSZ9477_PORT_ACL_CTRL_0, val);
833 	if (ret)
834 		return ret;
835 
836 	/* wait until everything is written  */
837 	return ksz9477_acl_wait_ready(dev, port);
838 }
839 
840 /**
841  * ksz9477_acl_port_enable - Enables ACL functionality on a given port.
842  * @dev: The ksz_device instance.
843  * @port: The port number on which to enable ACL functionality.
844  *
845  * This function enables ACL functionality on the specified port by configuring
846  * the appropriate control registers. It returns 0 if the operation is
847  * successful, or a negative error code if an error occurs.
848  *
849  * 0xn801 - KSZ9477S 5.2.8.2 Port Priority Control Register
850  *        Bit 7 - Highest Priority
851  *        Bit 6 - OR'ed Priority
852  *        Bit 4 - MAC Address Priority Classification
853  *        Bit 3 - VLAN Priority Classification
854  *        Bit 2 - 802.1p Priority Classification
855  *        Bit 1 - Diffserv Priority Classification
856  *        Bit 0 - ACL Priority Classification
857  *
858  * Current driver implementation sets 802.1p priority classification by default.
859  * In this function we add ACL priority classification with OR'ed priority.
860  * According to testing, priority set by ACL will supersede the 802.1p priority.
861  *
862  * 0xn803 - KSZ9477S 5.2.8.4 Port Authentication Control Register
863  *        Bit 2 - Access Control List (ACL) Enable
864  *        Bits 1:0 - Authentication Mode
865  *                00 = Reserved
866  *                01 = Block Mode. Authentication is enabled. When ACL is
867  *                     enabled, all traffic that misses the ACL rules is
868  *                     blocked; otherwise ACL actions apply.
869  *                10 = Pass Mode. Authentication is disabled. When ACL is
870  *                     enabled, all traffic that misses the ACL rules is
871  *                     forwarded; otherwise ACL actions apply.
872  *                11 = Trap Mode. Authentication is enabled. All traffic is
873  *                     forwarded to the host port. When ACL is enabled, all
874  *                     traffic that misses the ACL rules is blocked; otherwise
875  *                     ACL actions apply.
876  *
877  * We are using Pass Mode int this function.
878  *
879  * Returns: 0 if the operation is successful, or a negative error code if an
880  * error occurs.
881  */
ksz9477_acl_port_enable(struct ksz_device * dev,int port)882 static int ksz9477_acl_port_enable(struct ksz_device *dev, int port)
883 {
884 	int ret;
885 
886 	ret = ksz_prmw8(dev, port, P_PRIO_CTRL, 0, PORT_ACL_PRIO_ENABLE |
887 			PORT_OR_PRIO);
888 	if (ret)
889 		return ret;
890 
891 	return ksz_pwrite8(dev, port, REG_PORT_MRI_AUTHEN_CTRL,
892 			   PORT_ACL_ENABLE |
893 			   FIELD_PREP(PORT_AUTHEN_MODE, PORT_AUTHEN_PASS));
894 }
895 
896 /**
897  * ksz9477_acl_port_disable - Disables ACL functionality on a given port.
898  * @dev: The ksz_device instance.
899  * @port: The port number on which to disable ACL functionality.
900  *
901  * This function disables ACL functionality on the specified port by writing a
902  * value of 0 to the REG_PORT_MRI_AUTHEN_CTRL control register and remove
903  * PORT_ACL_PRIO_ENABLE bit from P_PRIO_CTRL register.
904  *
905  * Returns: 0 if the operation is successful, or a negative error code if an
906  * error occurs.
907  */
ksz9477_acl_port_disable(struct ksz_device * dev,int port)908 static int ksz9477_acl_port_disable(struct ksz_device *dev, int port)
909 {
910 	int ret;
911 
912 	ret = ksz_prmw8(dev, port, P_PRIO_CTRL, PORT_ACL_PRIO_ENABLE, 0);
913 	if (ret)
914 		return ret;
915 
916 	return ksz_pwrite8(dev, port, REG_PORT_MRI_AUTHEN_CTRL, 0);
917 }
918 
919 /**
920  * ksz9477_acl_write_list - Write a list of ACL entries to a given port.
921  * @dev: The ksz_device instance.
922  * @port: The port number on which to write ACL entries.
923  *
924  * This function enables ACL functionality on the specified port, writes a list
925  * of ACL entries to the port, and disables ACL functionality if there are no
926  * entries.
927  *
928  * Returns: 0 if the operation is successful, or a negative error code if an
929  * error occurs.
930  */
ksz9477_acl_write_list(struct ksz_device * dev,int port)931 int ksz9477_acl_write_list(struct ksz_device *dev, int port)
932 {
933 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
934 	struct ksz9477_acl_entries *acles = &acl->acles;
935 	int ret, i;
936 
937 	/* ACL should be enabled before writing entries */
938 	ret = ksz9477_acl_port_enable(dev, port);
939 	if (ret)
940 		return ret;
941 
942 	/* write all entries */
943 	for (i = 0; i < ARRAY_SIZE(acles->entries); i++) {
944 		u8 *entry = acles->entries[i].entry;
945 
946 		/* Check if entry was removed and should be zeroed.
947 		 * If last fields of the entry are not zero, it means
948 		 * it is removed locally but currently not synced with the HW.
949 		 * So, we will write it down to the HW to remove it.
950 		 */
951 		if (i >= acles->entries_count &&
952 		    entry[KSZ9477_ACL_PORT_ACCESS_10] == 0 &&
953 		    entry[KSZ9477_ACL_PORT_ACCESS_11] == 0)
954 			continue;
955 
956 		ret = ksz9477_acl_entry_write(dev, port, entry, i);
957 		if (ret)
958 			return ret;
959 
960 		/* now removed entry is clean on HW side, so it can
961 		 * in the cache too
962 		 */
963 		if (i >= acles->entries_count &&
964 		    entry[KSZ9477_ACL_PORT_ACCESS_10] != 0 &&
965 		    entry[KSZ9477_ACL_PORT_ACCESS_11] != 0) {
966 			entry[KSZ9477_ACL_PORT_ACCESS_10] = 0;
967 			entry[KSZ9477_ACL_PORT_ACCESS_11] = 0;
968 		}
969 	}
970 
971 	if (!acles->entries_count)
972 		return ksz9477_acl_port_disable(dev, port);
973 
974 	return 0;
975 }
976 
977 /**
978  * ksz9477_acl_remove_entries - Remove ACL entries with a given cookie from a
979  *                              specified ksz9477_acl_entries structure.
980  * @dev: The ksz_device instance.
981  * @port: The port number on which to remove ACL entries.
982  * @acles: The ksz9477_acl_entries instance.
983  * @cookie: The cookie value to match for entry removal.
984  *
985  * This function iterates through the entries array, removing any entries with
986  * a matching cookie value. The remaining entries are then shifted down to fill
987  * the gap.
988  */
ksz9477_acl_remove_entries(struct ksz_device * dev,int port,struct ksz9477_acl_entries * acles,unsigned long cookie)989 void ksz9477_acl_remove_entries(struct ksz_device *dev, int port,
990 				struct ksz9477_acl_entries *acles,
991 				unsigned long cookie)
992 {
993 	int entries_count = acles->entries_count;
994 	int ret, i, src_count;
995 	int src_idx = -1;
996 
997 	if (!entries_count)
998 		return;
999 
1000 	/* Search for the first position with the cookie */
1001 	for (i = 0; i < entries_count; i++) {
1002 		if (acles->entries[i].cookie == cookie) {
1003 			src_idx = i;
1004 			break;
1005 		}
1006 	}
1007 
1008 	/* No entries with the matching cookie found */
1009 	if (src_idx == -1)
1010 		return;
1011 
1012 	/* Get the size of the cookie entry. We may have complex entries. */
1013 	src_count = ksz9477_acl_get_cont_entr(dev, port, src_idx);
1014 	if (src_count <= 0)
1015 		return;
1016 
1017 	/* Move all entries down to overwrite removed entry with the cookie */
1018 	ret = ksz9477_move_entries_downwards(dev, acles, src_idx,
1019 					     src_count,
1020 					     entries_count - src_count);
1021 	if (ret) {
1022 		dev_err(dev->dev, "Failed to move ACL entries down\n");
1023 		return;
1024 	}
1025 
1026 	/* Overwrite new empty places at the end of the list with zeros to make
1027 	 * sure not unexpected things will happen or no unexplored quirks will
1028 	 * come out.
1029 	 */
1030 	for (i = entries_count - src_count; i < entries_count; i++) {
1031 		struct ksz9477_acl_entry *entry = &acles->entries[i];
1032 
1033 		memset(entry, 0, sizeof(*entry));
1034 
1035 		/* Set all access bits to be able to write zeroed entry to HW */
1036 		entry->entry[KSZ9477_ACL_PORT_ACCESS_10] = 0xff;
1037 		entry->entry[KSZ9477_ACL_PORT_ACCESS_11] = 0xff;
1038 	}
1039 
1040 	/* Adjust the total entries count */
1041 	acles->entries_count -= src_count;
1042 }
1043 
1044 /**
1045  * ksz9477_port_acl_init - Initialize the ACL for a specified port on a ksz
1046  *			   device.
1047  * @dev: The ksz_device instance.
1048  * @port: The port number to initialize the ACL for.
1049  *
1050  * This function allocates memory for an acl structure, associates it with the
1051  * specified port, and initializes the ACL entries to a default state. The
1052  * entries are then written using the ksz9477_acl_write_list function, ensuring
1053  * the ACL has a predictable initial hardware state.
1054  *
1055  * Returns: 0 on success, or an error code on failure.
1056  */
ksz9477_port_acl_init(struct ksz_device * dev,int port)1057 int ksz9477_port_acl_init(struct ksz_device *dev, int port)
1058 {
1059 	struct ksz9477_acl_entries *acles;
1060 	struct ksz9477_acl_priv *acl;
1061 	int ret, i;
1062 
1063 	acl = kzalloc(sizeof(*acl), GFP_KERNEL);
1064 	if (!acl)
1065 		return -ENOMEM;
1066 
1067 	dev->ports[port].acl_priv = acl;
1068 
1069 	acles = &acl->acles;
1070 	/* write all entries */
1071 	for (i = 0; i < ARRAY_SIZE(acles->entries); i++) {
1072 		u8 *entry = acles->entries[i].entry;
1073 
1074 		/* Set all access bits to be able to write zeroed
1075 		 * entry
1076 		 */
1077 		entry[KSZ9477_ACL_PORT_ACCESS_10] = 0xff;
1078 		entry[KSZ9477_ACL_PORT_ACCESS_11] = 0xff;
1079 	}
1080 
1081 	ret = ksz9477_acl_write_list(dev, port);
1082 	if (ret)
1083 		goto free_acl;
1084 
1085 	return 0;
1086 
1087 free_acl:
1088 	kfree(dev->ports[port].acl_priv);
1089 	dev->ports[port].acl_priv = NULL;
1090 
1091 	return ret;
1092 }
1093 
1094 /**
1095  * ksz9477_port_acl_free - Free the ACL resources for a specified port on a ksz
1096  *			   device.
1097  * @dev: The ksz_device instance.
1098  * @port: The port number to initialize the ACL for.
1099  *
1100  * This disables the ACL for the specified port and frees the associated memory,
1101  */
ksz9477_port_acl_free(struct ksz_device * dev,int port)1102 void ksz9477_port_acl_free(struct ksz_device *dev, int port)
1103 {
1104 	if (!dev->ports[port].acl_priv)
1105 		return;
1106 
1107 	ksz9477_acl_port_disable(dev, port);
1108 
1109 	kfree(dev->ports[port].acl_priv);
1110 	dev->ports[port].acl_priv = NULL;
1111 }
1112 
1113 /**
1114  * ksz9477_acl_set_reg - Set entry[16] and entry[17] depending on the updated
1115  *			   entry[]
1116  * @entry: An array containing the entries
1117  * @reg: The register of the entry that needs to be updated
1118  * @value: The value to be assigned to the updated entry
1119  *
1120  * This function updates the entry[] array based on the provided register and
1121  * value. It also sets entry[0x10] and entry[0x11] according to the ACL byte
1122  * enable rules.
1123  *
1124  * 0x10 - Byte Enable [15:8]
1125  *
1126  * Each bit enables accessing one of the ACL bytes when a read or write is
1127  * initiated by writing to the Port ACL Byte Enable LSB Register.
1128  * Bit 0 applies to the Port ACL Access 7 Register
1129  * Bit 1 applies to the Port ACL Access 6 Register, etc.
1130  * Bit 7 applies to the Port ACL Access 0 Register
1131  * 1 = Byte is selected for read/write
1132  * 0 = Byte is not selected
1133  *
1134  * 0x11 - Byte Enable [7:0]
1135  *
1136  * Each bit enables accessing one of the ACL bytes when a read or write is
1137  * initiated by writing to the Port ACL Byte Enable LSB Register.
1138  * Bit 0 applies to the Port ACL Access F Register
1139  * Bit 1 applies to the Port ACL Access E Register, etc.
1140  * Bit 7 applies to the Port ACL Access 8 Register
1141  * 1 = Byte is selected for read/write
1142  * 0 = Byte is not selected
1143  */
ksz9477_acl_set_reg(u8 * entry,enum ksz9477_acl_port_access reg,u8 value)1144 static void ksz9477_acl_set_reg(u8 *entry, enum ksz9477_acl_port_access reg,
1145 				u8 value)
1146 {
1147 	if (reg >= KSZ9477_ACL_PORT_ACCESS_0 &&
1148 	    reg <= KSZ9477_ACL_PORT_ACCESS_7) {
1149 		entry[KSZ9477_ACL_PORT_ACCESS_10] |=
1150 				BIT(KSZ9477_ACL_PORT_ACCESS_7 - reg);
1151 	} else if (reg >= KSZ9477_ACL_PORT_ACCESS_8 &&
1152 		   reg <= KSZ9477_ACL_PORT_ACCESS_F) {
1153 		entry[KSZ9477_ACL_PORT_ACCESS_11] |=
1154 			BIT(KSZ9477_ACL_PORT_ACCESS_F - reg);
1155 	} else {
1156 		WARN_ON(1);
1157 		return;
1158 	}
1159 
1160 	entry[reg] = value;
1161 }
1162 
1163 /**
1164  * ksz9477_acl_matching_rule_cfg_l2 - Configure an ACL filtering entry to match
1165  *				      L2 types of Ethernet frames
1166  * @entry: Pointer to ACL entry buffer
1167  * @ethertype: Ethertype value
1168  * @eth_addr: Pointer to Ethernet address
1169  * @is_src: If true, match the source MAC address; if false, match the
1170  *	    destination MAC address
1171  *
1172  * This function configures an Access Control List (ACL) filtering
1173  * entry to match Layer 2 types of Ethernet frames based on the provided
1174  * ethertype and Ethernet address. Additionally, it can match either the source
1175  * or destination MAC address depending on the value of the is_src parameter.
1176  *
1177  * Register Descriptions for MD = 01 and ENB != 00 (Layer 2 MAC header
1178  * filtering)
1179  *
1180  * 0x01 - Mode and Enable
1181  *        Bits 5:4 - MD (Mode)
1182  *                01 = Layer 2 MAC header or counter filtering
1183  *        Bits 3:2 - ENB (Enable)
1184  *                01 = Comparison is performed only on the TYPE value
1185  *                10 = Comparison is performed only on the MAC Address value
1186  *                11 = Both the MAC Address and TYPE are tested
1187  *        Bit  1   - S/D (Source / Destination)
1188  *                0 = Destination address
1189  *                1 = Source address
1190  *        Bit  0   - EQ (Equal / Not Equal)
1191  *                0 = Not Equal produces true result
1192  *                1 = Equal produces true result
1193  *
1194  * 0x02-0x07 - MAC Address
1195  *        0x02 - MAC Address [47:40]
1196  *        0x03 - MAC Address [39:32]
1197  *        0x04 - MAC Address [31:24]
1198  *        0x05 - MAC Address [23:16]
1199  *        0x06 - MAC Address [15:8]
1200  *        0x07 - MAC Address [7:0]
1201  *
1202  * 0x08-0x09 - EtherType
1203  *        0x08 - EtherType [15:8]
1204  *        0x09 - EtherType [7:0]
1205  */
ksz9477_acl_matching_rule_cfg_l2(u8 * entry,u16 ethertype,u8 * eth_addr,bool is_src)1206 static void ksz9477_acl_matching_rule_cfg_l2(u8 *entry, u16 ethertype,
1207 					     u8 *eth_addr, bool is_src)
1208 {
1209 	u8 enb = 0;
1210 	u8 val;
1211 
1212 	if (ethertype)
1213 		enb |= KSZ9477_ACL_ENB_L2_TYPE;
1214 	if (eth_addr)
1215 		enb |= KSZ9477_ACL_ENB_L2_MAC;
1216 
1217 	val = FIELD_PREP(KSZ9477_ACL_MD_MASK, KSZ9477_ACL_MD_L2_MAC) |
1218 	      FIELD_PREP(KSZ9477_ACL_ENB_MASK, enb) |
1219 	      FIELD_PREP(KSZ9477_ACL_SD_SRC, is_src) | KSZ9477_ACL_EQ_EQUAL;
1220 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_1, val);
1221 
1222 	if (eth_addr) {
1223 		int i;
1224 
1225 		for (i = 0; i < ETH_ALEN; i++) {
1226 			ksz9477_acl_set_reg(entry,
1227 					    KSZ9477_ACL_PORT_ACCESS_2 + i,
1228 					    eth_addr[i]);
1229 		}
1230 	}
1231 
1232 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_8, ethertype >> 8);
1233 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_9, ethertype & 0xff);
1234 }
1235 
1236 /**
1237  * ksz9477_acl_action_rule_cfg - Set action for an ACL entry
1238  * @entry: Pointer to the ACL entry
1239  * @force_prio: If true, force the priority value
1240  * @prio_val: Priority value
1241  *
1242  * This function sets the action for the specified ACL entry. It prepares
1243  * the priority mode and traffic class values and updates the entry's
1244  * action registers accordingly. Currently, there is no port or VLAN PCP
1245  * remapping.
1246  *
1247  * ACL Action Rule Parameters for Non-Count Modes (MD ≠ 01 or ENB ≠ 00)
1248  *
1249  * 0x0A - PM, P, RPE, RP[2:1]
1250  *        Bits 7:6 - PM[1:0] - Priority Mode
1251  *		00 = ACL does not specify the packet priority. Priority is
1252  *		     determined by standard QoS functions.
1253  *		01 = Change packet priority to P[2:0] if it is greater than QoS
1254  *		     result.
1255  *		10 = Change packet priority to P[2:0] if it is smaller than the
1256  *		     QoS result.
1257  *		11 = Always change packet priority to P[2:0].
1258  *        Bits 5:3 - P[2:0] - Priority value
1259  *        Bit  2   - RPE - Remark Priority Enable
1260  *        Bits 1:0 - RP[2:1] - Remarked Priority value (bits 2:1)
1261  *		0 = Disable priority remarking
1262  *		1 = Enable priority remarking. VLAN tag priority (PCP) bits are
1263  *		    replaced by RP[2:0].
1264  *
1265  * 0x0B - RP[0], MM
1266  *        Bit  7   - RP[0] - Remarked Priority value (bit 0)
1267  *        Bits 6:5 - MM[1:0] - Map Mode
1268  *		00 = No forwarding remapping
1269  *		01 = The forwarding map in FORWARD is OR'ed with the forwarding
1270  *		     map from the Address Lookup Table.
1271  *		10 = The forwarding map in FORWARD is AND'ed with the forwarding
1272  *		     map from the Address Lookup Table.
1273  *		11 = The forwarding map in FORWARD replaces the forwarding map
1274  *		     from the Address Lookup Table.
1275  * 0x0D - FORWARD[n:0]
1276  *       Bits 7:0 - FORWARD[n:0] - Forwarding map. Bit 0 = port 1,
1277  *		    bit 1 = port 2, etc.
1278  *		1 = enable forwarding to this port
1279  *		0 = do not forward to this port
1280  */
ksz9477_acl_action_rule_cfg(u8 * entry,bool force_prio,u8 prio_val)1281 void ksz9477_acl_action_rule_cfg(u8 *entry, bool force_prio, u8 prio_val)
1282 {
1283 	u8 prio_mode, val;
1284 
1285 	if (force_prio)
1286 		prio_mode = KSZ9477_ACL_PM_REPLACE;
1287 	else
1288 		prio_mode = KSZ9477_ACL_PM_DISABLE;
1289 
1290 	val = FIELD_PREP(KSZ9477_ACL_PM_M, prio_mode) |
1291 	      FIELD_PREP(KSZ9477_ACL_P_M, prio_val);
1292 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_A, val);
1293 
1294 	/* no port or VLAN PCP remapping for now */
1295 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_B, 0);
1296 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_D, 0);
1297 }
1298 
1299 /**
1300  * ksz9477_acl_processing_rule_set_action - Set the action for the processing
1301  *					    rule set.
1302  * @entry: Pointer to the ACL entry
1303  * @action_idx: Index of the action to be applied
1304  *
1305  * This function sets the action for the processing rule set by updating the
1306  * appropriate register in the entry. There can be only one action per
1307  * processing rule.
1308  *
1309  * Access Control List (ACL) Processing Rule Registers:
1310  *
1311  * 0x00 - First Rule Number (FRN)
1312  *        Bits 3:0 - First Rule Number. Pointer to an Action rule entry.
1313  */
ksz9477_acl_processing_rule_set_action(u8 * entry,u8 action_idx)1314 void ksz9477_acl_processing_rule_set_action(u8 *entry, u8 action_idx)
1315 {
1316 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_0, action_idx);
1317 }
1318 
1319 /**
1320  * ksz9477_acl_processing_rule_add_match - Add a matching rule to the rule set
1321  * @entry: Pointer to the ACL entry
1322  * @match_idx: Index of the matching rule to be added
1323  *
1324  * This function adds a matching rule to the rule set by updating the
1325  * appropriate bits in the entry's rule set registers.
1326  *
1327  * Access Control List (ACL) Processing Rule Registers:
1328  *
1329  * 0x0E - RuleSet [15:8]
1330  *        Bits 7:0 - RuleSet [15:8] Specifies a set of one or more Matching rule
1331  *        entries. RuleSet has one bit for each of the 16 Matching rule entries.
1332  *        If multiple Matching rules are selected, then all conditions will be
1333  *	  AND'ed to produce a final match result.
1334  *		0 = Matching rule not selected
1335  *		1 = Matching rule selected
1336  *
1337  * 0x0F - RuleSet [7:0]
1338  *        Bits 7:0 - RuleSet [7:0]
1339  */
ksz9477_acl_processing_rule_add_match(u8 * entry,u8 match_idx)1340 static void ksz9477_acl_processing_rule_add_match(u8 *entry, u8 match_idx)
1341 {
1342 	u8 vale = entry[KSZ9477_ACL_PORT_ACCESS_E];
1343 	u8 valf = entry[KSZ9477_ACL_PORT_ACCESS_F];
1344 
1345 	if (match_idx < 8)
1346 		valf |= BIT(match_idx);
1347 	else
1348 		vale |= BIT(match_idx - 8);
1349 
1350 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_E, vale);
1351 	ksz9477_acl_set_reg(entry, KSZ9477_ACL_PORT_ACCESS_F, valf);
1352 }
1353 
1354 /**
1355  * ksz9477_acl_get_init_entry - Get a new uninitialized entry for a specified
1356  *				port on a ksz_device.
1357  * @dev: The ksz_device instance.
1358  * @port: The port number to get the uninitialized entry for.
1359  * @cookie: The cookie to associate with the entry.
1360  * @prio: The priority to associate with the entry.
1361  *
1362  * This function retrieves the next available ACL entry for the specified port,
1363  * clears all access flags, and associates it with the current cookie.
1364  *
1365  * Returns: A pointer to the new uninitialized ACL entry.
1366  */
1367 static struct ksz9477_acl_entry *
ksz9477_acl_get_init_entry(struct ksz_device * dev,int port,unsigned long cookie,u32 prio)1368 ksz9477_acl_get_init_entry(struct ksz_device *dev, int port,
1369 			   unsigned long cookie, u32 prio)
1370 {
1371 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
1372 	struct ksz9477_acl_entries *acles = &acl->acles;
1373 	struct ksz9477_acl_entry *entry;
1374 
1375 	entry = &acles->entries[acles->entries_count];
1376 	entry->cookie = cookie;
1377 	entry->prio = prio;
1378 
1379 	/* clear all access flags */
1380 	entry->entry[KSZ9477_ACL_PORT_ACCESS_10] = 0;
1381 	entry->entry[KSZ9477_ACL_PORT_ACCESS_11] = 0;
1382 
1383 	return entry;
1384 }
1385 
1386 /**
1387  * ksz9477_acl_match_process_l2 - Configure Layer 2 ACL matching rules and
1388  *                                processing rules.
1389  * @dev: Pointer to the ksz_device.
1390  * @port: Port number.
1391  * @ethtype: Ethernet type.
1392  * @src_mac: Source MAC address.
1393  * @dst_mac: Destination MAC address.
1394  * @cookie: The cookie to associate with the entry.
1395  * @prio: The priority of the entry.
1396  *
1397  * This function sets up matching and processing rules for Layer 2 ACLs.
1398  * It takes into account that only one MAC per entry is supported.
1399  */
ksz9477_acl_match_process_l2(struct ksz_device * dev,int port,u16 ethtype,u8 * src_mac,u8 * dst_mac,unsigned long cookie,u32 prio)1400 void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port,
1401 				  u16 ethtype, u8 *src_mac, u8 *dst_mac,
1402 				  unsigned long cookie, u32 prio)
1403 {
1404 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
1405 	struct ksz9477_acl_entries *acles = &acl->acles;
1406 	struct ksz9477_acl_entry *entry;
1407 
1408 	entry = ksz9477_acl_get_init_entry(dev, port, cookie, prio);
1409 
1410 	/* ACL supports only one MAC per entry */
1411 	if (src_mac && dst_mac) {
1412 		ksz9477_acl_matching_rule_cfg_l2(entry->entry, ethtype, src_mac,
1413 						 true);
1414 
1415 		/* Add both match entries to first processing rule */
1416 		ksz9477_acl_processing_rule_add_match(entry->entry,
1417 						      acles->entries_count);
1418 		acles->entries_count++;
1419 		ksz9477_acl_processing_rule_add_match(entry->entry,
1420 						      acles->entries_count);
1421 
1422 		entry = ksz9477_acl_get_init_entry(dev, port, cookie, prio);
1423 		ksz9477_acl_matching_rule_cfg_l2(entry->entry, 0, dst_mac,
1424 						 false);
1425 		acles->entries_count++;
1426 	} else {
1427 		u8 *mac = src_mac ? src_mac : dst_mac;
1428 		bool is_src = src_mac ? true : false;
1429 
1430 		ksz9477_acl_matching_rule_cfg_l2(entry->entry, ethtype, mac,
1431 						 is_src);
1432 		ksz9477_acl_processing_rule_add_match(entry->entry,
1433 						      acles->entries_count);
1434 		acles->entries_count++;
1435 	}
1436 }
1437