xref: /linux/drivers/net/ethernet/mediatek/mtk_ppe.c (revision 9410645520e9b820069761f3450ef6661418e279)
1ba37b7caSFelix Fietkau // SPDX-License-Identifier: GPL-2.0-only
2ba37b7caSFelix Fietkau /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
3ba37b7caSFelix Fietkau 
4ba37b7caSFelix Fietkau #include <linux/kernel.h>
5ba37b7caSFelix Fietkau #include <linux/io.h>
6c5d66587SIlya Lipnitskiy #include <linux/iopoll.h>
7ba37b7caSFelix Fietkau #include <linux/etherdevice.h>
8ba37b7caSFelix Fietkau #include <linux/platform_device.h>
933fc42deSFelix Fietkau #include <linux/if_ether.h>
1033fc42deSFelix Fietkau #include <linux/if_vlan.h>
11*b908c722SSimon Horman 
125f36ca1bSFelix Fietkau #include <net/dst_metadata.h>
1333fc42deSFelix Fietkau #include <net/dsa.h>
14*b908c722SSimon Horman #include <net/ipv6.h>
15*b908c722SSimon Horman 
16c4f033d9SFelix Fietkau #include "mtk_eth_soc.h"
17ba37b7caSFelix Fietkau #include "mtk_ppe.h"
18ba37b7caSFelix Fietkau #include "mtk_ppe_regs.h"
19ba37b7caSFelix Fietkau 
20c4f033d9SFelix Fietkau static DEFINE_SPINLOCK(ppe_lock);
21c4f033d9SFelix Fietkau 
2233fc42deSFelix Fietkau static const struct rhashtable_params mtk_flow_l2_ht_params = {
2333fc42deSFelix Fietkau 	.head_offset = offsetof(struct mtk_flow_entry, l2_node),
2433fc42deSFelix Fietkau 	.key_offset = offsetof(struct mtk_flow_entry, data.bridge),
2533fc42deSFelix Fietkau 	.key_len = offsetof(struct mtk_foe_bridge, key_end),
2633fc42deSFelix Fietkau 	.automatic_shrinking = true,
2733fc42deSFelix Fietkau };
2833fc42deSFelix Fietkau 
ppe_w32(struct mtk_ppe * ppe,u32 reg,u32 val)29ba37b7caSFelix Fietkau static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
30ba37b7caSFelix Fietkau {
31ba37b7caSFelix Fietkau 	writel(val, ppe->base + reg);
32ba37b7caSFelix Fietkau }
33ba37b7caSFelix Fietkau 
ppe_r32(struct mtk_ppe * ppe,u32 reg)34ba37b7caSFelix Fietkau static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
35ba37b7caSFelix Fietkau {
36ba37b7caSFelix Fietkau 	return readl(ppe->base + reg);
37ba37b7caSFelix Fietkau }
38ba37b7caSFelix Fietkau 
ppe_m32(struct mtk_ppe * ppe,u32 reg,u32 mask,u32 set)39ba37b7caSFelix Fietkau static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
40ba37b7caSFelix Fietkau {
41ba37b7caSFelix Fietkau 	u32 val;
42ba37b7caSFelix Fietkau 
43ba37b7caSFelix Fietkau 	val = ppe_r32(ppe, reg);
44ba37b7caSFelix Fietkau 	val &= ~mask;
45ba37b7caSFelix Fietkau 	val |= set;
46ba37b7caSFelix Fietkau 	ppe_w32(ppe, reg, val);
47ba37b7caSFelix Fietkau 
48ba37b7caSFelix Fietkau 	return val;
49ba37b7caSFelix Fietkau }
50ba37b7caSFelix Fietkau 
ppe_set(struct mtk_ppe * ppe,u32 reg,u32 val)51ba37b7caSFelix Fietkau static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
52ba37b7caSFelix Fietkau {
53ba37b7caSFelix Fietkau 	return ppe_m32(ppe, reg, 0, val);
54ba37b7caSFelix Fietkau }
55ba37b7caSFelix Fietkau 
ppe_clear(struct mtk_ppe * ppe,u32 reg,u32 val)56ba37b7caSFelix Fietkau static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
57ba37b7caSFelix Fietkau {
58ba37b7caSFelix Fietkau 	return ppe_m32(ppe, reg, val, 0);
59ba37b7caSFelix Fietkau }
60ba37b7caSFelix Fietkau 
mtk_eth_timestamp(struct mtk_eth * eth)61c4f033d9SFelix Fietkau static u32 mtk_eth_timestamp(struct mtk_eth *eth)
62c4f033d9SFelix Fietkau {
6303a3180eSLorenzo Bianconi 	return mtk_r32(eth, 0x0010) & mtk_get_ib1_ts_mask(eth);
64c4f033d9SFelix Fietkau }
65c4f033d9SFelix Fietkau 
mtk_ppe_wait_busy(struct mtk_ppe * ppe)66ba37b7caSFelix Fietkau static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
67ba37b7caSFelix Fietkau {
68c5d66587SIlya Lipnitskiy 	int ret;
69c5d66587SIlya Lipnitskiy 	u32 val;
70ba37b7caSFelix Fietkau 
71c5d66587SIlya Lipnitskiy 	ret = readl_poll_timeout(ppe->base + MTK_PPE_GLO_CFG, val,
72c5d66587SIlya Lipnitskiy 				 !(val & MTK_PPE_GLO_CFG_BUSY),
73c5d66587SIlya Lipnitskiy 				 20, MTK_PPE_WAIT_TIMEOUT_US);
74ba37b7caSFelix Fietkau 
75c5d66587SIlya Lipnitskiy 	if (ret)
76ba37b7caSFelix Fietkau 		dev_err(ppe->dev, "PPE table busy");
77ba37b7caSFelix Fietkau 
78c5d66587SIlya Lipnitskiy 	return ret;
79ba37b7caSFelix Fietkau }
80ba37b7caSFelix Fietkau 
mtk_ppe_mib_wait_busy(struct mtk_ppe * ppe)813fbe4d8cSDaniel Golle static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
823fbe4d8cSDaniel Golle {
833fbe4d8cSDaniel Golle 	int ret;
843fbe4d8cSDaniel Golle 	u32 val;
853fbe4d8cSDaniel Golle 
863fbe4d8cSDaniel Golle 	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
873fbe4d8cSDaniel Golle 				 !(val & MTK_PPE_MIB_SER_CR_ST),
883fbe4d8cSDaniel Golle 				 20, MTK_PPE_WAIT_TIMEOUT_US);
893fbe4d8cSDaniel Golle 
903fbe4d8cSDaniel Golle 	if (ret)
913fbe4d8cSDaniel Golle 		dev_err(ppe->dev, "MIB table busy");
923fbe4d8cSDaniel Golle 
933fbe4d8cSDaniel Golle 	return ret;
943fbe4d8cSDaniel Golle }
953fbe4d8cSDaniel Golle 
mtk_mib_entry_read(struct mtk_ppe * ppe,u16 index,u64 * bytes,u64 * packets)963fbe4d8cSDaniel Golle static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
973fbe4d8cSDaniel Golle {
983fbe4d8cSDaniel Golle 	u32 val, cnt_r0, cnt_r1, cnt_r2;
993fbe4d8cSDaniel Golle 	int ret;
1003fbe4d8cSDaniel Golle 
1013fbe4d8cSDaniel Golle 	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
1023fbe4d8cSDaniel Golle 	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
1033fbe4d8cSDaniel Golle 
1043fbe4d8cSDaniel Golle 	ret = mtk_ppe_mib_wait_busy(ppe);
1053fbe4d8cSDaniel Golle 	if (ret)
1063fbe4d8cSDaniel Golle 		return ret;
1073fbe4d8cSDaniel Golle 
1083fbe4d8cSDaniel Golle 	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
1093fbe4d8cSDaniel Golle 	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
1103fbe4d8cSDaniel Golle 	cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
1113fbe4d8cSDaniel Golle 
112571e9c49SDaniel Golle 	if (mtk_is_netsys_v3_or_greater(ppe->eth)) {
113571e9c49SDaniel Golle 		/* 64 bit for each counter */
114571e9c49SDaniel Golle 		u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3);
115571e9c49SDaniel Golle 		*bytes = ((u64)cnt_r1 << 32) | cnt_r0;
116571e9c49SDaniel Golle 		*packets = ((u64)cnt_r3 << 32) | cnt_r2;
117571e9c49SDaniel Golle 	} else {
118571e9c49SDaniel Golle 		/* 48 bit byte counter, 40 bit packet counter */
119571e9c49SDaniel Golle 		u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
120571e9c49SDaniel Golle 		u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
121571e9c49SDaniel Golle 		u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
122571e9c49SDaniel Golle 		u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
1233fbe4d8cSDaniel Golle 		*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
124571e9c49SDaniel Golle 		*packets = ((u64)pkt_cnt_high << 16) | pkt_cnt_low;
125571e9c49SDaniel Golle 	}
1263fbe4d8cSDaniel Golle 
1273fbe4d8cSDaniel Golle 	return 0;
1283fbe4d8cSDaniel Golle }
1293fbe4d8cSDaniel Golle 
mtk_ppe_cache_clear(struct mtk_ppe * ppe)130ba37b7caSFelix Fietkau static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
131ba37b7caSFelix Fietkau {
132ba37b7caSFelix Fietkau 	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
133ba37b7caSFelix Fietkau 	ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
134ba37b7caSFelix Fietkau }
135ba37b7caSFelix Fietkau 
mtk_ppe_cache_enable(struct mtk_ppe * ppe,bool enable)136ba37b7caSFelix Fietkau static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
137ba37b7caSFelix Fietkau {
138ba37b7caSFelix Fietkau 	mtk_ppe_cache_clear(ppe);
139ba37b7caSFelix Fietkau 
140ba37b7caSFelix Fietkau 	ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
141ba37b7caSFelix Fietkau 		enable * MTK_PPE_CACHE_CTL_EN);
142ba37b7caSFelix Fietkau }
143ba37b7caSFelix Fietkau 
mtk_ppe_hash_entry(struct mtk_eth * eth,struct mtk_foe_entry * e)144ba2fc48cSLorenzo Bianconi static u32 mtk_ppe_hash_entry(struct mtk_eth *eth, struct mtk_foe_entry *e)
145ba37b7caSFelix Fietkau {
146ba37b7caSFelix Fietkau 	u32 hv1, hv2, hv3;
147ba37b7caSFelix Fietkau 	u32 hash;
148ba37b7caSFelix Fietkau 
14903a3180eSLorenzo Bianconi 	switch (mtk_get_ib1_pkt_type(eth, e->ib1)) {
150ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
151ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
152ba37b7caSFelix Fietkau 			hv1 = e->ipv4.orig.ports;
153ba37b7caSFelix Fietkau 			hv2 = e->ipv4.orig.dest_ip;
154ba37b7caSFelix Fietkau 			hv3 = e->ipv4.orig.src_ip;
155ba37b7caSFelix Fietkau 			break;
156ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
157ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
158ba37b7caSFelix Fietkau 			hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
159ba37b7caSFelix Fietkau 			hv1 ^= e->ipv6.ports;
160ba37b7caSFelix Fietkau 
161ba37b7caSFelix Fietkau 			hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
162ba37b7caSFelix Fietkau 			hv2 ^= e->ipv6.dest_ip[0];
163ba37b7caSFelix Fietkau 
164ba37b7caSFelix Fietkau 			hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
165ba37b7caSFelix Fietkau 			hv3 ^= e->ipv6.src_ip[0];
166ba37b7caSFelix Fietkau 			break;
167ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
168ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_6RD:
169ba37b7caSFelix Fietkau 		default:
170ba37b7caSFelix Fietkau 			WARN_ON_ONCE(1);
171ba37b7caSFelix Fietkau 			return MTK_PPE_HASH_MASK;
172ba37b7caSFelix Fietkau 	}
173ba37b7caSFelix Fietkau 
174ba37b7caSFelix Fietkau 	hash = (hv1 & hv2) | ((~hv1) & hv3);
175ba37b7caSFelix Fietkau 	hash = (hash >> 24) | ((hash & 0xffffff) << 8);
176ba37b7caSFelix Fietkau 	hash ^= hv1 ^ hv2 ^ hv3;
177ba37b7caSFelix Fietkau 	hash ^= hash >> 16;
178ba2fc48cSLorenzo Bianconi 	hash <<= (ffs(eth->soc->hash_offset) - 1);
179ba37b7caSFelix Fietkau 	hash &= MTK_PPE_ENTRIES - 1;
180ba37b7caSFelix Fietkau 
181ba37b7caSFelix Fietkau 	return hash;
182ba37b7caSFelix Fietkau }
183ba37b7caSFelix Fietkau 
184ba37b7caSFelix Fietkau static inline struct mtk_foe_mac_info *
mtk_foe_entry_l2(struct mtk_eth * eth,struct mtk_foe_entry * entry)18503a3180eSLorenzo Bianconi mtk_foe_entry_l2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
186ba37b7caSFelix Fietkau {
18703a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
188ba37b7caSFelix Fietkau 
18933fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
19033fc42deSFelix Fietkau 		return &entry->bridge.l2;
19133fc42deSFelix Fietkau 
192ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
193ba37b7caSFelix Fietkau 		return &entry->ipv6.l2;
194ba37b7caSFelix Fietkau 
195ba37b7caSFelix Fietkau 	return &entry->ipv4.l2;
196ba37b7caSFelix Fietkau }
197ba37b7caSFelix Fietkau 
198ba37b7caSFelix Fietkau static inline u32 *
mtk_foe_entry_ib2(struct mtk_eth * eth,struct mtk_foe_entry * entry)19903a3180eSLorenzo Bianconi mtk_foe_entry_ib2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
200ba37b7caSFelix Fietkau {
20103a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
202ba37b7caSFelix Fietkau 
20333fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
20433fc42deSFelix Fietkau 		return &entry->bridge.ib2;
20533fc42deSFelix Fietkau 
206ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
207ba37b7caSFelix Fietkau 		return &entry->ipv6.ib2;
208ba37b7caSFelix Fietkau 
209ba37b7caSFelix Fietkau 	return &entry->ipv4.ib2;
210ba37b7caSFelix Fietkau }
211ba37b7caSFelix Fietkau 
mtk_foe_entry_prepare(struct mtk_eth * eth,struct mtk_foe_entry * entry,int type,int l4proto,u8 pse_port,u8 * src_mac,u8 * dest_mac)21203a3180eSLorenzo Bianconi int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry,
21303a3180eSLorenzo Bianconi 			  int type, int l4proto, u8 pse_port, u8 *src_mac,
21403a3180eSLorenzo Bianconi 			  u8 *dest_mac)
215ba37b7caSFelix Fietkau {
216ba37b7caSFelix Fietkau 	struct mtk_foe_mac_info *l2;
217ba37b7caSFelix Fietkau 	u32 ports_pad, val;
218ba37b7caSFelix Fietkau 
219ba37b7caSFelix Fietkau 	memset(entry, 0, sizeof(*entry));
220ba37b7caSFelix Fietkau 
221a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(eth)) {
22203a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
22303a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) |
22403a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
22503a3180eSLorenzo Bianconi 		      MTK_FOE_IB1_BIND_CACHE_V2 | MTK_FOE_IB1_BIND_TTL_V2;
22603a3180eSLorenzo Bianconi 		entry->ib1 = val;
22703a3180eSLorenzo Bianconi 
22803a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) |
22903a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf);
23003a3180eSLorenzo Bianconi 	} else {
23171ba8e48SFelix Fietkau 		int port_mg = eth->soc->offload_version > 1 ? 0 : 0x3f;
23271ba8e48SFelix Fietkau 
233ba37b7caSFelix Fietkau 		val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
234ba37b7caSFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
235ba37b7caSFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
23603a3180eSLorenzo Bianconi 		      MTK_FOE_IB1_BIND_CACHE | MTK_FOE_IB1_BIND_TTL;
237ba37b7caSFelix Fietkau 		entry->ib1 = val;
238ba37b7caSFelix Fietkau 
23903a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) |
24071ba8e48SFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB2_PORT_MG, port_mg) |
24103a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f);
24203a3180eSLorenzo Bianconi 	}
243ba37b7caSFelix Fietkau 
244ba37b7caSFelix Fietkau 	if (is_multicast_ether_addr(dest_mac))
24503a3180eSLorenzo Bianconi 		val |= mtk_get_ib2_multicast_mask(eth);
246ba37b7caSFelix Fietkau 
247ba37b7caSFelix Fietkau 	ports_pad = 0xa5a5a500 | (l4proto & 0xff);
248ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
249ba37b7caSFelix Fietkau 		entry->ipv4.orig.ports = ports_pad;
250ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
251ba37b7caSFelix Fietkau 		entry->ipv6.ports = ports_pad;
252ba37b7caSFelix Fietkau 
25333fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
25433fc42deSFelix Fietkau 		ether_addr_copy(entry->bridge.src_mac, src_mac);
25533fc42deSFelix Fietkau 		ether_addr_copy(entry->bridge.dest_mac, dest_mac);
25633fc42deSFelix Fietkau 		entry->bridge.ib2 = val;
25733fc42deSFelix Fietkau 		l2 = &entry->bridge.l2;
25833fc42deSFelix Fietkau 	} else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
259ba37b7caSFelix Fietkau 		entry->ipv6.ib2 = val;
260ba37b7caSFelix Fietkau 		l2 = &entry->ipv6.l2;
261ba37b7caSFelix Fietkau 	} else {
262ba37b7caSFelix Fietkau 		entry->ipv4.ib2 = val;
263ba37b7caSFelix Fietkau 		l2 = &entry->ipv4.l2;
264ba37b7caSFelix Fietkau 	}
265ba37b7caSFelix Fietkau 
266ba37b7caSFelix Fietkau 	l2->dest_mac_hi = get_unaligned_be32(dest_mac);
267ba37b7caSFelix Fietkau 	l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
268ba37b7caSFelix Fietkau 	l2->src_mac_hi = get_unaligned_be32(src_mac);
269ba37b7caSFelix Fietkau 	l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
270ba37b7caSFelix Fietkau 
271ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
272ba37b7caSFelix Fietkau 		l2->etype = ETH_P_IPV6;
273ba37b7caSFelix Fietkau 	else
274ba37b7caSFelix Fietkau 		l2->etype = ETH_P_IP;
275ba37b7caSFelix Fietkau 
276ba37b7caSFelix Fietkau 	return 0;
277ba37b7caSFelix Fietkau }
278ba37b7caSFelix Fietkau 
mtk_foe_entry_set_pse_port(struct mtk_eth * eth,struct mtk_foe_entry * entry,u8 port)27903a3180eSLorenzo Bianconi int mtk_foe_entry_set_pse_port(struct mtk_eth *eth,
28003a3180eSLorenzo Bianconi 			       struct mtk_foe_entry *entry, u8 port)
281ba37b7caSFelix Fietkau {
28203a3180eSLorenzo Bianconi 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
28303a3180eSLorenzo Bianconi 	u32 val = *ib2;
284ba37b7caSFelix Fietkau 
285a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(eth)) {
28603a3180eSLorenzo Bianconi 		val &= ~MTK_FOE_IB2_DEST_PORT_V2;
28703a3180eSLorenzo Bianconi 		val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port);
28803a3180eSLorenzo Bianconi 	} else {
289ba37b7caSFelix Fietkau 		val &= ~MTK_FOE_IB2_DEST_PORT;
290ba37b7caSFelix Fietkau 		val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
29103a3180eSLorenzo Bianconi 	}
292ba37b7caSFelix Fietkau 	*ib2 = val;
293ba37b7caSFelix Fietkau 
294ba37b7caSFelix Fietkau 	return 0;
295ba37b7caSFelix Fietkau }
296ba37b7caSFelix Fietkau 
mtk_foe_entry_set_ipv4_tuple(struct mtk_eth * eth,struct mtk_foe_entry * entry,bool egress,__be32 src_addr,__be16 src_port,__be32 dest_addr,__be16 dest_port)29703a3180eSLorenzo Bianconi int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth,
29803a3180eSLorenzo Bianconi 				 struct mtk_foe_entry *entry, bool egress,
299ba37b7caSFelix Fietkau 				 __be32 src_addr, __be16 src_port,
300ba37b7caSFelix Fietkau 				 __be32 dest_addr, __be16 dest_port)
301ba37b7caSFelix Fietkau {
30203a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
303ba37b7caSFelix Fietkau 	struct mtk_ipv4_tuple *t;
304ba37b7caSFelix Fietkau 
305ba37b7caSFelix Fietkau 	switch (type) {
306ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
307ba37b7caSFelix Fietkau 		if (egress) {
308ba37b7caSFelix Fietkau 			t = &entry->ipv4.new;
309ba37b7caSFelix Fietkau 			break;
310ba37b7caSFelix Fietkau 		}
311ba37b7caSFelix Fietkau 		fallthrough;
312ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
313ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
314ba37b7caSFelix Fietkau 		t = &entry->ipv4.orig;
315ba37b7caSFelix Fietkau 		break;
316ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_6RD:
317ba37b7caSFelix Fietkau 		entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
318ba37b7caSFelix Fietkau 		entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
319ba37b7caSFelix Fietkau 		return 0;
320ba37b7caSFelix Fietkau 	default:
321ba37b7caSFelix Fietkau 		WARN_ON_ONCE(1);
322ba37b7caSFelix Fietkau 		return -EINVAL;
323ba37b7caSFelix Fietkau 	}
324ba37b7caSFelix Fietkau 
325ba37b7caSFelix Fietkau 	t->src_ip = be32_to_cpu(src_addr);
326ba37b7caSFelix Fietkau 	t->dest_ip = be32_to_cpu(dest_addr);
327ba37b7caSFelix Fietkau 
328ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
329ba37b7caSFelix Fietkau 		return 0;
330ba37b7caSFelix Fietkau 
331ba37b7caSFelix Fietkau 	t->src_port = be16_to_cpu(src_port);
332ba37b7caSFelix Fietkau 	t->dest_port = be16_to_cpu(dest_port);
333ba37b7caSFelix Fietkau 
334ba37b7caSFelix Fietkau 	return 0;
335ba37b7caSFelix Fietkau }
336ba37b7caSFelix Fietkau 
mtk_foe_entry_set_ipv6_tuple(struct mtk_eth * eth,struct mtk_foe_entry * entry,__be32 * src_addr,__be16 src_port,__be32 * dest_addr,__be16 dest_port)33703a3180eSLorenzo Bianconi int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth,
33803a3180eSLorenzo Bianconi 				 struct mtk_foe_entry *entry,
339ba37b7caSFelix Fietkau 				 __be32 *src_addr, __be16 src_port,
340ba37b7caSFelix Fietkau 				 __be32 *dest_addr, __be16 dest_port)
341ba37b7caSFelix Fietkau {
34203a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
343ba37b7caSFelix Fietkau 	u32 *src, *dest;
344ba37b7caSFelix Fietkau 
345ba37b7caSFelix Fietkau 	switch (type) {
346ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
347ba37b7caSFelix Fietkau 		src = entry->dslite.tunnel_src_ip;
348ba37b7caSFelix Fietkau 		dest = entry->dslite.tunnel_dest_ip;
349ba37b7caSFelix Fietkau 		break;
350ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
351ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_6RD:
352ba37b7caSFelix Fietkau 		entry->ipv6.src_port = be16_to_cpu(src_port);
353ba37b7caSFelix Fietkau 		entry->ipv6.dest_port = be16_to_cpu(dest_port);
354ba37b7caSFelix Fietkau 		fallthrough;
355ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
356ba37b7caSFelix Fietkau 		src = entry->ipv6.src_ip;
357ba37b7caSFelix Fietkau 		dest = entry->ipv6.dest_ip;
358ba37b7caSFelix Fietkau 		break;
359ba37b7caSFelix Fietkau 	default:
360ba37b7caSFelix Fietkau 		WARN_ON_ONCE(1);
361ba37b7caSFelix Fietkau 		return -EINVAL;
3623b2c32f9SQiheng Lin 	}
363ba37b7caSFelix Fietkau 
364*b908c722SSimon Horman 	ipv6_addr_be32_to_cpu(src, src_addr);
365*b908c722SSimon Horman 	ipv6_addr_be32_to_cpu(dest, dest_addr);
366ba37b7caSFelix Fietkau 
367ba37b7caSFelix Fietkau 	return 0;
368ba37b7caSFelix Fietkau }
369ba37b7caSFelix Fietkau 
mtk_foe_entry_set_dsa(struct mtk_eth * eth,struct mtk_foe_entry * entry,int port)37003a3180eSLorenzo Bianconi int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry,
37103a3180eSLorenzo Bianconi 			  int port)
372ba37b7caSFelix Fietkau {
37303a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
374ba37b7caSFelix Fietkau 
375ba37b7caSFelix Fietkau 	l2->etype = BIT(port);
376ba37b7caSFelix Fietkau 
37703a3180eSLorenzo Bianconi 	if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)))
37803a3180eSLorenzo Bianconi 		entry->ib1 |= mtk_prep_ib1_vlan_layer(eth, 1);
379ba37b7caSFelix Fietkau 	else
380ba37b7caSFelix Fietkau 		l2->etype |= BIT(8);
381ba37b7caSFelix Fietkau 
38203a3180eSLorenzo Bianconi 	entry->ib1 &= ~mtk_get_ib1_vlan_tag_mask(eth);
383ba37b7caSFelix Fietkau 
384ba37b7caSFelix Fietkau 	return 0;
385ba37b7caSFelix Fietkau }
386ba37b7caSFelix Fietkau 
mtk_foe_entry_set_vlan(struct mtk_eth * eth,struct mtk_foe_entry * entry,int vid)38703a3180eSLorenzo Bianconi int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry,
38803a3180eSLorenzo Bianconi 			   int vid)
389ba37b7caSFelix Fietkau {
39003a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
391ba37b7caSFelix Fietkau 
392fb7da771SDaniel Golle 	switch (mtk_get_ib1_vlan_layer(eth, entry->ib1)) {
393ba37b7caSFelix Fietkau 	case 0:
39403a3180eSLorenzo Bianconi 		entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) |
39503a3180eSLorenzo Bianconi 			      mtk_prep_ib1_vlan_layer(eth, 1);
396ba37b7caSFelix Fietkau 		l2->vlan1 = vid;
397ba37b7caSFelix Fietkau 		return 0;
398ba37b7caSFelix Fietkau 	case 1:
39903a3180eSLorenzo Bianconi 		if (!(entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) {
400ba37b7caSFelix Fietkau 			l2->vlan1 = vid;
401ba37b7caSFelix Fietkau 			l2->etype |= BIT(8);
402ba37b7caSFelix Fietkau 		} else {
403ba37b7caSFelix Fietkau 			l2->vlan2 = vid;
40403a3180eSLorenzo Bianconi 			entry->ib1 += mtk_prep_ib1_vlan_layer(eth, 1);
405ba37b7caSFelix Fietkau 		}
406ba37b7caSFelix Fietkau 		return 0;
407ba37b7caSFelix Fietkau 	default:
408ba37b7caSFelix Fietkau 		return -ENOSPC;
409ba37b7caSFelix Fietkau 	}
410ba37b7caSFelix Fietkau }
411ba37b7caSFelix Fietkau 
mtk_foe_entry_set_pppoe(struct mtk_eth * eth,struct mtk_foe_entry * entry,int sid)41203a3180eSLorenzo Bianconi int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
41303a3180eSLorenzo Bianconi 			    int sid)
414ba37b7caSFelix Fietkau {
41503a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
416ba37b7caSFelix Fietkau 
41703a3180eSLorenzo Bianconi 	if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)) ||
41803a3180eSLorenzo Bianconi 	    (entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth)))
419ba37b7caSFelix Fietkau 		l2->etype = ETH_P_PPP_SES;
420ba37b7caSFelix Fietkau 
42103a3180eSLorenzo Bianconi 	entry->ib1 |= mtk_get_ib1_ppoe_mask(eth);
422ba37b7caSFelix Fietkau 	l2->pppoe_id = sid;
423ba37b7caSFelix Fietkau 
424ba37b7caSFelix Fietkau 	return 0;
425ba37b7caSFelix Fietkau }
426ba37b7caSFelix Fietkau 
mtk_foe_entry_set_wdma(struct mtk_eth * eth,struct mtk_foe_entry * entry,int wdma_idx,int txq,int bss,int wcid,bool amsdu_en)42703a3180eSLorenzo Bianconi int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
428b230812bSSujuan Chen 			   int wdma_idx, int txq, int bss, int wcid,
429b230812bSSujuan Chen 			   bool amsdu_en)
430a333215eSFelix Fietkau {
43103a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
43203a3180eSLorenzo Bianconi 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
433a333215eSFelix Fietkau 
43488efedf5SLorenzo Bianconi 	switch (eth->soc->version) {
43588efedf5SLorenzo Bianconi 	case 3:
43688efedf5SLorenzo Bianconi 		*ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
43788efedf5SLorenzo Bianconi 		*ib2 |=  FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
43888efedf5SLorenzo Bianconi 			 MTK_FOE_IB2_WDMA_WINFO_V2;
43988efedf5SLorenzo Bianconi 		l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) |
44088efedf5SLorenzo Bianconi 			     FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss);
441b230812bSSujuan Chen 		l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en);
44288efedf5SLorenzo Bianconi 		break;
44388efedf5SLorenzo Bianconi 	case 2:
44403a3180eSLorenzo Bianconi 		*ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
44503a3180eSLorenzo Bianconi 		*ib2 |=  FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
44603a3180eSLorenzo Bianconi 			 MTK_FOE_IB2_WDMA_WINFO_V2;
44703a3180eSLorenzo Bianconi 		l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) |
44803a3180eSLorenzo Bianconi 			    FIELD_PREP(MTK_FOE_WINFO_BSS, bss);
44988efedf5SLorenzo Bianconi 		break;
45088efedf5SLorenzo Bianconi 	default:
451a333215eSFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_PORT_MG;
452a333215eSFelix Fietkau 		*ib2 |= MTK_FOE_IB2_WDMA_WINFO;
453a333215eSFelix Fietkau 		if (wdma_idx)
454a333215eSFelix Fietkau 			*ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
455a333215eSFelix Fietkau 		l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
456a333215eSFelix Fietkau 			    FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
457a333215eSFelix Fietkau 			    FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
45888efedf5SLorenzo Bianconi 		break;
45903a3180eSLorenzo Bianconi 	}
460a333215eSFelix Fietkau 
461a333215eSFelix Fietkau 	return 0;
462a333215eSFelix Fietkau }
463a333215eSFelix Fietkau 
mtk_foe_entry_set_queue(struct mtk_eth * eth,struct mtk_foe_entry * entry,unsigned int queue)4648bd8dcc5SFelix Fietkau int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
4658bd8dcc5SFelix Fietkau 			    unsigned int queue)
4668bd8dcc5SFelix Fietkau {
4678bd8dcc5SFelix Fietkau 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
4688bd8dcc5SFelix Fietkau 
469a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(eth)) {
4708bd8dcc5SFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_QID_V2;
4718bd8dcc5SFelix Fietkau 		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue);
4728bd8dcc5SFelix Fietkau 		*ib2 |= MTK_FOE_IB2_PSE_QOS_V2;
4738bd8dcc5SFelix Fietkau 	} else {
4748bd8dcc5SFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_QID;
4758bd8dcc5SFelix Fietkau 		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, queue);
4768bd8dcc5SFelix Fietkau 		*ib2 |= MTK_FOE_IB2_PSE_QOS;
4778bd8dcc5SFelix Fietkau 	}
4788bd8dcc5SFelix Fietkau 
4798bd8dcc5SFelix Fietkau 	return 0;
4808bd8dcc5SFelix Fietkau }
4818bd8dcc5SFelix Fietkau 
482c4f033d9SFelix Fietkau static bool
mtk_flow_entry_match(struct mtk_eth * eth,struct mtk_flow_entry * entry,struct mtk_foe_entry * data)48303a3180eSLorenzo Bianconi mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
48403a3180eSLorenzo Bianconi 		     struct mtk_foe_entry *data)
485c4f033d9SFelix Fietkau {
486c4f033d9SFelix Fietkau 	int type, len;
487c4f033d9SFelix Fietkau 
488c4f033d9SFelix Fietkau 	if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
489c4f033d9SFelix Fietkau 		return false;
490c4f033d9SFelix Fietkau 
49103a3180eSLorenzo Bianconi 	type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
492c4f033d9SFelix Fietkau 	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
493c4f033d9SFelix Fietkau 		len = offsetof(struct mtk_foe_entry, ipv6._rsv);
494c4f033d9SFelix Fietkau 	else
495c4f033d9SFelix Fietkau 		len = offsetof(struct mtk_foe_entry, ipv4.ib2);
496c4f033d9SFelix Fietkau 
497c4f033d9SFelix Fietkau 	return !memcmp(&entry->data.data, &data->data, len - 4);
498c4f033d9SFelix Fietkau }
499c4f033d9SFelix Fietkau 
500c4f033d9SFelix Fietkau static void
__mtk_foe_entry_clear(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)50133fc42deSFelix Fietkau __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
50233fc42deSFelix Fietkau {
50333fc42deSFelix Fietkau 	struct hlist_head *head;
50433fc42deSFelix Fietkau 	struct hlist_node *tmp;
50533fc42deSFelix Fietkau 
50633fc42deSFelix Fietkau 	if (entry->type == MTK_FLOW_TYPE_L2) {
50733fc42deSFelix Fietkau 		rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
50833fc42deSFelix Fietkau 				       mtk_flow_l2_ht_params);
50933fc42deSFelix Fietkau 
51033fc42deSFelix Fietkau 		head = &entry->l2_flows;
51133fc42deSFelix Fietkau 		hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
51233fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, entry);
51333fc42deSFelix Fietkau 		return;
51433fc42deSFelix Fietkau 	}
51533fc42deSFelix Fietkau 
51633fc42deSFelix Fietkau 	hlist_del_init(&entry->list);
51733fc42deSFelix Fietkau 	if (entry->hash != 0xffff) {
5189d8cb4c0SLorenzo Bianconi 		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
5199d8cb4c0SLorenzo Bianconi 
5209d8cb4c0SLorenzo Bianconi 		hwe->ib1 &= ~MTK_FOE_IB1_STATE;
521e52f7c1dSJakub Kicinski 		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
52233fc42deSFelix Fietkau 		dma_wmb();
52392453132SFelix Fietkau 		mtk_ppe_cache_clear(ppe);
52479548b79SJakub Kicinski 
5253fbe4d8cSDaniel Golle 		if (ppe->accounting) {
5263fbe4d8cSDaniel Golle 			struct mtk_foe_accounting *acct;
5273fbe4d8cSDaniel Golle 
5283fbe4d8cSDaniel Golle 			acct = ppe->acct_table + entry->hash * sizeof(*acct);
5293fbe4d8cSDaniel Golle 			acct->packets = 0;
5303fbe4d8cSDaniel Golle 			acct->bytes = 0;
5313fbe4d8cSDaniel Golle 		}
53233fc42deSFelix Fietkau 	}
53333fc42deSFelix Fietkau 	entry->hash = 0xffff;
53433fc42deSFelix Fietkau 
53533fc42deSFelix Fietkau 	if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
53633fc42deSFelix Fietkau 		return;
53733fc42deSFelix Fietkau 
53833fc42deSFelix Fietkau 	hlist_del_init(&entry->l2_data.list);
53933fc42deSFelix Fietkau 	kfree(entry);
54033fc42deSFelix Fietkau }
54133fc42deSFelix Fietkau 
__mtk_foe_entry_idle_time(struct mtk_ppe * ppe,u32 ib1)54233fc42deSFelix Fietkau static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
54333fc42deSFelix Fietkau {
54403a3180eSLorenzo Bianconi 	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
54503a3180eSLorenzo Bianconi 	u16 now = mtk_eth_timestamp(ppe->eth);
54603a3180eSLorenzo Bianconi 	u16 timestamp = ib1 & ib1_ts_mask;
54733fc42deSFelix Fietkau 
54833fc42deSFelix Fietkau 	if (timestamp > now)
54903a3180eSLorenzo Bianconi 		return ib1_ts_mask + 1 - timestamp + now;
55033fc42deSFelix Fietkau 	else
55133fc42deSFelix Fietkau 		return now - timestamp;
55233fc42deSFelix Fietkau }
55333fc42deSFelix Fietkau 
55433fc42deSFelix Fietkau static void
mtk_flow_entry_update_l2(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)55533fc42deSFelix Fietkau mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
55633fc42deSFelix Fietkau {
55703a3180eSLorenzo Bianconi 	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
55833fc42deSFelix Fietkau 	struct mtk_flow_entry *cur;
55933fc42deSFelix Fietkau 	struct mtk_foe_entry *hwe;
56033fc42deSFelix Fietkau 	struct hlist_node *tmp;
56133fc42deSFelix Fietkau 	int idle;
56233fc42deSFelix Fietkau 
56333fc42deSFelix Fietkau 	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
56433fc42deSFelix Fietkau 	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
56533fc42deSFelix Fietkau 		int cur_idle;
56633fc42deSFelix Fietkau 		u32 ib1;
56733fc42deSFelix Fietkau 
5689d8cb4c0SLorenzo Bianconi 		hwe = mtk_foe_get_entry(ppe, cur->hash);
56933fc42deSFelix Fietkau 		ib1 = READ_ONCE(hwe->ib1);
57033fc42deSFelix Fietkau 
57133fc42deSFelix Fietkau 		if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
57233fc42deSFelix Fietkau 			cur->hash = 0xffff;
57333fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, cur);
57433fc42deSFelix Fietkau 			continue;
57533fc42deSFelix Fietkau 		}
57633fc42deSFelix Fietkau 
57733fc42deSFelix Fietkau 		cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
57833fc42deSFelix Fietkau 		if (cur_idle >= idle)
57933fc42deSFelix Fietkau 			continue;
58033fc42deSFelix Fietkau 
58133fc42deSFelix Fietkau 		idle = cur_idle;
58203a3180eSLorenzo Bianconi 		entry->data.ib1 &= ~ib1_ts_mask;
58304172043Slinke li 		entry->data.ib1 |= ib1 & ib1_ts_mask;
58433fc42deSFelix Fietkau 	}
58533fc42deSFelix Fietkau }
58633fc42deSFelix Fietkau 
58733fc42deSFelix Fietkau static void
mtk_flow_entry_update(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)588c4f033d9SFelix Fietkau mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
589ba37b7caSFelix Fietkau {
5909d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry foe = {};
591ba37b7caSFelix Fietkau 	struct mtk_foe_entry *hwe;
592ba37b7caSFelix Fietkau 
593c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
59433fc42deSFelix Fietkau 
59533fc42deSFelix Fietkau 	if (entry->type == MTK_FLOW_TYPE_L2) {
59633fc42deSFelix Fietkau 		mtk_flow_entry_update_l2(ppe, entry);
59733fc42deSFelix Fietkau 		goto out;
59833fc42deSFelix Fietkau 	}
59933fc42deSFelix Fietkau 
600c4f033d9SFelix Fietkau 	if (entry->hash == 0xffff)
601c4f033d9SFelix Fietkau 		goto out;
602c4f033d9SFelix Fietkau 
6039d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, entry->hash);
6049d8cb4c0SLorenzo Bianconi 	memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
60503a3180eSLorenzo Bianconi 	if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
606c4f033d9SFelix Fietkau 		entry->hash = 0xffff;
607c4f033d9SFelix Fietkau 		goto out;
608c4f033d9SFelix Fietkau 	}
609c4f033d9SFelix Fietkau 
610c4f033d9SFelix Fietkau 	entry->data.ib1 = foe.ib1;
611c4f033d9SFelix Fietkau 
612c4f033d9SFelix Fietkau out:
613c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
614c4f033d9SFelix Fietkau }
615c4f033d9SFelix Fietkau 
616c4f033d9SFelix Fietkau static void
__mtk_foe_entry_commit(struct mtk_ppe * ppe,struct mtk_foe_entry * entry,u16 hash)617c4f033d9SFelix Fietkau __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
618c4f033d9SFelix Fietkau 		       u16 hash)
619c4f033d9SFelix Fietkau {
62003a3180eSLorenzo Bianconi 	struct mtk_eth *eth = ppe->eth;
62103a3180eSLorenzo Bianconi 	u16 timestamp = mtk_eth_timestamp(eth);
622c4f033d9SFelix Fietkau 	struct mtk_foe_entry *hwe;
6234eaeca1fSFelix Fietkau 	u32 val;
624c4f033d9SFelix Fietkau 
625a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(eth)) {
62603a3180eSLorenzo Bianconi 		entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
62703a3180eSLorenzo Bianconi 		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2,
62803a3180eSLorenzo Bianconi 					 timestamp);
62903a3180eSLorenzo Bianconi 	} else {
630ba37b7caSFelix Fietkau 		entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
63103a3180eSLorenzo Bianconi 		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP,
63203a3180eSLorenzo Bianconi 					 timestamp);
63303a3180eSLorenzo Bianconi 	}
634ba37b7caSFelix Fietkau 
6359d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, hash);
636454b20e1SDaniel Golle 	memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size - sizeof(hwe->ib1));
637ba37b7caSFelix Fietkau 	wmb();
638ba37b7caSFelix Fietkau 	hwe->ib1 = entry->ib1;
639ba37b7caSFelix Fietkau 
6404eaeca1fSFelix Fietkau 	if (ppe->accounting) {
641a008e2a8SLorenzo Bianconi 		if (mtk_is_netsys_v2_or_greater(eth))
6424eaeca1fSFelix Fietkau 			val = MTK_FOE_IB2_MIB_CNT_V2;
6434eaeca1fSFelix Fietkau 		else
6444eaeca1fSFelix Fietkau 			val = MTK_FOE_IB2_MIB_CNT;
6454eaeca1fSFelix Fietkau 		*mtk_foe_entry_ib2(eth, hwe) |= val;
6464eaeca1fSFelix Fietkau 	}
6473fbe4d8cSDaniel Golle 
648ba37b7caSFelix Fietkau 	dma_wmb();
649ba37b7caSFelix Fietkau 
650ba37b7caSFelix Fietkau 	mtk_ppe_cache_clear(ppe);
651ba37b7caSFelix Fietkau }
652ba37b7caSFelix Fietkau 
mtk_foe_entry_clear(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)653c4f033d9SFelix Fietkau void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
654c4f033d9SFelix Fietkau {
655c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
65633fc42deSFelix Fietkau 	__mtk_foe_entry_clear(ppe, entry);
657c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
658c4f033d9SFelix Fietkau }
659c4f033d9SFelix Fietkau 
66033fc42deSFelix Fietkau static int
mtk_foe_entry_commit_l2(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)66133fc42deSFelix Fietkau mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
66233fc42deSFelix Fietkau {
663e2853114SFelix Fietkau 	struct mtk_flow_entry *prev;
664e2853114SFelix Fietkau 
66533fc42deSFelix Fietkau 	entry->type = MTK_FLOW_TYPE_L2;
66633fc42deSFelix Fietkau 
667e2853114SFelix Fietkau 	prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &entry->l2_node,
66833fc42deSFelix Fietkau 						 mtk_flow_l2_ht_params);
669e2853114SFelix Fietkau 	if (likely(!prev))
670e2853114SFelix Fietkau 		return 0;
671e2853114SFelix Fietkau 
672e2853114SFelix Fietkau 	if (IS_ERR(prev))
673e2853114SFelix Fietkau 		return PTR_ERR(prev);
674e2853114SFelix Fietkau 
675e2853114SFelix Fietkau 	return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node,
676e2853114SFelix Fietkau 				       &entry->l2_node, mtk_flow_l2_ht_params);
67733fc42deSFelix Fietkau }
67833fc42deSFelix Fietkau 
mtk_foe_entry_commit(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)679c4f033d9SFelix Fietkau int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
680c4f033d9SFelix Fietkau {
681ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
68203a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(ppe->eth, entry->data.ib1);
68333fc42deSFelix Fietkau 	u32 hash;
684c4f033d9SFelix Fietkau 
68533fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
68633fc42deSFelix Fietkau 		return mtk_foe_entry_commit_l2(ppe, entry);
68733fc42deSFelix Fietkau 
688ba2fc48cSLorenzo Bianconi 	hash = mtk_ppe_hash_entry(ppe->eth, &entry->data);
689c4f033d9SFelix Fietkau 	entry->hash = 0xffff;
690c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
691ba2fc48cSLorenzo Bianconi 	hlist_add_head(&entry->list, &ppe->foe_flow[hash / soc->hash_offset]);
692c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
693c4f033d9SFelix Fietkau 
694c4f033d9SFelix Fietkau 	return 0;
695c4f033d9SFelix Fietkau }
696c4f033d9SFelix Fietkau 
69733fc42deSFelix Fietkau static void
mtk_foe_entry_commit_subflow(struct mtk_ppe * ppe,struct mtk_flow_entry * entry,u16 hash)69833fc42deSFelix Fietkau mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
69933fc42deSFelix Fietkau 			     u16 hash)
70033fc42deSFelix Fietkau {
701ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
70233fc42deSFelix Fietkau 	struct mtk_flow_entry *flow_info;
7039d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry foe = {}, *hwe;
70433fc42deSFelix Fietkau 	struct mtk_foe_mac_info *l2;
70503a3180eSLorenzo Bianconi 	u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
70633fc42deSFelix Fietkau 	int type;
70733fc42deSFelix Fietkau 
708f3eceaedSKees Cook 	flow_info = kzalloc(sizeof(*flow_info), GFP_ATOMIC);
70933fc42deSFelix Fietkau 	if (!flow_info)
71033fc42deSFelix Fietkau 		return;
71133fc42deSFelix Fietkau 
71233fc42deSFelix Fietkau 	flow_info->l2_data.base_flow = entry;
71333fc42deSFelix Fietkau 	flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
71433fc42deSFelix Fietkau 	flow_info->hash = hash;
715ba2fc48cSLorenzo Bianconi 	hlist_add_head(&flow_info->list,
716ba2fc48cSLorenzo Bianconi 		       &ppe->foe_flow[hash / soc->hash_offset]);
71733fc42deSFelix Fietkau 	hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
71833fc42deSFelix Fietkau 
7199d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, hash);
7209d8cb4c0SLorenzo Bianconi 	memcpy(&foe, hwe, soc->foe_entry_size);
72133fc42deSFelix Fietkau 	foe.ib1 &= ib1_mask;
72233fc42deSFelix Fietkau 	foe.ib1 |= entry->data.ib1 & ~ib1_mask;
72333fc42deSFelix Fietkau 
72403a3180eSLorenzo Bianconi 	l2 = mtk_foe_entry_l2(ppe->eth, &foe);
72533fc42deSFelix Fietkau 	memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
72633fc42deSFelix Fietkau 
72703a3180eSLorenzo Bianconi 	type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
72833fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
72933fc42deSFelix Fietkau 		memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
73033fc42deSFelix Fietkau 	else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
73133fc42deSFelix Fietkau 		l2->etype = ETH_P_IPV6;
73233fc42deSFelix Fietkau 
73303a3180eSLorenzo Bianconi 	*mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
73433fc42deSFelix Fietkau 
73533fc42deSFelix Fietkau 	__mtk_foe_entry_commit(ppe, &foe, hash);
73633fc42deSFelix Fietkau }
73733fc42deSFelix Fietkau 
__mtk_ppe_check_skb(struct mtk_ppe * ppe,struct sk_buff * skb,u16 hash)738c4f033d9SFelix Fietkau void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
739c4f033d9SFelix Fietkau {
740ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
741ba2fc48cSLorenzo Bianconi 	struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset];
7429d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
74333fc42deSFelix Fietkau 	struct mtk_flow_entry *entry;
74433fc42deSFelix Fietkau 	struct mtk_foe_bridge key = {};
74517a5f6a7SDan Carpenter 	struct hlist_node *n;
74633fc42deSFelix Fietkau 	struct ethhdr *eh;
747c4f033d9SFelix Fietkau 	bool found = false;
74833fc42deSFelix Fietkau 	u8 *tag;
749c4f033d9SFelix Fietkau 
750c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
75133fc42deSFelix Fietkau 
75233fc42deSFelix Fietkau 	if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
75333fc42deSFelix Fietkau 		goto out;
75433fc42deSFelix Fietkau 
75517a5f6a7SDan Carpenter 	hlist_for_each_entry_safe(entry, n, head, list) {
75633fc42deSFelix Fietkau 		if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
75733fc42deSFelix Fietkau 			if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
75833fc42deSFelix Fietkau 				     MTK_FOE_STATE_BIND))
75933fc42deSFelix Fietkau 				continue;
76033fc42deSFelix Fietkau 
76133fc42deSFelix Fietkau 			entry->hash = 0xffff;
76233fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, entry);
76333fc42deSFelix Fietkau 			continue;
76433fc42deSFelix Fietkau 		}
76533fc42deSFelix Fietkau 
76603a3180eSLorenzo Bianconi 		if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
767c4f033d9SFelix Fietkau 			if (entry->hash != 0xffff)
768c4f033d9SFelix Fietkau 				entry->hash = 0xffff;
769c4f033d9SFelix Fietkau 			continue;
770c4f033d9SFelix Fietkau 		}
771c4f033d9SFelix Fietkau 
772c4f033d9SFelix Fietkau 		entry->hash = hash;
773c4f033d9SFelix Fietkau 		__mtk_foe_entry_commit(ppe, &entry->data, hash);
774c4f033d9SFelix Fietkau 		found = true;
775c4f033d9SFelix Fietkau 	}
77633fc42deSFelix Fietkau 
77733fc42deSFelix Fietkau 	if (found)
77833fc42deSFelix Fietkau 		goto out;
77933fc42deSFelix Fietkau 
78033fc42deSFelix Fietkau 	eh = eth_hdr(skb);
78133fc42deSFelix Fietkau 	ether_addr_copy(key.dest_mac, eh->h_dest);
78233fc42deSFelix Fietkau 	ether_addr_copy(key.src_mac, eh->h_source);
78333fc42deSFelix Fietkau 	tag = skb->data - 2;
78433fc42deSFelix Fietkau 	key.vlan = 0;
78533fc42deSFelix Fietkau 	switch (skb->protocol) {
78633fc42deSFelix Fietkau #if IS_ENABLED(CONFIG_NET_DSA)
78733fc42deSFelix Fietkau 	case htons(ETH_P_XDSA):
78833fc42deSFelix Fietkau 		if (!netdev_uses_dsa(skb->dev) ||
78933fc42deSFelix Fietkau 		    skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
79033fc42deSFelix Fietkau 			goto out;
79133fc42deSFelix Fietkau 
7925f36ca1bSFelix Fietkau 		if (!skb_metadata_dst(skb))
79333fc42deSFelix Fietkau 			tag += 4;
7945f36ca1bSFelix Fietkau 
79533fc42deSFelix Fietkau 		if (get_unaligned_be16(tag) != ETH_P_8021Q)
79633fc42deSFelix Fietkau 			break;
79733fc42deSFelix Fietkau 
79833fc42deSFelix Fietkau 		fallthrough;
79933fc42deSFelix Fietkau #endif
80033fc42deSFelix Fietkau 	case htons(ETH_P_8021Q):
80133fc42deSFelix Fietkau 		key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
80233fc42deSFelix Fietkau 		break;
80333fc42deSFelix Fietkau 	default:
80433fc42deSFelix Fietkau 		break;
80533fc42deSFelix Fietkau 	}
80633fc42deSFelix Fietkau 
80733fc42deSFelix Fietkau 	entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
80833fc42deSFelix Fietkau 	if (!entry)
80933fc42deSFelix Fietkau 		goto out;
81033fc42deSFelix Fietkau 
81133fc42deSFelix Fietkau 	mtk_foe_entry_commit_subflow(ppe, entry, hash);
81233fc42deSFelix Fietkau 
81333fc42deSFelix Fietkau out:
814c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
815c4f033d9SFelix Fietkau }
816c4f033d9SFelix Fietkau 
mtk_foe_entry_idle_time(struct mtk_ppe * ppe,struct mtk_flow_entry * entry)817c4f033d9SFelix Fietkau int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
818c4f033d9SFelix Fietkau {
819c4f033d9SFelix Fietkau 	mtk_flow_entry_update(ppe, entry);
820c4f033d9SFelix Fietkau 
82133fc42deSFelix Fietkau 	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
822c4f033d9SFelix Fietkau }
823c4f033d9SFelix Fietkau 
mtk_ppe_prepare_reset(struct mtk_ppe * ppe)82406127504SLorenzo Bianconi int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
82506127504SLorenzo Bianconi {
82606127504SLorenzo Bianconi 	if (!ppe)
82706127504SLorenzo Bianconi 		return -EINVAL;
82806127504SLorenzo Bianconi 
82906127504SLorenzo Bianconi 	/* disable KA */
83006127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
83106127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
83206127504SLorenzo Bianconi 	ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0);
83306127504SLorenzo Bianconi 	usleep_range(10000, 11000);
83406127504SLorenzo Bianconi 
83506127504SLorenzo Bianconi 	/* set KA timer to maximum */
83606127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
83706127504SLorenzo Bianconi 	ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0xffffffff);
83806127504SLorenzo Bianconi 
83906127504SLorenzo Bianconi 	/* set KA tick select */
84006127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_TICK_SEL);
84106127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
84206127504SLorenzo Bianconi 	usleep_range(10000, 11000);
84306127504SLorenzo Bianconi 
84406127504SLorenzo Bianconi 	/* disable scan mode */
84506127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_SCAN_MODE);
84606127504SLorenzo Bianconi 	usleep_range(10000, 11000);
84706127504SLorenzo Bianconi 
84806127504SLorenzo Bianconi 	return mtk_ppe_wait_busy(ppe);
84906127504SLorenzo Bianconi }
85006127504SLorenzo Bianconi 
mtk_foe_entry_get_mib(struct mtk_ppe * ppe,u32 index,struct mtk_foe_accounting * diff)8513fbe4d8cSDaniel Golle struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
8523fbe4d8cSDaniel Golle 						 struct mtk_foe_accounting *diff)
853ba37b7caSFelix Fietkau {
8543fbe4d8cSDaniel Golle 	struct mtk_foe_accounting *acct;
8553fbe4d8cSDaniel Golle 	int size = sizeof(struct mtk_foe_accounting);
8563fbe4d8cSDaniel Golle 	u64 bytes, packets;
8573fbe4d8cSDaniel Golle 
8583fbe4d8cSDaniel Golle 	if (!ppe->accounting)
8593fbe4d8cSDaniel Golle 		return NULL;
8603fbe4d8cSDaniel Golle 
8613fbe4d8cSDaniel Golle 	if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
8623fbe4d8cSDaniel Golle 		return NULL;
8633fbe4d8cSDaniel Golle 
8643fbe4d8cSDaniel Golle 	acct = ppe->acct_table + index * size;
8653fbe4d8cSDaniel Golle 
8663fbe4d8cSDaniel Golle 	acct->bytes += bytes;
8673fbe4d8cSDaniel Golle 	acct->packets += packets;
8683fbe4d8cSDaniel Golle 
8693fbe4d8cSDaniel Golle 	if (diff) {
8703fbe4d8cSDaniel Golle 		diff->bytes = bytes;
8713fbe4d8cSDaniel Golle 		diff->packets = packets;
8723fbe4d8cSDaniel Golle 	}
8733fbe4d8cSDaniel Golle 
8743fbe4d8cSDaniel Golle 	return acct;
8753fbe4d8cSDaniel Golle }
8763fbe4d8cSDaniel Golle 
mtk_ppe_init(struct mtk_eth * eth,void __iomem * base,int index)8773fbe4d8cSDaniel Golle struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
8783fbe4d8cSDaniel Golle {
8793fbe4d8cSDaniel Golle 	bool accounting = eth->soc->has_accounting;
880ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = eth->soc;
8813fbe4d8cSDaniel Golle 	struct mtk_foe_accounting *acct;
882c4f033d9SFelix Fietkau 	struct device *dev = eth->dev;
8833fbe4d8cSDaniel Golle 	struct mtk_mib_entry *mib;
8841ccc723bSFelix Fietkau 	struct mtk_ppe *ppe;
885ba2fc48cSLorenzo Bianconi 	u32 foe_flow_size;
8869d8cb4c0SLorenzo Bianconi 	void *foe;
8871ccc723bSFelix Fietkau 
8881ccc723bSFelix Fietkau 	ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
8891ccc723bSFelix Fietkau 	if (!ppe)
8901ccc723bSFelix Fietkau 		return NULL;
891ba37b7caSFelix Fietkau 
89233fc42deSFelix Fietkau 	rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
89333fc42deSFelix Fietkau 
894ba37b7caSFelix Fietkau 	/* need to allocate a separate device, since it PPE DMA access is
895ba37b7caSFelix Fietkau 	 * not coherent.
896ba37b7caSFelix Fietkau 	 */
897ba37b7caSFelix Fietkau 	ppe->base = base;
898c4f033d9SFelix Fietkau 	ppe->eth = eth;
899ba37b7caSFelix Fietkau 	ppe->dev = dev;
9003fbe4d8cSDaniel Golle 	ppe->version = eth->soc->offload_version;
9013fbe4d8cSDaniel Golle 	ppe->accounting = accounting;
902ba37b7caSFelix Fietkau 
9039d8cb4c0SLorenzo Bianconi 	foe = dmam_alloc_coherent(ppe->dev,
9049d8cb4c0SLorenzo Bianconi 				  MTK_PPE_ENTRIES * soc->foe_entry_size,
905ba37b7caSFelix Fietkau 				  &ppe->foe_phys, GFP_KERNEL);
906ba37b7caSFelix Fietkau 	if (!foe)
907603ea5e7SYan Cangang 		goto err_free_l2_flows;
908ba37b7caSFelix Fietkau 
909ba37b7caSFelix Fietkau 	ppe->foe_table = foe;
910ba37b7caSFelix Fietkau 
911ba2fc48cSLorenzo Bianconi 	foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) *
912ba2fc48cSLorenzo Bianconi 			sizeof(*ppe->foe_flow);
913ba2fc48cSLorenzo Bianconi 	ppe->foe_flow = devm_kzalloc(dev, foe_flow_size, GFP_KERNEL);
914ba2fc48cSLorenzo Bianconi 	if (!ppe->foe_flow)
915603ea5e7SYan Cangang 		goto err_free_l2_flows;
916ba2fc48cSLorenzo Bianconi 
9173fbe4d8cSDaniel Golle 	if (accounting) {
9183fbe4d8cSDaniel Golle 		mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
9193fbe4d8cSDaniel Golle 					  &ppe->mib_phys, GFP_KERNEL);
9203fbe4d8cSDaniel Golle 		if (!mib)
9213fbe4d8cSDaniel Golle 			return NULL;
9223fbe4d8cSDaniel Golle 
9233fbe4d8cSDaniel Golle 		ppe->mib_table = mib;
9243fbe4d8cSDaniel Golle 
9253fbe4d8cSDaniel Golle 		acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
9263fbe4d8cSDaniel Golle 				    GFP_KERNEL);
9273fbe4d8cSDaniel Golle 
9283fbe4d8cSDaniel Golle 		if (!acct)
9293fbe4d8cSDaniel Golle 			return NULL;
9303fbe4d8cSDaniel Golle 
9313fbe4d8cSDaniel Golle 		ppe->acct_table = acct;
9323fbe4d8cSDaniel Golle 	}
9333fbe4d8cSDaniel Golle 
9344ff1a3fcSLorenzo Bianconi 	mtk_ppe_debugfs_init(ppe, index);
935ba37b7caSFelix Fietkau 
9361ccc723bSFelix Fietkau 	return ppe;
937603ea5e7SYan Cangang 
938603ea5e7SYan Cangang err_free_l2_flows:
939603ea5e7SYan Cangang 	rhashtable_destroy(&ppe->l2_flows);
940603ea5e7SYan Cangang 	return NULL;
941603ea5e7SYan Cangang }
942603ea5e7SYan Cangang 
mtk_ppe_deinit(struct mtk_eth * eth)943603ea5e7SYan Cangang void mtk_ppe_deinit(struct mtk_eth *eth)
944603ea5e7SYan Cangang {
945603ea5e7SYan Cangang 	int i;
946603ea5e7SYan Cangang 
947603ea5e7SYan Cangang 	for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) {
948603ea5e7SYan Cangang 		if (!eth->ppe[i])
949603ea5e7SYan Cangang 			return;
950603ea5e7SYan Cangang 		rhashtable_destroy(&eth->ppe[i]->l2_flows);
951603ea5e7SYan Cangang 	}
952ba37b7caSFelix Fietkau }
953ba37b7caSFelix Fietkau 
mtk_ppe_init_foe_table(struct mtk_ppe * ppe)954ba37b7caSFelix Fietkau static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
955ba37b7caSFelix Fietkau {
956ba37b7caSFelix Fietkau 	static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
957ba37b7caSFelix Fietkau 	int i, k;
958ba37b7caSFelix Fietkau 
9599d8cb4c0SLorenzo Bianconi 	memset(ppe->foe_table, 0,
9609d8cb4c0SLorenzo Bianconi 	       MTK_PPE_ENTRIES * ppe->eth->soc->foe_entry_size);
961ba37b7caSFelix Fietkau 
962ba37b7caSFelix Fietkau 	if (!IS_ENABLED(CONFIG_SOC_MT7621))
963ba37b7caSFelix Fietkau 		return;
964ba37b7caSFelix Fietkau 
965ba37b7caSFelix Fietkau 	/* skip all entries that cross the 1024 byte boundary */
9669d8cb4c0SLorenzo Bianconi 	for (i = 0; i < MTK_PPE_ENTRIES; i += 128) {
9679d8cb4c0SLorenzo Bianconi 		for (k = 0; k < ARRAY_SIZE(skip); k++) {
9689d8cb4c0SLorenzo Bianconi 			struct mtk_foe_entry *hwe;
9699d8cb4c0SLorenzo Bianconi 
9709d8cb4c0SLorenzo Bianconi 			hwe = mtk_foe_get_entry(ppe, i + skip[k]);
9719d8cb4c0SLorenzo Bianconi 			hwe->ib1 |= MTK_FOE_IB1_STATIC;
9729d8cb4c0SLorenzo Bianconi 		}
9739d8cb4c0SLorenzo Bianconi 	}
974ba37b7caSFelix Fietkau }
975ba37b7caSFelix Fietkau 
mtk_ppe_start(struct mtk_ppe * ppe)9764ff1a3fcSLorenzo Bianconi void mtk_ppe_start(struct mtk_ppe *ppe)
977ba37b7caSFelix Fietkau {
978ba37b7caSFelix Fietkau 	u32 val;
979ba37b7caSFelix Fietkau 
9804ff1a3fcSLorenzo Bianconi 	if (!ppe)
9814ff1a3fcSLorenzo Bianconi 		return;
9824ff1a3fcSLorenzo Bianconi 
983ba37b7caSFelix Fietkau 	mtk_ppe_init_foe_table(ppe);
984ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
985ba37b7caSFelix Fietkau 
98688efedf5SLorenzo Bianconi 	val = MTK_PPE_TB_CFG_AGE_NON_L4 |
987ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UNBIND |
988ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP |
989ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UDP |
990ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
991ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
992ba37b7caSFelix Fietkau 			 MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
993ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
994ba37b7caSFelix Fietkau 			 MTK_PPE_KEEPALIVE_DISABLE) |
995ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
996ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
997ea80e3edSDaniel Golle 			 MTK_PPE_SCAN_MODE_CHECK_AGE) |
998ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
999ba37b7caSFelix Fietkau 			 MTK_PPE_ENTRIES_SHIFT);
1000a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(ppe->eth))
100103a3180eSLorenzo Bianconi 		val |= MTK_PPE_TB_CFG_INFO_SEL;
100288efedf5SLorenzo Bianconi 	if (!mtk_is_netsys_v3_or_greater(ppe->eth))
100388efedf5SLorenzo Bianconi 		val |= MTK_PPE_TB_CFG_ENTRY_80B;
1004ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_TB_CFG, val);
1005ba37b7caSFelix Fietkau 
1006ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
1007ba37b7caSFelix Fietkau 		MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
1008ba37b7caSFelix Fietkau 
1009ba37b7caSFelix Fietkau 	mtk_ppe_cache_enable(ppe, true);
1010ba37b7caSFelix Fietkau 
101103a3180eSLorenzo Bianconi 	val = MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
1012ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
1013ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP6_6RD |
1014ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAT |
1015ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAPT |
1016ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_DSLITE |
1017ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
1018a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(ppe->eth))
101903a3180eSLorenzo Bianconi 		val |= MTK_PPE_MD_TOAP_BYP_CRSN0 |
102003a3180eSLorenzo Bianconi 		       MTK_PPE_MD_TOAP_BYP_CRSN1 |
102103a3180eSLorenzo Bianconi 		       MTK_PPE_MD_TOAP_BYP_CRSN2 |
102203a3180eSLorenzo Bianconi 		       MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY;
102303a3180eSLorenzo Bianconi 	else
102403a3180eSLorenzo Bianconi 		val |= MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
102503a3180eSLorenzo Bianconi 		       MTK_PPE_FLOW_CFG_IP4_UDP_FRAG;
1026ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
1027ba37b7caSFelix Fietkau 
1028ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
1029ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
1030ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
1031ba37b7caSFelix Fietkau 
1032ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) |
1033ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
1034ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
1035ba37b7caSFelix Fietkau 
1036ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
1037ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7);
1038ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
1039ba37b7caSFelix Fietkau 
1040ba37b7caSFelix Fietkau 	val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
1041ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
1042ba37b7caSFelix Fietkau 
1043ba37b7caSFelix Fietkau 	val = MTK_PPE_BIND_LIMIT1_FULL |
1044ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
1045ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
1046ba37b7caSFelix Fietkau 
1047ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
1048ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
1049ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
1050ba37b7caSFelix Fietkau 
1051ba37b7caSFelix Fietkau 	/* enable PPE */
1052ba37b7caSFelix Fietkau 	val = MTK_PPE_GLO_CFG_EN |
1053ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
1054ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_IP4_CS_DROP |
1055ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
1056ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
1057ba37b7caSFelix Fietkau 
1058ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
105903a3180eSLorenzo Bianconi 
1060a008e2a8SLorenzo Bianconi 	if (mtk_is_netsys_v2_or_greater(ppe->eth)) {
106103a3180eSLorenzo Bianconi 		ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
106203a3180eSLorenzo Bianconi 		ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
106303a3180eSLorenzo Bianconi 	}
10643fbe4d8cSDaniel Golle 
10653fbe4d8cSDaniel Golle 	if (ppe->accounting && ppe->mib_phys) {
10663fbe4d8cSDaniel Golle 		ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
10673fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
10683fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_EN);
10693fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
10703fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_RD_CLR);
10713fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
10723fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_RD_CLR);
10733fbe4d8cSDaniel Golle 	}
1074ba37b7caSFelix Fietkau }
1075ba37b7caSFelix Fietkau 
mtk_ppe_stop(struct mtk_ppe * ppe)1076ba37b7caSFelix Fietkau int mtk_ppe_stop(struct mtk_ppe *ppe)
1077ba37b7caSFelix Fietkau {
1078ba37b7caSFelix Fietkau 	u32 val;
1079ba37b7caSFelix Fietkau 	int i;
1080ba37b7caSFelix Fietkau 
10814ff1a3fcSLorenzo Bianconi 	if (!ppe)
10824ff1a3fcSLorenzo Bianconi 		return 0;
10834ff1a3fcSLorenzo Bianconi 
10849d8cb4c0SLorenzo Bianconi 	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
10859d8cb4c0SLorenzo Bianconi 		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, i);
10869d8cb4c0SLorenzo Bianconi 
10879d8cb4c0SLorenzo Bianconi 		hwe->ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
1088ba37b7caSFelix Fietkau 				      MTK_FOE_STATE_INVALID);
10899d8cb4c0SLorenzo Bianconi 	}
1090ba37b7caSFelix Fietkau 
1091ba37b7caSFelix Fietkau 	mtk_ppe_cache_enable(ppe, false);
1092ba37b7caSFelix Fietkau 
1093ba37b7caSFelix Fietkau 	/* disable aging */
1094ba37b7caSFelix Fietkau 	val = MTK_PPE_TB_CFG_AGE_NON_L4 |
1095ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UNBIND |
1096ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP |
1097ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UDP |
1098ea80e3edSDaniel Golle 	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
1099ea80e3edSDaniel Golle 		  MTK_PPE_TB_CFG_SCAN_MODE;
1100ba37b7caSFelix Fietkau 	ppe_clear(ppe, MTK_PPE_TB_CFG, val);
1101ba37b7caSFelix Fietkau 
1102ea80e3edSDaniel Golle 	if (mtk_ppe_wait_busy(ppe))
1103ea80e3edSDaniel Golle 		return -ETIMEDOUT;
1104ea80e3edSDaniel Golle 
1105ea80e3edSDaniel Golle 	/* disable offload engine */
1106ea80e3edSDaniel Golle 	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
1107ea80e3edSDaniel Golle 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
1108ea80e3edSDaniel Golle 
1109ea80e3edSDaniel Golle 	return 0;
1110ba37b7caSFelix Fietkau }
1111