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>
115f36ca1bSFelix Fietkau #include <net/dst_metadata.h>
1233fc42deSFelix Fietkau #include <net/dsa.h>
13c4f033d9SFelix Fietkau #include "mtk_eth_soc.h"
14ba37b7caSFelix Fietkau #include "mtk_ppe.h"
15ba37b7caSFelix Fietkau #include "mtk_ppe_regs.h"
16ba37b7caSFelix Fietkau 
17c4f033d9SFelix Fietkau static DEFINE_SPINLOCK(ppe_lock);
18c4f033d9SFelix Fietkau 
1933fc42deSFelix Fietkau static const struct rhashtable_params mtk_flow_l2_ht_params = {
2033fc42deSFelix Fietkau 	.head_offset = offsetof(struct mtk_flow_entry, l2_node),
2133fc42deSFelix Fietkau 	.key_offset = offsetof(struct mtk_flow_entry, data.bridge),
2233fc42deSFelix Fietkau 	.key_len = offsetof(struct mtk_foe_bridge, key_end),
2333fc42deSFelix Fietkau 	.automatic_shrinking = true,
2433fc42deSFelix Fietkau };
2533fc42deSFelix Fietkau 
26ba37b7caSFelix Fietkau static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
27ba37b7caSFelix Fietkau {
28ba37b7caSFelix Fietkau 	writel(val, ppe->base + reg);
29ba37b7caSFelix Fietkau }
30ba37b7caSFelix Fietkau 
31ba37b7caSFelix Fietkau static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
32ba37b7caSFelix Fietkau {
33ba37b7caSFelix Fietkau 	return readl(ppe->base + reg);
34ba37b7caSFelix Fietkau }
35ba37b7caSFelix Fietkau 
36ba37b7caSFelix Fietkau static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
37ba37b7caSFelix Fietkau {
38ba37b7caSFelix Fietkau 	u32 val;
39ba37b7caSFelix Fietkau 
40ba37b7caSFelix Fietkau 	val = ppe_r32(ppe, reg);
41ba37b7caSFelix Fietkau 	val &= ~mask;
42ba37b7caSFelix Fietkau 	val |= set;
43ba37b7caSFelix Fietkau 	ppe_w32(ppe, reg, val);
44ba37b7caSFelix Fietkau 
45ba37b7caSFelix Fietkau 	return val;
46ba37b7caSFelix Fietkau }
47ba37b7caSFelix Fietkau 
48ba37b7caSFelix Fietkau static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
49ba37b7caSFelix Fietkau {
50ba37b7caSFelix Fietkau 	return ppe_m32(ppe, reg, 0, val);
51ba37b7caSFelix Fietkau }
52ba37b7caSFelix Fietkau 
53ba37b7caSFelix Fietkau static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
54ba37b7caSFelix Fietkau {
55ba37b7caSFelix Fietkau 	return ppe_m32(ppe, reg, val, 0);
56ba37b7caSFelix Fietkau }
57ba37b7caSFelix Fietkau 
58c4f033d9SFelix Fietkau static u32 mtk_eth_timestamp(struct mtk_eth *eth)
59c4f033d9SFelix Fietkau {
6003a3180eSLorenzo Bianconi 	return mtk_r32(eth, 0x0010) & mtk_get_ib1_ts_mask(eth);
61c4f033d9SFelix Fietkau }
62c4f033d9SFelix Fietkau 
63ba37b7caSFelix Fietkau static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
64ba37b7caSFelix Fietkau {
65c5d66587SIlya Lipnitskiy 	int ret;
66c5d66587SIlya Lipnitskiy 	u32 val;
67ba37b7caSFelix Fietkau 
68c5d66587SIlya Lipnitskiy 	ret = readl_poll_timeout(ppe->base + MTK_PPE_GLO_CFG, val,
69c5d66587SIlya Lipnitskiy 				 !(val & MTK_PPE_GLO_CFG_BUSY),
70c5d66587SIlya Lipnitskiy 				 20, MTK_PPE_WAIT_TIMEOUT_US);
71ba37b7caSFelix Fietkau 
72c5d66587SIlya Lipnitskiy 	if (ret)
73ba37b7caSFelix Fietkau 		dev_err(ppe->dev, "PPE table busy");
74ba37b7caSFelix Fietkau 
75c5d66587SIlya Lipnitskiy 	return ret;
76ba37b7caSFelix Fietkau }
77ba37b7caSFelix Fietkau 
783fbe4d8cSDaniel Golle static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
793fbe4d8cSDaniel Golle {
803fbe4d8cSDaniel Golle 	int ret;
813fbe4d8cSDaniel Golle 	u32 val;
823fbe4d8cSDaniel Golle 
833fbe4d8cSDaniel Golle 	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
843fbe4d8cSDaniel Golle 				 !(val & MTK_PPE_MIB_SER_CR_ST),
853fbe4d8cSDaniel Golle 				 20, MTK_PPE_WAIT_TIMEOUT_US);
863fbe4d8cSDaniel Golle 
873fbe4d8cSDaniel Golle 	if (ret)
883fbe4d8cSDaniel Golle 		dev_err(ppe->dev, "MIB table busy");
893fbe4d8cSDaniel Golle 
903fbe4d8cSDaniel Golle 	return ret;
913fbe4d8cSDaniel Golle }
923fbe4d8cSDaniel Golle 
933fbe4d8cSDaniel Golle static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
943fbe4d8cSDaniel Golle {
953fbe4d8cSDaniel Golle 	u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
963fbe4d8cSDaniel Golle 	u32 val, cnt_r0, cnt_r1, cnt_r2;
973fbe4d8cSDaniel Golle 	int ret;
983fbe4d8cSDaniel Golle 
993fbe4d8cSDaniel Golle 	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
1003fbe4d8cSDaniel Golle 	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
1013fbe4d8cSDaniel Golle 
1023fbe4d8cSDaniel Golle 	ret = mtk_ppe_mib_wait_busy(ppe);
1033fbe4d8cSDaniel Golle 	if (ret)
1043fbe4d8cSDaniel Golle 		return ret;
1053fbe4d8cSDaniel Golle 
1063fbe4d8cSDaniel Golle 	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
1073fbe4d8cSDaniel Golle 	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
1083fbe4d8cSDaniel Golle 	cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
1093fbe4d8cSDaniel Golle 
1103fbe4d8cSDaniel Golle 	byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
1113fbe4d8cSDaniel Golle 	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
1123fbe4d8cSDaniel Golle 	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
1133fbe4d8cSDaniel Golle 	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
1143fbe4d8cSDaniel Golle 	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
1153fbe4d8cSDaniel Golle 	*packets = (pkt_cnt_high << 16) | pkt_cnt_low;
1163fbe4d8cSDaniel Golle 
1173fbe4d8cSDaniel Golle 	return 0;
1183fbe4d8cSDaniel Golle }
1193fbe4d8cSDaniel Golle 
120ba37b7caSFelix Fietkau static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
121ba37b7caSFelix Fietkau {
122ba37b7caSFelix Fietkau 	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
123ba37b7caSFelix Fietkau 	ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
124ba37b7caSFelix Fietkau }
125ba37b7caSFelix Fietkau 
126ba37b7caSFelix Fietkau static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
127ba37b7caSFelix Fietkau {
128ba37b7caSFelix Fietkau 	mtk_ppe_cache_clear(ppe);
129ba37b7caSFelix Fietkau 
130ba37b7caSFelix Fietkau 	ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
131ba37b7caSFelix Fietkau 		enable * MTK_PPE_CACHE_CTL_EN);
132ba37b7caSFelix Fietkau }
133ba37b7caSFelix Fietkau 
134ba2fc48cSLorenzo Bianconi static u32 mtk_ppe_hash_entry(struct mtk_eth *eth, struct mtk_foe_entry *e)
135ba37b7caSFelix Fietkau {
136ba37b7caSFelix Fietkau 	u32 hv1, hv2, hv3;
137ba37b7caSFelix Fietkau 	u32 hash;
138ba37b7caSFelix Fietkau 
13903a3180eSLorenzo Bianconi 	switch (mtk_get_ib1_pkt_type(eth, e->ib1)) {
140ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
141ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
142ba37b7caSFelix Fietkau 			hv1 = e->ipv4.orig.ports;
143ba37b7caSFelix Fietkau 			hv2 = e->ipv4.orig.dest_ip;
144ba37b7caSFelix Fietkau 			hv3 = e->ipv4.orig.src_ip;
145ba37b7caSFelix Fietkau 			break;
146ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
147ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
148ba37b7caSFelix Fietkau 			hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
149ba37b7caSFelix Fietkau 			hv1 ^= e->ipv6.ports;
150ba37b7caSFelix Fietkau 
151ba37b7caSFelix Fietkau 			hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
152ba37b7caSFelix Fietkau 			hv2 ^= e->ipv6.dest_ip[0];
153ba37b7caSFelix Fietkau 
154ba37b7caSFelix Fietkau 			hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
155ba37b7caSFelix Fietkau 			hv3 ^= e->ipv6.src_ip[0];
156ba37b7caSFelix Fietkau 			break;
157ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
158ba37b7caSFelix Fietkau 		case MTK_PPE_PKT_TYPE_IPV6_6RD:
159ba37b7caSFelix Fietkau 		default:
160ba37b7caSFelix Fietkau 			WARN_ON_ONCE(1);
161ba37b7caSFelix Fietkau 			return MTK_PPE_HASH_MASK;
162ba37b7caSFelix Fietkau 	}
163ba37b7caSFelix Fietkau 
164ba37b7caSFelix Fietkau 	hash = (hv1 & hv2) | ((~hv1) & hv3);
165ba37b7caSFelix Fietkau 	hash = (hash >> 24) | ((hash & 0xffffff) << 8);
166ba37b7caSFelix Fietkau 	hash ^= hv1 ^ hv2 ^ hv3;
167ba37b7caSFelix Fietkau 	hash ^= hash >> 16;
168ba2fc48cSLorenzo Bianconi 	hash <<= (ffs(eth->soc->hash_offset) - 1);
169ba37b7caSFelix Fietkau 	hash &= MTK_PPE_ENTRIES - 1;
170ba37b7caSFelix Fietkau 
171ba37b7caSFelix Fietkau 	return hash;
172ba37b7caSFelix Fietkau }
173ba37b7caSFelix Fietkau 
174ba37b7caSFelix Fietkau static inline struct mtk_foe_mac_info *
17503a3180eSLorenzo Bianconi mtk_foe_entry_l2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
176ba37b7caSFelix Fietkau {
17703a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
178ba37b7caSFelix Fietkau 
17933fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
18033fc42deSFelix Fietkau 		return &entry->bridge.l2;
18133fc42deSFelix Fietkau 
182ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
183ba37b7caSFelix Fietkau 		return &entry->ipv6.l2;
184ba37b7caSFelix Fietkau 
185ba37b7caSFelix Fietkau 	return &entry->ipv4.l2;
186ba37b7caSFelix Fietkau }
187ba37b7caSFelix Fietkau 
188ba37b7caSFelix Fietkau static inline u32 *
18903a3180eSLorenzo Bianconi mtk_foe_entry_ib2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
190ba37b7caSFelix Fietkau {
19103a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
192ba37b7caSFelix Fietkau 
19333fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
19433fc42deSFelix Fietkau 		return &entry->bridge.ib2;
19533fc42deSFelix Fietkau 
196ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
197ba37b7caSFelix Fietkau 		return &entry->ipv6.ib2;
198ba37b7caSFelix Fietkau 
199ba37b7caSFelix Fietkau 	return &entry->ipv4.ib2;
200ba37b7caSFelix Fietkau }
201ba37b7caSFelix Fietkau 
20203a3180eSLorenzo Bianconi int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry,
20303a3180eSLorenzo Bianconi 			  int type, int l4proto, u8 pse_port, u8 *src_mac,
20403a3180eSLorenzo Bianconi 			  u8 *dest_mac)
205ba37b7caSFelix Fietkau {
206ba37b7caSFelix Fietkau 	struct mtk_foe_mac_info *l2;
207ba37b7caSFelix Fietkau 	u32 ports_pad, val;
208ba37b7caSFelix Fietkau 
209ba37b7caSFelix Fietkau 	memset(entry, 0, sizeof(*entry));
210ba37b7caSFelix Fietkau 
21103a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
21203a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
21303a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) |
21403a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
21503a3180eSLorenzo Bianconi 		      MTK_FOE_IB1_BIND_CACHE_V2 | MTK_FOE_IB1_BIND_TTL_V2;
21603a3180eSLorenzo Bianconi 		entry->ib1 = val;
21703a3180eSLorenzo Bianconi 
21803a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) |
21903a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf);
22003a3180eSLorenzo Bianconi 	} else {
22171ba8e48SFelix Fietkau 		int port_mg = eth->soc->offload_version > 1 ? 0 : 0x3f;
22271ba8e48SFelix Fietkau 
223ba37b7caSFelix Fietkau 		val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
224ba37b7caSFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
225ba37b7caSFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
22603a3180eSLorenzo Bianconi 		      MTK_FOE_IB1_BIND_CACHE | MTK_FOE_IB1_BIND_TTL;
227ba37b7caSFelix Fietkau 		entry->ib1 = val;
228ba37b7caSFelix Fietkau 
22903a3180eSLorenzo Bianconi 		val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) |
23071ba8e48SFelix Fietkau 		      FIELD_PREP(MTK_FOE_IB2_PORT_MG, port_mg) |
23103a3180eSLorenzo Bianconi 		      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f);
23203a3180eSLorenzo Bianconi 	}
233ba37b7caSFelix Fietkau 
234ba37b7caSFelix Fietkau 	if (is_multicast_ether_addr(dest_mac))
23503a3180eSLorenzo Bianconi 		val |= mtk_get_ib2_multicast_mask(eth);
236ba37b7caSFelix Fietkau 
237ba37b7caSFelix Fietkau 	ports_pad = 0xa5a5a500 | (l4proto & 0xff);
238ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
239ba37b7caSFelix Fietkau 		entry->ipv4.orig.ports = ports_pad;
240ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
241ba37b7caSFelix Fietkau 		entry->ipv6.ports = ports_pad;
242ba37b7caSFelix Fietkau 
24333fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
24433fc42deSFelix Fietkau 		ether_addr_copy(entry->bridge.src_mac, src_mac);
24533fc42deSFelix Fietkau 		ether_addr_copy(entry->bridge.dest_mac, dest_mac);
24633fc42deSFelix Fietkau 		entry->bridge.ib2 = val;
24733fc42deSFelix Fietkau 		l2 = &entry->bridge.l2;
24833fc42deSFelix Fietkau 	} else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
249ba37b7caSFelix Fietkau 		entry->ipv6.ib2 = val;
250ba37b7caSFelix Fietkau 		l2 = &entry->ipv6.l2;
251ba37b7caSFelix Fietkau 	} else {
252ba37b7caSFelix Fietkau 		entry->ipv4.ib2 = val;
253ba37b7caSFelix Fietkau 		l2 = &entry->ipv4.l2;
254ba37b7caSFelix Fietkau 	}
255ba37b7caSFelix Fietkau 
256ba37b7caSFelix Fietkau 	l2->dest_mac_hi = get_unaligned_be32(dest_mac);
257ba37b7caSFelix Fietkau 	l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
258ba37b7caSFelix Fietkau 	l2->src_mac_hi = get_unaligned_be32(src_mac);
259ba37b7caSFelix Fietkau 	l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
260ba37b7caSFelix Fietkau 
261ba37b7caSFelix Fietkau 	if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
262ba37b7caSFelix Fietkau 		l2->etype = ETH_P_IPV6;
263ba37b7caSFelix Fietkau 	else
264ba37b7caSFelix Fietkau 		l2->etype = ETH_P_IP;
265ba37b7caSFelix Fietkau 
266ba37b7caSFelix Fietkau 	return 0;
267ba37b7caSFelix Fietkau }
268ba37b7caSFelix Fietkau 
26903a3180eSLorenzo Bianconi int mtk_foe_entry_set_pse_port(struct mtk_eth *eth,
27003a3180eSLorenzo Bianconi 			       struct mtk_foe_entry *entry, u8 port)
271ba37b7caSFelix Fietkau {
27203a3180eSLorenzo Bianconi 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
27303a3180eSLorenzo Bianconi 	u32 val = *ib2;
274ba37b7caSFelix Fietkau 
27503a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
27603a3180eSLorenzo Bianconi 		val &= ~MTK_FOE_IB2_DEST_PORT_V2;
27703a3180eSLorenzo Bianconi 		val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port);
27803a3180eSLorenzo Bianconi 	} else {
279ba37b7caSFelix Fietkau 		val &= ~MTK_FOE_IB2_DEST_PORT;
280ba37b7caSFelix Fietkau 		val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
28103a3180eSLorenzo Bianconi 	}
282ba37b7caSFelix Fietkau 	*ib2 = val;
283ba37b7caSFelix Fietkau 
284ba37b7caSFelix Fietkau 	return 0;
285ba37b7caSFelix Fietkau }
286ba37b7caSFelix Fietkau 
28703a3180eSLorenzo Bianconi int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth,
28803a3180eSLorenzo Bianconi 				 struct mtk_foe_entry *entry, bool egress,
289ba37b7caSFelix Fietkau 				 __be32 src_addr, __be16 src_port,
290ba37b7caSFelix Fietkau 				 __be32 dest_addr, __be16 dest_port)
291ba37b7caSFelix Fietkau {
29203a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
293ba37b7caSFelix Fietkau 	struct mtk_ipv4_tuple *t;
294ba37b7caSFelix Fietkau 
295ba37b7caSFelix Fietkau 	switch (type) {
296ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
297ba37b7caSFelix Fietkau 		if (egress) {
298ba37b7caSFelix Fietkau 			t = &entry->ipv4.new;
299ba37b7caSFelix Fietkau 			break;
300ba37b7caSFelix Fietkau 		}
301ba37b7caSFelix Fietkau 		fallthrough;
302ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
303ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
304ba37b7caSFelix Fietkau 		t = &entry->ipv4.orig;
305ba37b7caSFelix Fietkau 		break;
306ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_6RD:
307ba37b7caSFelix Fietkau 		entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
308ba37b7caSFelix Fietkau 		entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
309ba37b7caSFelix Fietkau 		return 0;
310ba37b7caSFelix Fietkau 	default:
311ba37b7caSFelix Fietkau 		WARN_ON_ONCE(1);
312ba37b7caSFelix Fietkau 		return -EINVAL;
313ba37b7caSFelix Fietkau 	}
314ba37b7caSFelix Fietkau 
315ba37b7caSFelix Fietkau 	t->src_ip = be32_to_cpu(src_addr);
316ba37b7caSFelix Fietkau 	t->dest_ip = be32_to_cpu(dest_addr);
317ba37b7caSFelix Fietkau 
318ba37b7caSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
319ba37b7caSFelix Fietkau 		return 0;
320ba37b7caSFelix Fietkau 
321ba37b7caSFelix Fietkau 	t->src_port = be16_to_cpu(src_port);
322ba37b7caSFelix Fietkau 	t->dest_port = be16_to_cpu(dest_port);
323ba37b7caSFelix Fietkau 
324ba37b7caSFelix Fietkau 	return 0;
325ba37b7caSFelix Fietkau }
326ba37b7caSFelix Fietkau 
32703a3180eSLorenzo Bianconi int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth,
32803a3180eSLorenzo Bianconi 				 struct mtk_foe_entry *entry,
329ba37b7caSFelix Fietkau 				 __be32 *src_addr, __be16 src_port,
330ba37b7caSFelix Fietkau 				 __be32 *dest_addr, __be16 dest_port)
331ba37b7caSFelix Fietkau {
33203a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
333ba37b7caSFelix Fietkau 	u32 *src, *dest;
334ba37b7caSFelix Fietkau 	int i;
335ba37b7caSFelix Fietkau 
336ba37b7caSFelix Fietkau 	switch (type) {
337ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
338ba37b7caSFelix Fietkau 		src = entry->dslite.tunnel_src_ip;
339ba37b7caSFelix Fietkau 		dest = entry->dslite.tunnel_dest_ip;
340ba37b7caSFelix Fietkau 		break;
341ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
342ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_6RD:
343ba37b7caSFelix Fietkau 		entry->ipv6.src_port = be16_to_cpu(src_port);
344ba37b7caSFelix Fietkau 		entry->ipv6.dest_port = be16_to_cpu(dest_port);
345ba37b7caSFelix Fietkau 		fallthrough;
346ba37b7caSFelix Fietkau 	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
347ba37b7caSFelix Fietkau 		src = entry->ipv6.src_ip;
348ba37b7caSFelix Fietkau 		dest = entry->ipv6.dest_ip;
349ba37b7caSFelix Fietkau 		break;
350ba37b7caSFelix Fietkau 	default:
351ba37b7caSFelix Fietkau 		WARN_ON_ONCE(1);
352ba37b7caSFelix Fietkau 		return -EINVAL;
3533b2c32f9SQiheng Lin 	}
354ba37b7caSFelix Fietkau 
355ba37b7caSFelix Fietkau 	for (i = 0; i < 4; i++)
356ba37b7caSFelix Fietkau 		src[i] = be32_to_cpu(src_addr[i]);
357ba37b7caSFelix Fietkau 	for (i = 0; i < 4; i++)
358ba37b7caSFelix Fietkau 		dest[i] = be32_to_cpu(dest_addr[i]);
359ba37b7caSFelix Fietkau 
360ba37b7caSFelix Fietkau 	return 0;
361ba37b7caSFelix Fietkau }
362ba37b7caSFelix Fietkau 
36303a3180eSLorenzo Bianconi int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry,
36403a3180eSLorenzo Bianconi 			  int port)
365ba37b7caSFelix Fietkau {
36603a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
367ba37b7caSFelix Fietkau 
368ba37b7caSFelix Fietkau 	l2->etype = BIT(port);
369ba37b7caSFelix Fietkau 
37003a3180eSLorenzo Bianconi 	if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)))
37103a3180eSLorenzo Bianconi 		entry->ib1 |= mtk_prep_ib1_vlan_layer(eth, 1);
372ba37b7caSFelix Fietkau 	else
373ba37b7caSFelix Fietkau 		l2->etype |= BIT(8);
374ba37b7caSFelix Fietkau 
37503a3180eSLorenzo Bianconi 	entry->ib1 &= ~mtk_get_ib1_vlan_tag_mask(eth);
376ba37b7caSFelix Fietkau 
377ba37b7caSFelix Fietkau 	return 0;
378ba37b7caSFelix Fietkau }
379ba37b7caSFelix Fietkau 
38003a3180eSLorenzo Bianconi int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry,
38103a3180eSLorenzo Bianconi 			   int vid)
382ba37b7caSFelix Fietkau {
38303a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
384ba37b7caSFelix Fietkau 
385fb7da771SDaniel Golle 	switch (mtk_get_ib1_vlan_layer(eth, entry->ib1)) {
386ba37b7caSFelix Fietkau 	case 0:
38703a3180eSLorenzo Bianconi 		entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) |
38803a3180eSLorenzo Bianconi 			      mtk_prep_ib1_vlan_layer(eth, 1);
389ba37b7caSFelix Fietkau 		l2->vlan1 = vid;
390ba37b7caSFelix Fietkau 		return 0;
391ba37b7caSFelix Fietkau 	case 1:
39203a3180eSLorenzo Bianconi 		if (!(entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) {
393ba37b7caSFelix Fietkau 			l2->vlan1 = vid;
394ba37b7caSFelix Fietkau 			l2->etype |= BIT(8);
395ba37b7caSFelix Fietkau 		} else {
396ba37b7caSFelix Fietkau 			l2->vlan2 = vid;
39703a3180eSLorenzo Bianconi 			entry->ib1 += mtk_prep_ib1_vlan_layer(eth, 1);
398ba37b7caSFelix Fietkau 		}
399ba37b7caSFelix Fietkau 		return 0;
400ba37b7caSFelix Fietkau 	default:
401ba37b7caSFelix Fietkau 		return -ENOSPC;
402ba37b7caSFelix Fietkau 	}
403ba37b7caSFelix Fietkau }
404ba37b7caSFelix Fietkau 
40503a3180eSLorenzo Bianconi int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
40603a3180eSLorenzo Bianconi 			    int sid)
407ba37b7caSFelix Fietkau {
40803a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
409ba37b7caSFelix Fietkau 
41003a3180eSLorenzo Bianconi 	if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)) ||
41103a3180eSLorenzo Bianconi 	    (entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth)))
412ba37b7caSFelix Fietkau 		l2->etype = ETH_P_PPP_SES;
413ba37b7caSFelix Fietkau 
41403a3180eSLorenzo Bianconi 	entry->ib1 |= mtk_get_ib1_ppoe_mask(eth);
415ba37b7caSFelix Fietkau 	l2->pppoe_id = sid;
416ba37b7caSFelix Fietkau 
417ba37b7caSFelix Fietkau 	return 0;
418ba37b7caSFelix Fietkau }
419ba37b7caSFelix Fietkau 
42003a3180eSLorenzo Bianconi int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
42103a3180eSLorenzo Bianconi 			   int wdma_idx, int txq, int bss, int wcid)
422a333215eSFelix Fietkau {
42303a3180eSLorenzo Bianconi 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
42403a3180eSLorenzo Bianconi 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
425a333215eSFelix Fietkau 
42603a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
42703a3180eSLorenzo Bianconi 		*ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
42803a3180eSLorenzo Bianconi 		*ib2 |=  FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
42903a3180eSLorenzo Bianconi 			 MTK_FOE_IB2_WDMA_WINFO_V2;
43003a3180eSLorenzo Bianconi 		l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) |
43103a3180eSLorenzo Bianconi 			    FIELD_PREP(MTK_FOE_WINFO_BSS, bss);
43203a3180eSLorenzo Bianconi 	} else {
433a333215eSFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_PORT_MG;
434a333215eSFelix Fietkau 		*ib2 |= MTK_FOE_IB2_WDMA_WINFO;
435a333215eSFelix Fietkau 		if (wdma_idx)
436a333215eSFelix Fietkau 			*ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
437a333215eSFelix Fietkau 		l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
438a333215eSFelix Fietkau 			    FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
439a333215eSFelix Fietkau 			    FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
44003a3180eSLorenzo Bianconi 	}
441a333215eSFelix Fietkau 
442a333215eSFelix Fietkau 	return 0;
443a333215eSFelix Fietkau }
444a333215eSFelix Fietkau 
4458bd8dcc5SFelix Fietkau int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
4468bd8dcc5SFelix Fietkau 			    unsigned int queue)
4478bd8dcc5SFelix Fietkau {
4488bd8dcc5SFelix Fietkau 	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
4498bd8dcc5SFelix Fietkau 
4508bd8dcc5SFelix Fietkau 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
4518bd8dcc5SFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_QID_V2;
4528bd8dcc5SFelix Fietkau 		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue);
4538bd8dcc5SFelix Fietkau 		*ib2 |= MTK_FOE_IB2_PSE_QOS_V2;
4548bd8dcc5SFelix Fietkau 	} else {
4558bd8dcc5SFelix Fietkau 		*ib2 &= ~MTK_FOE_IB2_QID;
4568bd8dcc5SFelix Fietkau 		*ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, queue);
4578bd8dcc5SFelix Fietkau 		*ib2 |= MTK_FOE_IB2_PSE_QOS;
4588bd8dcc5SFelix Fietkau 	}
4598bd8dcc5SFelix Fietkau 
4608bd8dcc5SFelix Fietkau 	return 0;
4618bd8dcc5SFelix Fietkau }
4628bd8dcc5SFelix Fietkau 
463c4f033d9SFelix Fietkau static bool
46403a3180eSLorenzo Bianconi mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
46503a3180eSLorenzo Bianconi 		     struct mtk_foe_entry *data)
466c4f033d9SFelix Fietkau {
467c4f033d9SFelix Fietkau 	int type, len;
468c4f033d9SFelix Fietkau 
469c4f033d9SFelix Fietkau 	if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
470c4f033d9SFelix Fietkau 		return false;
471c4f033d9SFelix Fietkau 
47203a3180eSLorenzo Bianconi 	type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
473c4f033d9SFelix Fietkau 	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
474c4f033d9SFelix Fietkau 		len = offsetof(struct mtk_foe_entry, ipv6._rsv);
475c4f033d9SFelix Fietkau 	else
476c4f033d9SFelix Fietkau 		len = offsetof(struct mtk_foe_entry, ipv4.ib2);
477c4f033d9SFelix Fietkau 
478c4f033d9SFelix Fietkau 	return !memcmp(&entry->data.data, &data->data, len - 4);
479c4f033d9SFelix Fietkau }
480c4f033d9SFelix Fietkau 
481c4f033d9SFelix Fietkau static void
48233fc42deSFelix Fietkau __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
48333fc42deSFelix Fietkau {
48433fc42deSFelix Fietkau 	struct hlist_head *head;
48533fc42deSFelix Fietkau 	struct hlist_node *tmp;
48633fc42deSFelix Fietkau 
48733fc42deSFelix Fietkau 	if (entry->type == MTK_FLOW_TYPE_L2) {
48833fc42deSFelix Fietkau 		rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
48933fc42deSFelix Fietkau 				       mtk_flow_l2_ht_params);
49033fc42deSFelix Fietkau 
49133fc42deSFelix Fietkau 		head = &entry->l2_flows;
49233fc42deSFelix Fietkau 		hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
49333fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, entry);
49433fc42deSFelix Fietkau 		return;
49533fc42deSFelix Fietkau 	}
49633fc42deSFelix Fietkau 
49733fc42deSFelix Fietkau 	hlist_del_init(&entry->list);
49833fc42deSFelix Fietkau 	if (entry->hash != 0xffff) {
4999d8cb4c0SLorenzo Bianconi 		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
5009d8cb4c0SLorenzo Bianconi 
5019d8cb4c0SLorenzo Bianconi 		hwe->ib1 &= ~MTK_FOE_IB1_STATE;
502e52f7c1dSJakub Kicinski 		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
50333fc42deSFelix Fietkau 		dma_wmb();
50492453132SFelix Fietkau 		mtk_ppe_cache_clear(ppe);
50579548b79SJakub Kicinski 
5063fbe4d8cSDaniel Golle 		if (ppe->accounting) {
5073fbe4d8cSDaniel Golle 			struct mtk_foe_accounting *acct;
5083fbe4d8cSDaniel Golle 
5093fbe4d8cSDaniel Golle 			acct = ppe->acct_table + entry->hash * sizeof(*acct);
5103fbe4d8cSDaniel Golle 			acct->packets = 0;
5113fbe4d8cSDaniel Golle 			acct->bytes = 0;
5123fbe4d8cSDaniel Golle 		}
51333fc42deSFelix Fietkau 	}
51433fc42deSFelix Fietkau 	entry->hash = 0xffff;
51533fc42deSFelix Fietkau 
51633fc42deSFelix Fietkau 	if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
51733fc42deSFelix Fietkau 		return;
51833fc42deSFelix Fietkau 
51933fc42deSFelix Fietkau 	hlist_del_init(&entry->l2_data.list);
52033fc42deSFelix Fietkau 	kfree(entry);
52133fc42deSFelix Fietkau }
52233fc42deSFelix Fietkau 
52333fc42deSFelix Fietkau static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
52433fc42deSFelix Fietkau {
52503a3180eSLorenzo Bianconi 	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
52603a3180eSLorenzo Bianconi 	u16 now = mtk_eth_timestamp(ppe->eth);
52703a3180eSLorenzo Bianconi 	u16 timestamp = ib1 & ib1_ts_mask;
52833fc42deSFelix Fietkau 
52933fc42deSFelix Fietkau 	if (timestamp > now)
53003a3180eSLorenzo Bianconi 		return ib1_ts_mask + 1 - timestamp + now;
53133fc42deSFelix Fietkau 	else
53233fc42deSFelix Fietkau 		return now - timestamp;
53333fc42deSFelix Fietkau }
53433fc42deSFelix Fietkau 
53533fc42deSFelix Fietkau static void
53633fc42deSFelix Fietkau mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
53733fc42deSFelix Fietkau {
53803a3180eSLorenzo Bianconi 	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
53933fc42deSFelix Fietkau 	struct mtk_flow_entry *cur;
54033fc42deSFelix Fietkau 	struct mtk_foe_entry *hwe;
54133fc42deSFelix Fietkau 	struct hlist_node *tmp;
54233fc42deSFelix Fietkau 	int idle;
54333fc42deSFelix Fietkau 
54433fc42deSFelix Fietkau 	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
54533fc42deSFelix Fietkau 	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
54633fc42deSFelix Fietkau 		int cur_idle;
54733fc42deSFelix Fietkau 		u32 ib1;
54833fc42deSFelix Fietkau 
5499d8cb4c0SLorenzo Bianconi 		hwe = mtk_foe_get_entry(ppe, cur->hash);
55033fc42deSFelix Fietkau 		ib1 = READ_ONCE(hwe->ib1);
55133fc42deSFelix Fietkau 
55233fc42deSFelix Fietkau 		if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
55333fc42deSFelix Fietkau 			cur->hash = 0xffff;
55433fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, cur);
55533fc42deSFelix Fietkau 			continue;
55633fc42deSFelix Fietkau 		}
55733fc42deSFelix Fietkau 
55833fc42deSFelix Fietkau 		cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
55933fc42deSFelix Fietkau 		if (cur_idle >= idle)
56033fc42deSFelix Fietkau 			continue;
56133fc42deSFelix Fietkau 
56233fc42deSFelix Fietkau 		idle = cur_idle;
56303a3180eSLorenzo Bianconi 		entry->data.ib1 &= ~ib1_ts_mask;
56403a3180eSLorenzo Bianconi 		entry->data.ib1 |= hwe->ib1 & ib1_ts_mask;
56533fc42deSFelix Fietkau 	}
56633fc42deSFelix Fietkau }
56733fc42deSFelix Fietkau 
56833fc42deSFelix Fietkau static void
569c4f033d9SFelix Fietkau mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
570ba37b7caSFelix Fietkau {
5719d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry foe = {};
572ba37b7caSFelix Fietkau 	struct mtk_foe_entry *hwe;
573ba37b7caSFelix Fietkau 
574c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
57533fc42deSFelix Fietkau 
57633fc42deSFelix Fietkau 	if (entry->type == MTK_FLOW_TYPE_L2) {
57733fc42deSFelix Fietkau 		mtk_flow_entry_update_l2(ppe, entry);
57833fc42deSFelix Fietkau 		goto out;
57933fc42deSFelix Fietkau 	}
58033fc42deSFelix Fietkau 
581c4f033d9SFelix Fietkau 	if (entry->hash == 0xffff)
582c4f033d9SFelix Fietkau 		goto out;
583c4f033d9SFelix Fietkau 
5849d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, entry->hash);
5859d8cb4c0SLorenzo Bianconi 	memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
58603a3180eSLorenzo Bianconi 	if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
587c4f033d9SFelix Fietkau 		entry->hash = 0xffff;
588c4f033d9SFelix Fietkau 		goto out;
589c4f033d9SFelix Fietkau 	}
590c4f033d9SFelix Fietkau 
591c4f033d9SFelix Fietkau 	entry->data.ib1 = foe.ib1;
592c4f033d9SFelix Fietkau 
593c4f033d9SFelix Fietkau out:
594c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
595c4f033d9SFelix Fietkau }
596c4f033d9SFelix Fietkau 
597c4f033d9SFelix Fietkau static void
598c4f033d9SFelix Fietkau __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
599c4f033d9SFelix Fietkau 		       u16 hash)
600c4f033d9SFelix Fietkau {
60103a3180eSLorenzo Bianconi 	struct mtk_eth *eth = ppe->eth;
60203a3180eSLorenzo Bianconi 	u16 timestamp = mtk_eth_timestamp(eth);
603c4f033d9SFelix Fietkau 	struct mtk_foe_entry *hwe;
604c4f033d9SFelix Fietkau 
60503a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
60603a3180eSLorenzo Bianconi 		entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
60703a3180eSLorenzo Bianconi 		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2,
60803a3180eSLorenzo Bianconi 					 timestamp);
60903a3180eSLorenzo Bianconi 	} else {
610ba37b7caSFelix Fietkau 		entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
61103a3180eSLorenzo Bianconi 		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP,
61203a3180eSLorenzo Bianconi 					 timestamp);
61303a3180eSLorenzo Bianconi 	}
614ba37b7caSFelix Fietkau 
6159d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, hash);
616454b20e1SDaniel Golle 	memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size - sizeof(hwe->ib1));
617ba37b7caSFelix Fietkau 	wmb();
618ba37b7caSFelix Fietkau 	hwe->ib1 = entry->ib1;
619ba37b7caSFelix Fietkau 
6203fbe4d8cSDaniel Golle 	if (ppe->accounting)
6213fbe4d8cSDaniel Golle 		*mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
6223fbe4d8cSDaniel Golle 
623ba37b7caSFelix Fietkau 	dma_wmb();
624ba37b7caSFelix Fietkau 
625ba37b7caSFelix Fietkau 	mtk_ppe_cache_clear(ppe);
626ba37b7caSFelix Fietkau }
627ba37b7caSFelix Fietkau 
628c4f033d9SFelix Fietkau void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
629c4f033d9SFelix Fietkau {
630c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
63133fc42deSFelix Fietkau 	__mtk_foe_entry_clear(ppe, entry);
632c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
633c4f033d9SFelix Fietkau }
634c4f033d9SFelix Fietkau 
63533fc42deSFelix Fietkau static int
63633fc42deSFelix Fietkau mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
63733fc42deSFelix Fietkau {
638*e2853114SFelix Fietkau 	struct mtk_flow_entry *prev;
639*e2853114SFelix Fietkau 
64033fc42deSFelix Fietkau 	entry->type = MTK_FLOW_TYPE_L2;
64133fc42deSFelix Fietkau 
642*e2853114SFelix Fietkau 	prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &entry->l2_node,
64333fc42deSFelix Fietkau 						 mtk_flow_l2_ht_params);
644*e2853114SFelix Fietkau 	if (likely(!prev))
645*e2853114SFelix Fietkau 		return 0;
646*e2853114SFelix Fietkau 
647*e2853114SFelix Fietkau 	if (IS_ERR(prev))
648*e2853114SFelix Fietkau 		return PTR_ERR(prev);
649*e2853114SFelix Fietkau 
650*e2853114SFelix Fietkau 	return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node,
651*e2853114SFelix Fietkau 				       &entry->l2_node, mtk_flow_l2_ht_params);
65233fc42deSFelix Fietkau }
65333fc42deSFelix Fietkau 
654c4f033d9SFelix Fietkau int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
655c4f033d9SFelix Fietkau {
656ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
65703a3180eSLorenzo Bianconi 	int type = mtk_get_ib1_pkt_type(ppe->eth, entry->data.ib1);
65833fc42deSFelix Fietkau 	u32 hash;
659c4f033d9SFelix Fietkau 
66033fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
66133fc42deSFelix Fietkau 		return mtk_foe_entry_commit_l2(ppe, entry);
66233fc42deSFelix Fietkau 
663ba2fc48cSLorenzo Bianconi 	hash = mtk_ppe_hash_entry(ppe->eth, &entry->data);
664c4f033d9SFelix Fietkau 	entry->hash = 0xffff;
665c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
666ba2fc48cSLorenzo Bianconi 	hlist_add_head(&entry->list, &ppe->foe_flow[hash / soc->hash_offset]);
667c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
668c4f033d9SFelix Fietkau 
669c4f033d9SFelix Fietkau 	return 0;
670c4f033d9SFelix Fietkau }
671c4f033d9SFelix Fietkau 
67233fc42deSFelix Fietkau static void
67333fc42deSFelix Fietkau mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
67433fc42deSFelix Fietkau 			     u16 hash)
67533fc42deSFelix Fietkau {
676ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
67733fc42deSFelix Fietkau 	struct mtk_flow_entry *flow_info;
6789d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry foe = {}, *hwe;
67933fc42deSFelix Fietkau 	struct mtk_foe_mac_info *l2;
68003a3180eSLorenzo Bianconi 	u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
68133fc42deSFelix Fietkau 	int type;
68233fc42deSFelix Fietkau 
683f3eceaedSKees Cook 	flow_info = kzalloc(sizeof(*flow_info), GFP_ATOMIC);
68433fc42deSFelix Fietkau 	if (!flow_info)
68533fc42deSFelix Fietkau 		return;
68633fc42deSFelix Fietkau 
68733fc42deSFelix Fietkau 	flow_info->l2_data.base_flow = entry;
68833fc42deSFelix Fietkau 	flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
68933fc42deSFelix Fietkau 	flow_info->hash = hash;
690ba2fc48cSLorenzo Bianconi 	hlist_add_head(&flow_info->list,
691ba2fc48cSLorenzo Bianconi 		       &ppe->foe_flow[hash / soc->hash_offset]);
69233fc42deSFelix Fietkau 	hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
69333fc42deSFelix Fietkau 
6949d8cb4c0SLorenzo Bianconi 	hwe = mtk_foe_get_entry(ppe, hash);
6959d8cb4c0SLorenzo Bianconi 	memcpy(&foe, hwe, soc->foe_entry_size);
69633fc42deSFelix Fietkau 	foe.ib1 &= ib1_mask;
69733fc42deSFelix Fietkau 	foe.ib1 |= entry->data.ib1 & ~ib1_mask;
69833fc42deSFelix Fietkau 
69903a3180eSLorenzo Bianconi 	l2 = mtk_foe_entry_l2(ppe->eth, &foe);
70033fc42deSFelix Fietkau 	memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
70133fc42deSFelix Fietkau 
70203a3180eSLorenzo Bianconi 	type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
70333fc42deSFelix Fietkau 	if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
70433fc42deSFelix Fietkau 		memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
70533fc42deSFelix Fietkau 	else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
70633fc42deSFelix Fietkau 		l2->etype = ETH_P_IPV6;
70733fc42deSFelix Fietkau 
70803a3180eSLorenzo Bianconi 	*mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
70933fc42deSFelix Fietkau 
71033fc42deSFelix Fietkau 	__mtk_foe_entry_commit(ppe, &foe, hash);
71133fc42deSFelix Fietkau }
71233fc42deSFelix Fietkau 
713c4f033d9SFelix Fietkau void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
714c4f033d9SFelix Fietkau {
715ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = ppe->eth->soc;
716ba2fc48cSLorenzo Bianconi 	struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset];
7179d8cb4c0SLorenzo Bianconi 	struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
71833fc42deSFelix Fietkau 	struct mtk_flow_entry *entry;
71933fc42deSFelix Fietkau 	struct mtk_foe_bridge key = {};
72017a5f6a7SDan Carpenter 	struct hlist_node *n;
72133fc42deSFelix Fietkau 	struct ethhdr *eh;
722c4f033d9SFelix Fietkau 	bool found = false;
72333fc42deSFelix Fietkau 	u8 *tag;
724c4f033d9SFelix Fietkau 
725c4f033d9SFelix Fietkau 	spin_lock_bh(&ppe_lock);
72633fc42deSFelix Fietkau 
72733fc42deSFelix Fietkau 	if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
72833fc42deSFelix Fietkau 		goto out;
72933fc42deSFelix Fietkau 
73017a5f6a7SDan Carpenter 	hlist_for_each_entry_safe(entry, n, head, list) {
73133fc42deSFelix Fietkau 		if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
73233fc42deSFelix Fietkau 			if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
73333fc42deSFelix Fietkau 				     MTK_FOE_STATE_BIND))
73433fc42deSFelix Fietkau 				continue;
73533fc42deSFelix Fietkau 
73633fc42deSFelix Fietkau 			entry->hash = 0xffff;
73733fc42deSFelix Fietkau 			__mtk_foe_entry_clear(ppe, entry);
73833fc42deSFelix Fietkau 			continue;
73933fc42deSFelix Fietkau 		}
74033fc42deSFelix Fietkau 
74103a3180eSLorenzo Bianconi 		if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
742c4f033d9SFelix Fietkau 			if (entry->hash != 0xffff)
743c4f033d9SFelix Fietkau 				entry->hash = 0xffff;
744c4f033d9SFelix Fietkau 			continue;
745c4f033d9SFelix Fietkau 		}
746c4f033d9SFelix Fietkau 
747c4f033d9SFelix Fietkau 		entry->hash = hash;
748c4f033d9SFelix Fietkau 		__mtk_foe_entry_commit(ppe, &entry->data, hash);
749c4f033d9SFelix Fietkau 		found = true;
750c4f033d9SFelix Fietkau 	}
75133fc42deSFelix Fietkau 
75233fc42deSFelix Fietkau 	if (found)
75333fc42deSFelix Fietkau 		goto out;
75433fc42deSFelix Fietkau 
75533fc42deSFelix Fietkau 	eh = eth_hdr(skb);
75633fc42deSFelix Fietkau 	ether_addr_copy(key.dest_mac, eh->h_dest);
75733fc42deSFelix Fietkau 	ether_addr_copy(key.src_mac, eh->h_source);
75833fc42deSFelix Fietkau 	tag = skb->data - 2;
75933fc42deSFelix Fietkau 	key.vlan = 0;
76033fc42deSFelix Fietkau 	switch (skb->protocol) {
76133fc42deSFelix Fietkau #if IS_ENABLED(CONFIG_NET_DSA)
76233fc42deSFelix Fietkau 	case htons(ETH_P_XDSA):
76333fc42deSFelix Fietkau 		if (!netdev_uses_dsa(skb->dev) ||
76433fc42deSFelix Fietkau 		    skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
76533fc42deSFelix Fietkau 			goto out;
76633fc42deSFelix Fietkau 
7675f36ca1bSFelix Fietkau 		if (!skb_metadata_dst(skb))
76833fc42deSFelix Fietkau 			tag += 4;
7695f36ca1bSFelix Fietkau 
77033fc42deSFelix Fietkau 		if (get_unaligned_be16(tag) != ETH_P_8021Q)
77133fc42deSFelix Fietkau 			break;
77233fc42deSFelix Fietkau 
77333fc42deSFelix Fietkau 		fallthrough;
77433fc42deSFelix Fietkau #endif
77533fc42deSFelix Fietkau 	case htons(ETH_P_8021Q):
77633fc42deSFelix Fietkau 		key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
77733fc42deSFelix Fietkau 		break;
77833fc42deSFelix Fietkau 	default:
77933fc42deSFelix Fietkau 		break;
78033fc42deSFelix Fietkau 	}
78133fc42deSFelix Fietkau 
78233fc42deSFelix Fietkau 	entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
78333fc42deSFelix Fietkau 	if (!entry)
78433fc42deSFelix Fietkau 		goto out;
78533fc42deSFelix Fietkau 
78633fc42deSFelix Fietkau 	mtk_foe_entry_commit_subflow(ppe, entry, hash);
78733fc42deSFelix Fietkau 
78833fc42deSFelix Fietkau out:
789c4f033d9SFelix Fietkau 	spin_unlock_bh(&ppe_lock);
790c4f033d9SFelix Fietkau }
791c4f033d9SFelix Fietkau 
792c4f033d9SFelix Fietkau int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
793c4f033d9SFelix Fietkau {
794c4f033d9SFelix Fietkau 	mtk_flow_entry_update(ppe, entry);
795c4f033d9SFelix Fietkau 
79633fc42deSFelix Fietkau 	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
797c4f033d9SFelix Fietkau }
798c4f033d9SFelix Fietkau 
79906127504SLorenzo Bianconi int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
80006127504SLorenzo Bianconi {
80106127504SLorenzo Bianconi 	if (!ppe)
80206127504SLorenzo Bianconi 		return -EINVAL;
80306127504SLorenzo Bianconi 
80406127504SLorenzo Bianconi 	/* disable KA */
80506127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
80606127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
80706127504SLorenzo Bianconi 	ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0);
80806127504SLorenzo Bianconi 	usleep_range(10000, 11000);
80906127504SLorenzo Bianconi 
81006127504SLorenzo Bianconi 	/* set KA timer to maximum */
81106127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
81206127504SLorenzo Bianconi 	ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0xffffffff);
81306127504SLorenzo Bianconi 
81406127504SLorenzo Bianconi 	/* set KA tick select */
81506127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_TICK_SEL);
81606127504SLorenzo Bianconi 	ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
81706127504SLorenzo Bianconi 	usleep_range(10000, 11000);
81806127504SLorenzo Bianconi 
81906127504SLorenzo Bianconi 	/* disable scan mode */
82006127504SLorenzo Bianconi 	ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_SCAN_MODE);
82106127504SLorenzo Bianconi 	usleep_range(10000, 11000);
82206127504SLorenzo Bianconi 
82306127504SLorenzo Bianconi 	return mtk_ppe_wait_busy(ppe);
82406127504SLorenzo Bianconi }
82506127504SLorenzo Bianconi 
8263fbe4d8cSDaniel Golle struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
8273fbe4d8cSDaniel Golle 						 struct mtk_foe_accounting *diff)
828ba37b7caSFelix Fietkau {
8293fbe4d8cSDaniel Golle 	struct mtk_foe_accounting *acct;
8303fbe4d8cSDaniel Golle 	int size = sizeof(struct mtk_foe_accounting);
8313fbe4d8cSDaniel Golle 	u64 bytes, packets;
8323fbe4d8cSDaniel Golle 
8333fbe4d8cSDaniel Golle 	if (!ppe->accounting)
8343fbe4d8cSDaniel Golle 		return NULL;
8353fbe4d8cSDaniel Golle 
8363fbe4d8cSDaniel Golle 	if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
8373fbe4d8cSDaniel Golle 		return NULL;
8383fbe4d8cSDaniel Golle 
8393fbe4d8cSDaniel Golle 	acct = ppe->acct_table + index * size;
8403fbe4d8cSDaniel Golle 
8413fbe4d8cSDaniel Golle 	acct->bytes += bytes;
8423fbe4d8cSDaniel Golle 	acct->packets += packets;
8433fbe4d8cSDaniel Golle 
8443fbe4d8cSDaniel Golle 	if (diff) {
8453fbe4d8cSDaniel Golle 		diff->bytes = bytes;
8463fbe4d8cSDaniel Golle 		diff->packets = packets;
8473fbe4d8cSDaniel Golle 	}
8483fbe4d8cSDaniel Golle 
8493fbe4d8cSDaniel Golle 	return acct;
8503fbe4d8cSDaniel Golle }
8513fbe4d8cSDaniel Golle 
8523fbe4d8cSDaniel Golle struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
8533fbe4d8cSDaniel Golle {
8543fbe4d8cSDaniel Golle 	bool accounting = eth->soc->has_accounting;
855ba2fc48cSLorenzo Bianconi 	const struct mtk_soc_data *soc = eth->soc;
8563fbe4d8cSDaniel Golle 	struct mtk_foe_accounting *acct;
857c4f033d9SFelix Fietkau 	struct device *dev = eth->dev;
8583fbe4d8cSDaniel Golle 	struct mtk_mib_entry *mib;
8591ccc723bSFelix Fietkau 	struct mtk_ppe *ppe;
860ba2fc48cSLorenzo Bianconi 	u32 foe_flow_size;
8619d8cb4c0SLorenzo Bianconi 	void *foe;
8621ccc723bSFelix Fietkau 
8631ccc723bSFelix Fietkau 	ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
8641ccc723bSFelix Fietkau 	if (!ppe)
8651ccc723bSFelix Fietkau 		return NULL;
866ba37b7caSFelix Fietkau 
86733fc42deSFelix Fietkau 	rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
86833fc42deSFelix Fietkau 
869ba37b7caSFelix Fietkau 	/* need to allocate a separate device, since it PPE DMA access is
870ba37b7caSFelix Fietkau 	 * not coherent.
871ba37b7caSFelix Fietkau 	 */
872ba37b7caSFelix Fietkau 	ppe->base = base;
873c4f033d9SFelix Fietkau 	ppe->eth = eth;
874ba37b7caSFelix Fietkau 	ppe->dev = dev;
8753fbe4d8cSDaniel Golle 	ppe->version = eth->soc->offload_version;
8763fbe4d8cSDaniel Golle 	ppe->accounting = accounting;
877ba37b7caSFelix Fietkau 
8789d8cb4c0SLorenzo Bianconi 	foe = dmam_alloc_coherent(ppe->dev,
8799d8cb4c0SLorenzo Bianconi 				  MTK_PPE_ENTRIES * soc->foe_entry_size,
880ba37b7caSFelix Fietkau 				  &ppe->foe_phys, GFP_KERNEL);
881ba37b7caSFelix Fietkau 	if (!foe)
882603ea5e7SYan Cangang 		goto err_free_l2_flows;
883ba37b7caSFelix Fietkau 
884ba37b7caSFelix Fietkau 	ppe->foe_table = foe;
885ba37b7caSFelix Fietkau 
886ba2fc48cSLorenzo Bianconi 	foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) *
887ba2fc48cSLorenzo Bianconi 			sizeof(*ppe->foe_flow);
888ba2fc48cSLorenzo Bianconi 	ppe->foe_flow = devm_kzalloc(dev, foe_flow_size, GFP_KERNEL);
889ba2fc48cSLorenzo Bianconi 	if (!ppe->foe_flow)
890603ea5e7SYan Cangang 		goto err_free_l2_flows;
891ba2fc48cSLorenzo Bianconi 
8923fbe4d8cSDaniel Golle 	if (accounting) {
8933fbe4d8cSDaniel Golle 		mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
8943fbe4d8cSDaniel Golle 					  &ppe->mib_phys, GFP_KERNEL);
8953fbe4d8cSDaniel Golle 		if (!mib)
8963fbe4d8cSDaniel Golle 			return NULL;
8973fbe4d8cSDaniel Golle 
8983fbe4d8cSDaniel Golle 		ppe->mib_table = mib;
8993fbe4d8cSDaniel Golle 
9003fbe4d8cSDaniel Golle 		acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
9013fbe4d8cSDaniel Golle 				    GFP_KERNEL);
9023fbe4d8cSDaniel Golle 
9033fbe4d8cSDaniel Golle 		if (!acct)
9043fbe4d8cSDaniel Golle 			return NULL;
9053fbe4d8cSDaniel Golle 
9063fbe4d8cSDaniel Golle 		ppe->acct_table = acct;
9073fbe4d8cSDaniel Golle 	}
9083fbe4d8cSDaniel Golle 
9094ff1a3fcSLorenzo Bianconi 	mtk_ppe_debugfs_init(ppe, index);
910ba37b7caSFelix Fietkau 
9111ccc723bSFelix Fietkau 	return ppe;
912603ea5e7SYan Cangang 
913603ea5e7SYan Cangang err_free_l2_flows:
914603ea5e7SYan Cangang 	rhashtable_destroy(&ppe->l2_flows);
915603ea5e7SYan Cangang 	return NULL;
916603ea5e7SYan Cangang }
917603ea5e7SYan Cangang 
918603ea5e7SYan Cangang void mtk_ppe_deinit(struct mtk_eth *eth)
919603ea5e7SYan Cangang {
920603ea5e7SYan Cangang 	int i;
921603ea5e7SYan Cangang 
922603ea5e7SYan Cangang 	for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) {
923603ea5e7SYan Cangang 		if (!eth->ppe[i])
924603ea5e7SYan Cangang 			return;
925603ea5e7SYan Cangang 		rhashtable_destroy(&eth->ppe[i]->l2_flows);
926603ea5e7SYan Cangang 	}
927ba37b7caSFelix Fietkau }
928ba37b7caSFelix Fietkau 
929ba37b7caSFelix Fietkau static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
930ba37b7caSFelix Fietkau {
931ba37b7caSFelix Fietkau 	static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
932ba37b7caSFelix Fietkau 	int i, k;
933ba37b7caSFelix Fietkau 
9349d8cb4c0SLorenzo Bianconi 	memset(ppe->foe_table, 0,
9359d8cb4c0SLorenzo Bianconi 	       MTK_PPE_ENTRIES * ppe->eth->soc->foe_entry_size);
936ba37b7caSFelix Fietkau 
937ba37b7caSFelix Fietkau 	if (!IS_ENABLED(CONFIG_SOC_MT7621))
938ba37b7caSFelix Fietkau 		return;
939ba37b7caSFelix Fietkau 
940ba37b7caSFelix Fietkau 	/* skip all entries that cross the 1024 byte boundary */
9419d8cb4c0SLorenzo Bianconi 	for (i = 0; i < MTK_PPE_ENTRIES; i += 128) {
9429d8cb4c0SLorenzo Bianconi 		for (k = 0; k < ARRAY_SIZE(skip); k++) {
9439d8cb4c0SLorenzo Bianconi 			struct mtk_foe_entry *hwe;
9449d8cb4c0SLorenzo Bianconi 
9459d8cb4c0SLorenzo Bianconi 			hwe = mtk_foe_get_entry(ppe, i + skip[k]);
9469d8cb4c0SLorenzo Bianconi 			hwe->ib1 |= MTK_FOE_IB1_STATIC;
9479d8cb4c0SLorenzo Bianconi 		}
9489d8cb4c0SLorenzo Bianconi 	}
949ba37b7caSFelix Fietkau }
950ba37b7caSFelix Fietkau 
9514ff1a3fcSLorenzo Bianconi void mtk_ppe_start(struct mtk_ppe *ppe)
952ba37b7caSFelix Fietkau {
953ba37b7caSFelix Fietkau 	u32 val;
954ba37b7caSFelix Fietkau 
9554ff1a3fcSLorenzo Bianconi 	if (!ppe)
9564ff1a3fcSLorenzo Bianconi 		return;
9574ff1a3fcSLorenzo Bianconi 
958ba37b7caSFelix Fietkau 	mtk_ppe_init_foe_table(ppe);
959ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
960ba37b7caSFelix Fietkau 
961ba37b7caSFelix Fietkau 	val = MTK_PPE_TB_CFG_ENTRY_80B |
962ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_NON_L4 |
963ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UNBIND |
964ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP |
965ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UDP |
966ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
967ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
968ba37b7caSFelix Fietkau 			 MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
969ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
970ba37b7caSFelix Fietkau 			 MTK_PPE_KEEPALIVE_DISABLE) |
971ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
972ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
973ba37b7caSFelix Fietkau 			 MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
974ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
975ba37b7caSFelix Fietkau 			 MTK_PPE_ENTRIES_SHIFT);
97603a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
97703a3180eSLorenzo Bianconi 		val |= MTK_PPE_TB_CFG_INFO_SEL;
978ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_TB_CFG, val);
979ba37b7caSFelix Fietkau 
980ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
981ba37b7caSFelix Fietkau 		MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
982ba37b7caSFelix Fietkau 
983ba37b7caSFelix Fietkau 	mtk_ppe_cache_enable(ppe, true);
984ba37b7caSFelix Fietkau 
98503a3180eSLorenzo Bianconi 	val = MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
986ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
987ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP6_6RD |
988ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAT |
989ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAPT |
990ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_DSLITE |
991ba37b7caSFelix Fietkau 	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
99203a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
99303a3180eSLorenzo Bianconi 		val |= MTK_PPE_MD_TOAP_BYP_CRSN0 |
99403a3180eSLorenzo Bianconi 		       MTK_PPE_MD_TOAP_BYP_CRSN1 |
99503a3180eSLorenzo Bianconi 		       MTK_PPE_MD_TOAP_BYP_CRSN2 |
99603a3180eSLorenzo Bianconi 		       MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY;
99703a3180eSLorenzo Bianconi 	else
99803a3180eSLorenzo Bianconi 		val |= MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
99903a3180eSLorenzo Bianconi 		       MTK_PPE_FLOW_CFG_IP4_UDP_FRAG;
1000ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
1001ba37b7caSFelix Fietkau 
1002ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
1003ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
1004ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
1005ba37b7caSFelix Fietkau 
1006ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) |
1007ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
1008ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
1009ba37b7caSFelix Fietkau 
1010ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
1011ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7);
1012ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
1013ba37b7caSFelix Fietkau 
1014ba37b7caSFelix Fietkau 	val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
1015ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
1016ba37b7caSFelix Fietkau 
1017ba37b7caSFelix Fietkau 	val = MTK_PPE_BIND_LIMIT1_FULL |
1018ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
1019ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
1020ba37b7caSFelix Fietkau 
1021ba37b7caSFelix Fietkau 	val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
1022ba37b7caSFelix Fietkau 	      FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
1023ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
1024ba37b7caSFelix Fietkau 
1025ba37b7caSFelix Fietkau 	/* enable PPE */
1026ba37b7caSFelix Fietkau 	val = MTK_PPE_GLO_CFG_EN |
1027ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
1028ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_IP4_CS_DROP |
1029ba37b7caSFelix Fietkau 	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
1030ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
1031ba37b7caSFelix Fietkau 
1032ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
103303a3180eSLorenzo Bianconi 
103403a3180eSLorenzo Bianconi 	if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) {
103503a3180eSLorenzo Bianconi 		ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
103603a3180eSLorenzo Bianconi 		ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
103703a3180eSLorenzo Bianconi 	}
10383fbe4d8cSDaniel Golle 
10393fbe4d8cSDaniel Golle 	if (ppe->accounting && ppe->mib_phys) {
10403fbe4d8cSDaniel Golle 		ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
10413fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
10423fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_EN);
10433fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
10443fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_RD_CLR);
10453fbe4d8cSDaniel Golle 		ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
10463fbe4d8cSDaniel Golle 			MTK_PPE_MIB_CFG_RD_CLR);
10473fbe4d8cSDaniel Golle 	}
1048ba37b7caSFelix Fietkau }
1049ba37b7caSFelix Fietkau 
1050ba37b7caSFelix Fietkau int mtk_ppe_stop(struct mtk_ppe *ppe)
1051ba37b7caSFelix Fietkau {
1052ba37b7caSFelix Fietkau 	u32 val;
1053ba37b7caSFelix Fietkau 	int i;
1054ba37b7caSFelix Fietkau 
10554ff1a3fcSLorenzo Bianconi 	if (!ppe)
10564ff1a3fcSLorenzo Bianconi 		return 0;
10574ff1a3fcSLorenzo Bianconi 
10589d8cb4c0SLorenzo Bianconi 	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
10599d8cb4c0SLorenzo Bianconi 		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, i);
10609d8cb4c0SLorenzo Bianconi 
10619d8cb4c0SLorenzo Bianconi 		hwe->ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
1062ba37b7caSFelix Fietkau 				      MTK_FOE_STATE_INVALID);
10639d8cb4c0SLorenzo Bianconi 	}
1064ba37b7caSFelix Fietkau 
1065ba37b7caSFelix Fietkau 	mtk_ppe_cache_enable(ppe, false);
1066ba37b7caSFelix Fietkau 
1067ba37b7caSFelix Fietkau 	/* disable offload engine */
1068ba37b7caSFelix Fietkau 	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
1069ba37b7caSFelix Fietkau 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
1070ba37b7caSFelix Fietkau 
1071ba37b7caSFelix Fietkau 	/* disable aging */
1072ba37b7caSFelix Fietkau 	val = MTK_PPE_TB_CFG_AGE_NON_L4 |
1073ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UNBIND |
1074ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP |
1075ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_UDP |
1076ba37b7caSFelix Fietkau 	      MTK_PPE_TB_CFG_AGE_TCP_FIN;
1077ba37b7caSFelix Fietkau 	ppe_clear(ppe, MTK_PPE_TB_CFG, val);
1078ba37b7caSFelix Fietkau 
1079ba37b7caSFelix Fietkau 	return mtk_ppe_wait_busy(ppe);
1080ba37b7caSFelix Fietkau }
1081