brintos

brintos / linux-shallow public Read only

0
0
Text · 18.9 KiB · c8aa29f Raw
652 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) Meta Platforms, Inc. and affiliates. */3 4#include <linux/etherdevice.h>5#include <linux/ethtool.h>6 7#include "fbnic.h"8#include "fbnic_netdev.h"9#include "fbnic_rpc.h"10 11void fbnic_reset_indir_tbl(struct fbnic_net *fbn)12{13	unsigned int num_rx = fbn->num_rx_queues;14	unsigned int i;15 16	for (i = 0; i < FBNIC_RPC_RSS_TBL_SIZE; i++) {17		fbn->indir_tbl[0][i] = ethtool_rxfh_indir_default(i, num_rx);18		fbn->indir_tbl[1][i] = ethtool_rxfh_indir_default(i, num_rx);19	}20}21 22void fbnic_rss_key_fill(u32 *buffer)23{24	static u32 rss_key[FBNIC_RPC_RSS_KEY_DWORD_LEN];25 26	net_get_random_once(rss_key, sizeof(rss_key));27	rss_key[FBNIC_RPC_RSS_KEY_LAST_IDX] &= FBNIC_RPC_RSS_KEY_LAST_MASK;28 29	memcpy(buffer, rss_key, sizeof(rss_key));30}31 32#define RX_HASH_OPT_L4 \33	(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)34#define RX_HASH_OPT_L3 \35	(RXH_IP_SRC | RXH_IP_DST)36#define RX_HASH_OPT_L2 RXH_L2DA37 38void fbnic_rss_init_en_mask(struct fbnic_net *fbn)39{40	fbn->rss_flow_hash[FBNIC_TCP4_HASH_OPT] = RX_HASH_OPT_L4;41	fbn->rss_flow_hash[FBNIC_TCP6_HASH_OPT] = RX_HASH_OPT_L4;42 43	fbn->rss_flow_hash[FBNIC_UDP4_HASH_OPT] = RX_HASH_OPT_L3;44	fbn->rss_flow_hash[FBNIC_UDP6_HASH_OPT] = RX_HASH_OPT_L3;45	fbn->rss_flow_hash[FBNIC_IPV4_HASH_OPT] = RX_HASH_OPT_L3;46	fbn->rss_flow_hash[FBNIC_IPV6_HASH_OPT] = RX_HASH_OPT_L3;47 48	fbn->rss_flow_hash[FBNIC_ETHER_HASH_OPT] = RX_HASH_OPT_L2;49}50 51void fbnic_rss_disable_hw(struct fbnic_dev *fbd)52{53	/* Disable RPC by clearing enable bit and configuration */54	if (!fbnic_bmc_present(fbd))55		wr32(fbd, FBNIC_RPC_RMI_CONFIG,56		     FIELD_PREP(FBNIC_RPC_RMI_CONFIG_OH_BYTES, 20));57}58 59#define FBNIC_FH_2_RSSEM_BIT(_fh, _rssem, _val)		\60	FIELD_PREP(FBNIC_RPC_ACT_TBL1_RSS_ENA_##_rssem,	\61		   FIELD_GET(RXH_##_fh, _val))62static u16 fbnic_flow_hash_2_rss_en_mask(struct fbnic_net *fbn, int flow_type)63{64	u32 flow_hash = fbn->rss_flow_hash[flow_type];65	u32 rss_en_mask = 0;66 67	rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L2DA, L2_DA, flow_hash);68	rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP_SRC, IP_SRC, flow_hash);69	rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP_DST, IP_DST, flow_hash);70	rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L4_B_0_1, L4_SRC, flow_hash);71	rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L4_B_2_3, L4_DST, flow_hash);72 73	return rss_en_mask;74}75 76void fbnic_rss_reinit_hw(struct fbnic_dev *fbd, struct fbnic_net *fbn)77{78	unsigned int i;79 80	for (i = 0; i < FBNIC_RPC_RSS_TBL_SIZE; i++) {81		wr32(fbd, FBNIC_RPC_RSS_TBL(0, i), fbn->indir_tbl[0][i]);82		wr32(fbd, FBNIC_RPC_RSS_TBL(1, i), fbn->indir_tbl[1][i]);83	}84 85	for (i = 0; i < FBNIC_RPC_RSS_KEY_DWORD_LEN; i++)86		wr32(fbd, FBNIC_RPC_RSS_KEY(i), fbn->rss_key[i]);87 88	/* Default action for this to drop w/ no destination */89	wr32(fbd, FBNIC_RPC_ACT_TBL0_DEFAULT, FBNIC_RPC_ACT_TBL0_DROP);90	wrfl(fbd);91 92	wr32(fbd, FBNIC_RPC_ACT_TBL1_DEFAULT, 0);93 94	/* If it isn't already enabled set the RMI Config value to enable RPC */95	wr32(fbd, FBNIC_RPC_RMI_CONFIG,96	     FIELD_PREP(FBNIC_RPC_RMI_CONFIG_MTU, FBNIC_MAX_JUMBO_FRAME_SIZE) |97	     FIELD_PREP(FBNIC_RPC_RMI_CONFIG_OH_BYTES, 20) |98	     FBNIC_RPC_RMI_CONFIG_ENABLE);99}100 101void fbnic_bmc_rpc_all_multi_config(struct fbnic_dev *fbd,102				    bool enable_host)103{104	struct fbnic_act_tcam *act_tcam;105	struct fbnic_mac_addr *mac_addr;106	int j;107 108	/* We need to add the all multicast filter at the end of the109	 * multicast address list. This way if there are any that are110	 * shared between the host and the BMC they can be directed to111	 * both. Otherwise the remainder just get sent directly to the112	 * BMC.113	 */114	mac_addr = &fbd->mac_addr[fbd->mac_addr_boundary - 1];115	if (fbnic_bmc_present(fbd) && fbd->fw_cap.all_multi) {116		if (mac_addr->state != FBNIC_TCAM_S_VALID) {117			eth_zero_addr(mac_addr->value.addr8);118			eth_broadcast_addr(mac_addr->mask.addr8);119			mac_addr->value.addr8[0] ^= 1;120			mac_addr->mask.addr8[0] ^= 1;121			set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);122			mac_addr->state = FBNIC_TCAM_S_ADD;123		}124		if (enable_host)125			set_bit(FBNIC_MAC_ADDR_T_ALLMULTI,126				mac_addr->act_tcam);127		else128			clear_bit(FBNIC_MAC_ADDR_T_ALLMULTI,129				  mac_addr->act_tcam);130	} else if (!test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam) &&131		   !is_zero_ether_addr(mac_addr->mask.addr8) &&132		   mac_addr->state == FBNIC_TCAM_S_VALID) {133		clear_bit(FBNIC_MAC_ADDR_T_ALLMULTI, mac_addr->act_tcam);134		clear_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);135		mac_addr->state = FBNIC_TCAM_S_DELETE;136	}137 138	/* We have to add a special handler for multicast as the139	 * BMC may have an all-multi rule already in place. As such140	 * adding a rule ourselves won't do any good so we will have141	 * to modify the rules for the ALL MULTI below if the BMC142	 * already has the rule in place.143	 */144	act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_BMC_ALL_MULTI_OFFSET];145 146	/* If we are not enabling the rule just delete it. We will fall147	 * back to the RSS rules that support the multicast addresses.148	 */149	if (!fbnic_bmc_present(fbd) || !fbd->fw_cap.all_multi || enable_host) {150		if (act_tcam->state == FBNIC_TCAM_S_VALID)151			act_tcam->state = FBNIC_TCAM_S_DELETE;152		return;153	}154 155	/* Rewrite TCAM rule 23 to handle BMC all-multi traffic */156	act_tcam->dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,157				    FBNIC_RPC_ACT_TBL0_DEST_BMC);158	act_tcam->mask.tcam[0] = 0xffff;159 160	/* MACDA 0 - 3 is reserved for the BMC MAC address */161	act_tcam->value.tcam[1] =162			FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,163				   fbd->mac_addr_boundary - 1) |164			FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;165	act_tcam->mask.tcam[1] = 0xffff &166			 ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX &167			 ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;168 169	for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)170		act_tcam->mask.tcam[j] = 0xffff;171 172	act_tcam->state = FBNIC_TCAM_S_UPDATE;173}174 175void fbnic_bmc_rpc_init(struct fbnic_dev *fbd)176{177	int i = FBNIC_RPC_TCAM_MACDA_BMC_ADDR_IDX;178	struct fbnic_act_tcam *act_tcam;179	struct fbnic_mac_addr *mac_addr;180	int j;181 182	/* Check if BMC is present */183	if (!fbnic_bmc_present(fbd))184		return;185 186	/* Fetch BMC MAC addresses from firmware capabilities */187	for (j = 0; j < 4; j++) {188		u8 *bmc_mac = fbd->fw_cap.bmc_mac_addr[j];189 190		/* Validate BMC MAC addresses */191		if (is_zero_ether_addr(bmc_mac))192			continue;193 194		if (is_multicast_ether_addr(bmc_mac))195			mac_addr = __fbnic_mc_sync(fbd, bmc_mac);196		else197			mac_addr = &fbd->mac_addr[i++];198 199		if (!mac_addr) {200			netdev_err(fbd->netdev,201				   "No slot for BMC MAC address[%d]\n", j);202			continue;203		}204 205		ether_addr_copy(mac_addr->value.addr8, bmc_mac);206		eth_zero_addr(mac_addr->mask.addr8);207 208		set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);209		mac_addr->state = FBNIC_TCAM_S_ADD;210	}211 212	/* Validate Broadcast is also present, record it and tag it */213	mac_addr = &fbd->mac_addr[FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX];214	eth_broadcast_addr(mac_addr->value.addr8);215	set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);216	mac_addr->state = FBNIC_TCAM_S_ADD;217 218	/* Rewrite TCAM rule 0 if it isn't present to relocate BMC rules */219	act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_BMC_OFFSET];220	act_tcam->dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,221				    FBNIC_RPC_ACT_TBL0_DEST_BMC);222	act_tcam->mask.tcam[0] = 0xffff;223 224	/* MACDA 0 - 3 is reserved for the BMC MAC address225	 * to account for that we have to mask out the lower 2 bits226	 * of the macda by performing an &= with 0x1c.227	 */228	act_tcam->value.tcam[1] = FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;229	act_tcam->mask.tcam[1] = 0xffff &230			~FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX, 0x1c) &231			~FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;232 233	for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)234		act_tcam->mask.tcam[j] = 0xffff;235 236	act_tcam->state = FBNIC_TCAM_S_UPDATE;237 238	fbnic_bmc_rpc_all_multi_config(fbd, false);239}240 241#define FBNIC_ACT1_INIT(_l4, _udp, _ip, _v6)		\242	(((_l4) ? FBNIC_RPC_TCAM_ACT1_L4_VALID : 0) |	\243	 ((_udp) ? FBNIC_RPC_TCAM_ACT1_L4_IS_UDP : 0) |	\244	 ((_ip) ? FBNIC_RPC_TCAM_ACT1_IP_VALID : 0) |	\245	 ((_v6) ? FBNIC_RPC_TCAM_ACT1_IP_IS_V6 : 0))246 247void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)248{249	static const u32 act1_value[FBNIC_NUM_HASH_OPT] = {250		FBNIC_ACT1_INIT(1, 1, 1, 1),	/* UDP6 */251		FBNIC_ACT1_INIT(1, 1, 1, 0),	/* UDP4 */252		FBNIC_ACT1_INIT(1, 0, 1, 1),	/* TCP6 */253		FBNIC_ACT1_INIT(1, 0, 1, 0),	/* TCP4 */254		FBNIC_ACT1_INIT(0, 0, 1, 1),	/* IP6 */255		FBNIC_ACT1_INIT(0, 0, 1, 0),	/* IP4 */256		0				/* Ether */257	};258	unsigned int i;259 260	/* To support scenarios where a BMC is present we must write the261	 * rules twice, once for the unicast cases, and once again for262	 * the broadcast/multicast cases as we have to support 2 destinations.263	 */264	BUILD_BUG_ON(FBNIC_RSS_EN_NUM_UNICAST * 2 != FBNIC_RSS_EN_NUM_ENTRIES);265	BUILD_BUG_ON(ARRAY_SIZE(act1_value) != FBNIC_NUM_HASH_OPT);266 267	/* Program RSS hash enable mask for host in action TCAM/table. */268	for (i = fbnic_bmc_present(fbd) ? 0 : FBNIC_RSS_EN_NUM_UNICAST;269	     i < FBNIC_RSS_EN_NUM_ENTRIES; i++) {270		unsigned int idx = i + FBNIC_RPC_ACT_TBL_RSS_OFFSET;271		struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[idx];272		u32 flow_hash, dest, rss_en_mask;273		int flow_type, j;274		u16 value = 0;275 276		flow_type = i % FBNIC_RSS_EN_NUM_UNICAST;277		flow_hash = fbn->rss_flow_hash[flow_type];278 279		/* Set DEST_HOST based on absence of RXH_DISCARD */280		dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,281				  !(RXH_DISCARD & flow_hash) ?282				  FBNIC_RPC_ACT_TBL0_DEST_HOST : 0);283 284		if (i >= FBNIC_RSS_EN_NUM_UNICAST && fbnic_bmc_present(fbd))285			dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,286					   FBNIC_RPC_ACT_TBL0_DEST_BMC);287 288		if (!dest)289			dest = FBNIC_RPC_ACT_TBL0_DROP;290 291		if (act1_value[flow_type] & FBNIC_RPC_TCAM_ACT1_L4_VALID)292			dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT,293					   FBNIC_RCD_HDR_AL_DMA_HINT_L4);294 295		rss_en_mask = fbnic_flow_hash_2_rss_en_mask(fbn, flow_type);296 297		act_tcam->dest = dest;298		act_tcam->rss_en_mask = rss_en_mask;299		act_tcam->state = FBNIC_TCAM_S_UPDATE;300 301		act_tcam->mask.tcam[0] = 0xffff;302 303		/* We reserve the upper 8 MACDA TCAM entries for host304		 * unicast. So we set the value to 24, and the mask the305		 * lower bits so that the lower entries can be used as306		 * multicast or BMC addresses.307		 */308		if (i < FBNIC_RSS_EN_NUM_UNICAST)309			value = FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,310					   fbd->mac_addr_boundary);311		value |= FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;312 313		flow_type = i % FBNIC_RSS_EN_NUM_UNICAST;314		value |= act1_value[flow_type];315 316		act_tcam->value.tcam[1] = value;317		act_tcam->mask.tcam[1] = ~value;318 319		for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)320			act_tcam->mask.tcam[j] = 0xffff;321 322		act_tcam->state = FBNIC_TCAM_S_UPDATE;323	}324}325 326struct fbnic_mac_addr *__fbnic_uc_sync(struct fbnic_dev *fbd,327				       const unsigned char *addr)328{329	struct fbnic_mac_addr *avail_addr = NULL;330	unsigned int i;331 332	/* Scan from middle of list to bottom, filling bottom up.333	 * Skip the first entry which is reserved for dev_addr and334	 * leave the last entry to use for promiscuous filtering.335	 */336	for (i = fbd->mac_addr_boundary - 1;337	     i < FBNIC_RPC_TCAM_MACDA_HOST_ADDR_IDX; i++) {338		struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];339 340		if (mac_addr->state == FBNIC_TCAM_S_DISABLED) {341			avail_addr = mac_addr;342		} else if (ether_addr_equal(mac_addr->value.addr8, addr)) {343			avail_addr = mac_addr;344			break;345		}346	}347 348	if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {349		ether_addr_copy(avail_addr->value.addr8, addr);350		eth_zero_addr(avail_addr->mask.addr8);351		avail_addr->state = FBNIC_TCAM_S_ADD;352	}353 354	return avail_addr;355}356 357struct fbnic_mac_addr *__fbnic_mc_sync(struct fbnic_dev *fbd,358				       const unsigned char *addr)359{360	struct fbnic_mac_addr *avail_addr = NULL;361	unsigned int i;362 363	/* Scan from middle of list to top, filling top down.364	 * Skip over the address reserved for the BMC MAC and365	 * exclude index 0 as that belongs to the broadcast address366	 */367	for (i = fbd->mac_addr_boundary;368	     --i > FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX;) {369		struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];370 371		if (mac_addr->state == FBNIC_TCAM_S_DISABLED) {372			avail_addr = mac_addr;373		} else if (ether_addr_equal(mac_addr->value.addr8, addr)) {374			avail_addr = mac_addr;375			break;376		}377	}378 379	/* Scan the BMC addresses to see if it may have already380	 * reserved the address.381	 */382	while (--i) {383		struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];384 385		if (!is_zero_ether_addr(mac_addr->mask.addr8))386			continue;387 388		/* Only move on if we find a match */389		if (!ether_addr_equal(mac_addr->value.addr8, addr))390			continue;391 392		/* We need to pull this address to the shared area */393		if (avail_addr) {394			memcpy(avail_addr, mac_addr, sizeof(*mac_addr));395			mac_addr->state = FBNIC_TCAM_S_DELETE;396			avail_addr->state = FBNIC_TCAM_S_ADD;397		}398 399		break;400	}401 402	if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {403		ether_addr_copy(avail_addr->value.addr8, addr);404		eth_zero_addr(avail_addr->mask.addr8);405		avail_addr->state = FBNIC_TCAM_S_ADD;406	}407 408	return avail_addr;409}410 411int __fbnic_xc_unsync(struct fbnic_mac_addr *mac_addr, unsigned int tcam_idx)412{413	if (!test_and_clear_bit(tcam_idx, mac_addr->act_tcam))414		return -ENOENT;415 416	if (bitmap_empty(mac_addr->act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES))417		mac_addr->state = FBNIC_TCAM_S_DELETE;418 419	return 0;420}421 422void fbnic_sift_macda(struct fbnic_dev *fbd)423{424	int dest, src;425 426	/* Move BMC only addresses back into BMC region */427	for (dest = FBNIC_RPC_TCAM_MACDA_BMC_ADDR_IDX,428	     src = FBNIC_RPC_TCAM_MACDA_MULTICAST_IDX;429	     ++dest < FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX &&430	     src < fbd->mac_addr_boundary;) {431		struct fbnic_mac_addr *dest_addr = &fbd->mac_addr[dest];432 433		if (dest_addr->state != FBNIC_TCAM_S_DISABLED)434			continue;435 436		while (src < fbd->mac_addr_boundary) {437			struct fbnic_mac_addr *src_addr = &fbd->mac_addr[src++];438 439			/* Verify BMC bit is set */440			if (!test_bit(FBNIC_MAC_ADDR_T_BMC, src_addr->act_tcam))441				continue;442 443			/* Verify filter isn't already disabled */444			if (src_addr->state == FBNIC_TCAM_S_DISABLED ||445			    src_addr->state == FBNIC_TCAM_S_DELETE)446				continue;447 448			/* Verify only BMC bit is set */449			if (bitmap_weight(src_addr->act_tcam,450					  FBNIC_RPC_TCAM_ACT_NUM_ENTRIES) != 1)451				continue;452 453			/* Verify we are not moving wildcard address */454			if (!is_zero_ether_addr(src_addr->mask.addr8))455				continue;456 457			memcpy(dest_addr, src_addr, sizeof(*src_addr));458			src_addr->state = FBNIC_TCAM_S_DELETE;459			dest_addr->state = FBNIC_TCAM_S_ADD;460		}461	}462}463 464static void fbnic_clear_macda_entry(struct fbnic_dev *fbd, unsigned int idx)465{466	int i;467 468	/* Invalidate entry and clear addr state info */469	for (i = 0; i <= FBNIC_RPC_TCAM_MACDA_WORD_LEN; i++)470		wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i), 0);471}472 473static void fbnic_clear_macda(struct fbnic_dev *fbd)474{475	int idx;476 477	for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {478		struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];479 480		if (mac_addr->state == FBNIC_TCAM_S_DISABLED)481			continue;482 483		if (test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam)) {484			if (fbnic_bmc_present(fbd))485				continue;486			dev_warn_once(fbd->dev,487				      "Found BMC MAC address w/ BMC not present\n");488		}489 490		fbnic_clear_macda_entry(fbd, idx);491 492		/* If rule was already destined for deletion just wipe it now */493		if (mac_addr->state == FBNIC_TCAM_S_DELETE) {494			memset(mac_addr, 0, sizeof(*mac_addr));495			continue;496		}497 498		/* Change state to update so that we will rewrite499		 * this tcam the next time fbnic_write_macda is called.500		 */501		mac_addr->state = FBNIC_TCAM_S_UPDATE;502	}503}504 505static void fbnic_write_macda_entry(struct fbnic_dev *fbd, unsigned int idx,506				    struct fbnic_mac_addr *mac_addr)507{508	__be16 *mask, *value;509	int i;510 511	mask = &mac_addr->mask.addr16[FBNIC_RPC_TCAM_MACDA_WORD_LEN - 1];512	value = &mac_addr->value.addr16[FBNIC_RPC_TCAM_MACDA_WORD_LEN - 1];513 514	for (i = 0; i < FBNIC_RPC_TCAM_MACDA_WORD_LEN; i++)515		wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i),516		     FIELD_PREP(FBNIC_RPC_TCAM_MACDA_MASK, ntohs(*mask--)) |517		     FIELD_PREP(FBNIC_RPC_TCAM_MACDA_VALUE, ntohs(*value--)));518 519	wrfl(fbd);520 521	wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i), FBNIC_RPC_TCAM_VALIDATE);522}523 524void fbnic_write_macda(struct fbnic_dev *fbd)525{526	int idx;527 528	for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {529		struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];530 531		/* Check if update flag is set else exit. */532		if (!(mac_addr->state & FBNIC_TCAM_S_UPDATE))533			continue;534 535		/* Clear by writing 0s. */536		if (mac_addr->state == FBNIC_TCAM_S_DELETE) {537			/* Invalidate entry and clear addr state info */538			fbnic_clear_macda_entry(fbd, idx);539			memset(mac_addr, 0, sizeof(*mac_addr));540 541			continue;542		}543 544		fbnic_write_macda_entry(fbd, idx, mac_addr);545 546		mac_addr->state = FBNIC_TCAM_S_VALID;547	}548}549 550static void fbnic_clear_act_tcam(struct fbnic_dev *fbd, unsigned int idx)551{552	int i;553 554	/* Invalidate entry and clear addr state info */555	for (i = 0; i <= FBNIC_RPC_TCAM_ACT_WORD_LEN; i++)556		wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i), 0);557}558 559void fbnic_clear_rules(struct fbnic_dev *fbd)560{561	u32 dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,562			      FBNIC_RPC_ACT_TBL0_DEST_BMC);563	int i = FBNIC_RPC_TCAM_ACT_NUM_ENTRIES - 1;564	struct fbnic_act_tcam *act_tcam;565 566	/* Clear MAC rules */567	fbnic_clear_macda(fbd);568 569	/* If BMC is present we need to preserve the last rule which570	 * will be used to route traffic to the BMC if it is received.571	 *572	 * At this point it should be the only MAC address in the MACDA573	 * so any unicast or multicast traffic received should be routed574	 * to it. So leave the last rule in place.575	 *576	 * It will be rewritten to add the host again when we bring577	 * the interface back up.578	 */579	if (fbnic_bmc_present(fbd)) {580		act_tcam = &fbd->act_tcam[i];581 582		if (act_tcam->state == FBNIC_TCAM_S_VALID &&583		    (act_tcam->dest & dest)) {584			wr32(fbd, FBNIC_RPC_ACT_TBL0(i), dest);585			wr32(fbd, FBNIC_RPC_ACT_TBL1(i), 0);586 587			act_tcam->state = FBNIC_TCAM_S_UPDATE;588 589			i--;590		}591	}592 593	/* Work from the bottom up deleting all other rules from hardware */594	do {595		act_tcam = &fbd->act_tcam[i];596 597		if (act_tcam->state != FBNIC_TCAM_S_VALID)598			continue;599 600		fbnic_clear_act_tcam(fbd, i);601		act_tcam->state = FBNIC_TCAM_S_UPDATE;602	} while (i--);603}604 605static void fbnic_delete_act_tcam(struct fbnic_dev *fbd, unsigned int idx)606{607	fbnic_clear_act_tcam(fbd, idx);608	memset(&fbd->act_tcam[idx], 0, sizeof(struct fbnic_act_tcam));609}610 611static void fbnic_update_act_tcam(struct fbnic_dev *fbd, unsigned int idx)612{613	struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[idx];614	int i;615 616	/* Update entry by writing the destination and RSS mask */617	wr32(fbd, FBNIC_RPC_ACT_TBL0(idx), act_tcam->dest);618	wr32(fbd, FBNIC_RPC_ACT_TBL1(idx), act_tcam->rss_en_mask);619 620	/* Write new TCAM rule to hardware */621	for (i = 0; i < FBNIC_RPC_TCAM_ACT_WORD_LEN; i++)622		wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i),623		     FIELD_PREP(FBNIC_RPC_TCAM_ACT_MASK,624				act_tcam->mask.tcam[i]) |625		     FIELD_PREP(FBNIC_RPC_TCAM_ACT_VALUE,626				act_tcam->value.tcam[i]));627 628	wrfl(fbd);629 630	wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i), FBNIC_RPC_TCAM_VALIDATE);631	act_tcam->state = FBNIC_TCAM_S_VALID;632}633 634void fbnic_write_rules(struct fbnic_dev *fbd)635{636	int i;637 638	/* Flush any pending action table rules */639	for (i = 0; i < FBNIC_RPC_ACT_TBL_NUM_ENTRIES; i++) {640		struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[i];641 642		/* Check if update flag is set else exit. */643		if (!(act_tcam->state & FBNIC_TCAM_S_UPDATE))644			continue;645 646		if (act_tcam->state == FBNIC_TCAM_S_DELETE)647			fbnic_delete_act_tcam(fbd, i);648		else649			fbnic_update_act_tcam(fbd, i);650	}651}652