brintos

brintos / linux-shallow public Read only

0
0
Text · 45.9 KiB · 7ba778d Raw
1437 lines · c
1// SPDX-License-Identifier: GPL-2.02// 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 incoming9 *   packets based on their header information (Layer 2 MAC, Layer 3 IP, or10 *   Layer 4 TCP/UDP). Different register settings are used depending on the11 *   matching rule mode (MD) and the Enable (ENB) settings.12 *13 * - Action Rules: These registers define how the ACL should modify the packet's14 *   priority, VLAN tag priority, and forwarding map once a matching rule has15 *   been triggered. The settings vary depending on whether the matching rule is16 *   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/disabling20 *   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 *                          v31 *               +----------------------+32 *               |   Processing Rules   |33 *               | (action idx,         |34 *               | matching rule set)   |35 *               +----------------------+36 *                          |37 *                          v38 *               +----------------------+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		0x60053 54enum 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 = 0x1173};74 75#define KSZ9477_ACL_MD_MASK			GENMASK(5, 4)76#define KSZ9477_ACL_MD_DISABLE			077#define KSZ9477_ACL_MD_L2_MAC			178#define KSZ9477_ACL_MD_L3_IP			279#define KSZ9477_ACL_MD_L4_TCP_UDP		380 81#define KSZ9477_ACL_ENB_MASK			GENMASK(3, 2)82#define KSZ9477_ACL_ENB_L2_COUNTER		083#define KSZ9477_ACL_ENB_L2_TYPE			184#define KSZ9477_ACL_ENB_L2_MAC			285#define KSZ9477_ACL_ENB_L2_MAC_TYPE		386 87/* only IPv4 src or dst can be used with mask */88#define KSZ9477_ACL_ENB_L3_IPV4_ADDR_MASK	189/* only IPv4 src and dst can be used without mask */90#define KSZ9477_ACL_ENB_L3_IPV4_ADDR_SRC_DST	291 92#define KSZ9477_ACL_ENB_L4_IP_PROTO	        093#define KSZ9477_ACL_ENB_L4_TCP_SRC_DST_PORT	194#define KSZ9477_ACL_ENB_L4_UDP_SRC_DST_PORT	295#define KSZ9477_ACL_ENB_L4_TCP_SEQ_NUMBER	396 97#define KSZ9477_ACL_SD_SRC			BIT(1)98#define KSZ9477_ACL_SD_DST			099#define KSZ9477_ACL_EQ_EQUAL			BIT(0)100#define KSZ9477_ACL_EQ_NOT_EQUAL		0101 102#define KSZ9477_ACL_PM_M			GENMASK(7, 6)103#define KSZ9477_ACL_PM_DISABLE			0104#define KSZ9477_ACL_PM_HIGHER			1105#define KSZ9477_ACL_PM_LOWER			2106#define KSZ9477_ACL_PM_REPLACE			3107#define KSZ9477_ACL_P_M				GENMASK(5, 3)108 109#define KSZ9477_PORT_ACL_CTRL_0			0x0612110 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 index118 *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 particular124 * index within the ksz9477 device's ACL table. It omits printing entries that125 * are empty.126 *127 * Return: 1 if the entry is non-empty and printed, 0 otherwise.128 */129static 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 entries157 *158 * @dev: Pointer to the device structure.159 * @acle: Pointer to the ACL entry array.160 */161static 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 valid176 *					matching rule.177 *178 * @entry: Pointer to ACL entry buffer179 *180 * This function checks if the given ACL entry buffer contains a valid181 * 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 */185static 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 validate207 *                             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 RuleSet213 * 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 and215 * KSZ9477_ACL_PORT_ACCESS_F. Each bit set in these registers corresponds to216 * an entry containing a Matching rule for this RuleSet.217 *218 * For a single Matching rule linked, only one bit is set. However, when an219 * 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 the223 * linked Matching rules are contiguous in terms of their indices. It calculates224 * and returns the number of these contiguous entries.225 *226 * Returns:227 *    - 0 if the entry is empty and can be safely overwritten228 *    - 1 if the entry represents a simple rule229 *    - The number of contiguous entries if it is the root entry of a complex230 *      rule231 *    - -ENOTEMPTY if the entry is part of a complex rule but not the root232 *      entry233 *    - -EINVAL if the validation fails234 */235static 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 case257			 * 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 not277		 * 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 matching289			 * 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 entry313 *                              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 when321 * it's moved from one position to another in the ACL table. The RuleSet322 * linkage is represented by two 8-bit registers, which are combined323 * into a 16-bit value for easier manipulation. The linkage bits are shifted324 * based on the difference between the old and new index. If any bits are lost325 * during the shift operation, an error is returned.326 *327 * Note: Fragmentation within a RuleSet is not supported. Hence, entries must328 * 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 lost331 * during the move.332 */333static 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	else370		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 indices389 *					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 to394 *	     be moved.395 * @src_count: Pointer to the variable that will hold the number of contiguous396 *	     source entries if the validation passes.397 * @dst_count: Pointer to the variable that will hold the number of contiguous398 *	     destination entries if the validation passes.399 *400 * This function performs validation on the source and destination indices401 * provided for ACL entries. It checks if the indices are within the valid402 * range, and if the source entries are contiguous. Additionally, the function403 * ensures that there's adequate space at the destination for the source entries404 * and that the destination index isn't in the middle of a RuleSet. If all405 * validations pass, the function returns the number of contiguous source and406 * destination entries.407 *408 * Return: 0 on success, otherwise returns a negative error code if any409 * validation check fails.410 */411static 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 of440	 * 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 in452 *				    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 post458 *           relocation.459 *460 * This function is responsible for rearranging a specific block of ACL entries461 * by shifting them downwards in the list based on the supplied source and462 * destination indices. It ensures that the linkage between the ACL entries is463 * maintained accurately after the relocation.464 *465 * Return: 0 on successful relocation of entries, otherwise returns a negative466 * error code.467 */468static 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 the492 *				  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 placed498 *		after moving.499 *500 * This function rearranges a chunk of ACL entries by moving them upwards501 * in the list based on the given source and destination indices. The reordering502 * process preserves the linkage between entries by updating it accordingly.503 *504 * Return: 0 if the entries were successfully moved, otherwise a negative error505 * code.506 */507static 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 a531 *			      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 source538 * index to the destination index while ensuring the integrity and validity of539 * the ACL table.540 *541 * In case of any errors during the adjustments or copying, the function will542 * restore the ACL entries to their original state from the backup.543 *544 * Return: 0 if the move operation is successful. Returns -EINVAL for validation545 * errors or other error codes based on specific failure conditions.546 */547static 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 the565	 * destination index to account for the entries that will be moved566	 * 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 ACL600 *				  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 provided606 * 'start' index and returns the beginning index of that block. If the block is607 * invalid or if it reaches the end of the ACL entries without finding another608 * 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 */615static 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 blocks635 * @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. The641 * main purpose is to aid in the sorting and reordering of ACL blocks based on642 * certain criteria, e.g., priority. It checks the validity of the block at643 * index 'i', ensuring it's not an empty block, and then proceeds to swap it644 * 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 */651static 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 on678 *			           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 the683 * bubble sort algorithm. It operates on blocks of ACL entries rather than684 * individual entries. Each block's starting point is identified and then685 * compared with subsequent blocks based on their priority. If the current686 * block has a lower priority than the subsequent block, the two blocks are687 * swapped.688 *689 * This is done in order to maintain an organized order of ACL entries based on690 * 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 */697static 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 specified740 * port. Before sorting, a backup of the original entries is created. If the741 * sorting process fails, the function will log error messages displaying both742 * the original and attempted sorted entries, and then restore the original743 * entries from the backup.744 *745 * Return: 0 if the sorting succeeds, otherwise a negative error code.746 */747int 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 wrong755	 * 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 given776 *			    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 by781 * polling the specified register.782 *783 * Returns: 0 if the operation is successful, or a negative error code if an784 * error occurs.785 */786static 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 the804 *			     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 the811 * given index.812 *813 * Returns: 0 if the operation is successful, or a negative error code if an814 * error occurs.815 */816static 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 configuring846 * the appropriate control registers. It returns 0 if the operation is847 * successful, or a negative error code if an error occurs.848 *849 * 0xn801 - KSZ9477S 5.2.8.2 Port Priority Control Register850 *        Bit 7 - Highest Priority851 *        Bit 6 - OR'ed Priority852 *        Bit 4 - MAC Address Priority Classification853 *        Bit 3 - VLAN Priority Classification854 *        Bit 2 - 802.1p Priority Classification855 *        Bit 1 - Diffserv Priority Classification856 *        Bit 0 - ACL Priority Classification857 *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 Register863 *        Bit 2 - Access Control List (ACL) Enable864 *        Bits 1:0 - Authentication Mode865 *                00 = Reserved866 *                01 = Block Mode. Authentication is enabled. When ACL is867 *                     enabled, all traffic that misses the ACL rules is868 *                     blocked; otherwise ACL actions apply.869 *                10 = Pass Mode. Authentication is disabled. When ACL is870 *                     enabled, all traffic that misses the ACL rules is871 *                     forwarded; otherwise ACL actions apply.872 *                11 = Trap Mode. Authentication is enabled. All traffic is873 *                     forwarded to the host port. When ACL is enabled, all874 *                     traffic that misses the ACL rules is blocked; otherwise875 *                     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 an880 * error occurs.881 */882static 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 a902 * value of 0 to the REG_PORT_MRI_AUTHEN_CTRL control register and remove903 * 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 an906 * error occurs.907 */908static 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 list925 * of ACL entries to the port, and disables ACL functionality if there are no926 * entries.927 *928 * Returns: 0 if the operation is successful, or a negative error code if an929 * error occurs.930 */931int 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 means948		 * 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 can961		 * in the cache too962		 */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 a979 *                              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 with986 * a matching cookie value. The remaining entries are then shifted down to fill987 * the gap.988 */989void 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 make1027	 * sure not unexpected things will happen or no unexplored quirks will1028	 * 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 ksz1046 *			   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 the1051 * specified port, and initializes the ACL entries to a default state. The1052 * entries are then written using the ksz9477_acl_write_list function, ensuring1053 * the ACL has a predictable initial hardware state.1054 *1055 * Returns: 0 on success, or an error code on failure.1056 */1057int 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 zeroed1075		 * entry1076		 */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 1087free_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 ksz1096 *			   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 */1102void 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 updated1115 *			   entry[]1116 * @entry: An array containing the entries1117 * @reg: The register of the entry that needs to be updated1118 * @value: The value to be assigned to the updated entry1119 *1120 * This function updates the entry[] array based on the provided register and1121 * value. It also sets entry[0x10] and entry[0x11] according to the ACL byte1122 * 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 is1127 * initiated by writing to the Port ACL Byte Enable LSB Register.1128 * Bit 0 applies to the Port ACL Access 7 Register1129 * Bit 1 applies to the Port ACL Access 6 Register, etc.1130 * Bit 7 applies to the Port ACL Access 0 Register1131 * 1 = Byte is selected for read/write1132 * 0 = Byte is not selected1133 *1134 * 0x11 - Byte Enable [7:0]1135 *1136 * Each bit enables accessing one of the ACL bytes when a read or write is1137 * initiated by writing to the Port ACL Byte Enable LSB Register.1138 * Bit 0 applies to the Port ACL Access F Register1139 * Bit 1 applies to the Port ACL Access E Register, etc.1140 * Bit 7 applies to the Port ACL Access 8 Register1141 * 1 = Byte is selected for read/write1142 * 0 = Byte is not selected1143 */1144static 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 match1165 *				      L2 types of Ethernet frames1166 * @entry: Pointer to ACL entry buffer1167 * @ethertype: Ethertype value1168 * @eth_addr: Pointer to Ethernet address1169 * @is_src: If true, match the source MAC address; if false, match the1170 *	    destination MAC address1171 *1172 * This function configures an Access Control List (ACL) filtering1173 * entry to match Layer 2 types of Ethernet frames based on the provided1174 * ethertype and Ethernet address. Additionally, it can match either the source1175 * 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 header1178 * filtering)1179 *1180 * 0x01 - Mode and Enable1181 *        Bits 5:4 - MD (Mode)1182 *                01 = Layer 2 MAC header or counter filtering1183 *        Bits 3:2 - ENB (Enable)1184 *                01 = Comparison is performed only on the TYPE value1185 *                10 = Comparison is performed only on the MAC Address value1186 *                11 = Both the MAC Address and TYPE are tested1187 *        Bit  1   - S/D (Source / Destination)1188 *                0 = Destination address1189 *                1 = Source address1190 *        Bit  0   - EQ (Equal / Not Equal)1191 *                0 = Not Equal produces true result1192 *                1 = Equal produces true result1193 *1194 * 0x02-0x07 - MAC Address1195 *        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 - EtherType1203 *        0x08 - EtherType [15:8]1204 *        0x09 - EtherType [7:0]1205 */1206static 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 entry1238 * @entry: Pointer to the ACL entry1239 * @force_prio: If true, force the priority value1240 * @prio_val: Priority value1241 *1242 * This function sets the action for the specified ACL entry. It prepares1243 * the priority mode and traffic class values and updates the entry's1244 * action registers accordingly. Currently, there is no port or VLAN PCP1245 * 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 Mode1251 *		00 = ACL does not specify the packet priority. Priority is1252 *		     determined by standard QoS functions.1253 *		01 = Change packet priority to P[2:0] if it is greater than QoS1254 *		     result.1255 *		10 = Change packet priority to P[2:0] if it is smaller than the1256 *		     QoS result.1257 *		11 = Always change packet priority to P[2:0].1258 *        Bits 5:3 - P[2:0] - Priority value1259 *        Bit  2   - RPE - Remark Priority Enable1260 *        Bits 1:0 - RP[2:1] - Remarked Priority value (bits 2:1)1261 *		0 = Disable priority remarking1262 *		1 = Enable priority remarking. VLAN tag priority (PCP) bits are1263 *		    replaced by RP[2:0].1264 *1265 * 0x0B - RP[0], MM1266 *        Bit  7   - RP[0] - Remarked Priority value (bit 0)1267 *        Bits 6:5 - MM[1:0] - Map Mode1268 *		00 = No forwarding remapping1269 *		01 = The forwarding map in FORWARD is OR'ed with the forwarding1270 *		     map from the Address Lookup Table.1271 *		10 = The forwarding map in FORWARD is AND'ed with the forwarding1272 *		     map from the Address Lookup Table.1273 *		11 = The forwarding map in FORWARD replaces the forwarding map1274 *		     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 port1279 *		0 = do not forward to this port1280 */1281void 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	else1288		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 processing1301 *					    rule set.1302 * @entry: Pointer to the ACL entry1303 * @action_idx: Index of the action to be applied1304 *1305 * This function sets the action for the processing rule set by updating the1306 * appropriate register in the entry. There can be only one action per1307 * 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 */1314void 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 set1321 * @entry: Pointer to the ACL entry1322 * @match_idx: Index of the matching rule to be added1323 *1324 * This function adds a matching rule to the rule set by updating the1325 * 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 rule1331 *        entries. RuleSet has one bit for each of the 16 Matching rule entries.1332 *        If multiple Matching rules are selected, then all conditions will be1333 *	  AND'ed to produce a final match result.1334 *		0 = Matching rule not selected1335 *		1 = Matching rule selected1336 *1337 * 0x0F - RuleSet [7:0]1338 *        Bits 7:0 - RuleSet [7:0]1339 */1340static 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	else1348		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 specified1356 *				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 */1367static struct ksz9477_acl_entry *1368ksz9477_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 and1388 *                                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 */1400void 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