162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/debugfs.h>
662306a36Sopenharmony_ci#include "mtk_eth_soc.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistruct mtk_flow_addr_info
962306a36Sopenharmony_ci{
1062306a36Sopenharmony_ci	void *src, *dest;
1162306a36Sopenharmony_ci	u16 *src_port, *dest_port;
1262306a36Sopenharmony_ci	bool ipv6;
1362306a36Sopenharmony_ci};
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic const char *mtk_foe_entry_state_str(int state)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	static const char * const state_str[] = {
1862306a36Sopenharmony_ci		[MTK_FOE_STATE_INVALID] = "INV",
1962306a36Sopenharmony_ci		[MTK_FOE_STATE_UNBIND] = "UNB",
2062306a36Sopenharmony_ci		[MTK_FOE_STATE_BIND] = "BND",
2162306a36Sopenharmony_ci		[MTK_FOE_STATE_FIN] = "FIN",
2262306a36Sopenharmony_ci	};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (state >= ARRAY_SIZE(state_str) || !state_str[state])
2562306a36Sopenharmony_ci		return "UNK";
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	return state_str[state];
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const char *mtk_foe_pkt_type_str(int type)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	static const char * const type_str[] = {
3362306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
3462306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
3562306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
3662306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
3762306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
3862306a36Sopenharmony_ci		[MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
3962306a36Sopenharmony_ci	};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (type >= ARRAY_SIZE(type_str) || !type_str[type])
4262306a36Sopenharmony_ci		return "UNKNOWN";
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return type_str[type];
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void
4862306a36Sopenharmony_cimtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	__be32 n_addr[4];
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (!ipv6) {
5462306a36Sopenharmony_ci		seq_printf(m, "%pI4h", addr);
5562306a36Sopenharmony_ci		return;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(n_addr); i++)
5962306a36Sopenharmony_ci		n_addr[i] = htonl(addr[i]);
6062306a36Sopenharmony_ci	seq_printf(m, "%pI6", n_addr);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void
6462306a36Sopenharmony_cimtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	mtk_print_addr(m, ai->src, ai->ipv6);
6762306a36Sopenharmony_ci	if (ai->src_port)
6862306a36Sopenharmony_ci		seq_printf(m, ":%d", *ai->src_port);
6962306a36Sopenharmony_ci	seq_printf(m, "->");
7062306a36Sopenharmony_ci	mtk_print_addr(m, ai->dest, ai->ipv6);
7162306a36Sopenharmony_ci	if (ai->dest_port)
7262306a36Sopenharmony_ci		seq_printf(m, ":%d", *ai->dest_port);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int
7662306a36Sopenharmony_cimtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct mtk_ppe *ppe = m->private;
7962306a36Sopenharmony_ci	int i;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
8262306a36Sopenharmony_ci		struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
8362306a36Sopenharmony_ci		struct mtk_foe_mac_info *l2;
8462306a36Sopenharmony_ci		struct mtk_flow_addr_info ai = {};
8562306a36Sopenharmony_ci		struct mtk_foe_accounting *acct;
8662306a36Sopenharmony_ci		unsigned char h_source[ETH_ALEN];
8762306a36Sopenharmony_ci		unsigned char h_dest[ETH_ALEN];
8862306a36Sopenharmony_ci		int type, state;
8962306a36Sopenharmony_ci		u32 ib2;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
9362306a36Sopenharmony_ci		if (!state)
9462306a36Sopenharmony_ci			continue;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		if (bind && state != MTK_FOE_STATE_BIND)
9762306a36Sopenharmony_ci			continue;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		acct = mtk_foe_entry_get_mib(ppe, i, NULL);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1);
10262306a36Sopenharmony_ci		seq_printf(m, "%05x %s %7s", i,
10362306a36Sopenharmony_ci			   mtk_foe_entry_state_str(state),
10462306a36Sopenharmony_ci			   mtk_foe_pkt_type_str(type));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		switch (type) {
10762306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
10862306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
10962306a36Sopenharmony_ci			ai.src_port = &entry->ipv4.orig.src_port;
11062306a36Sopenharmony_ci			ai.dest_port = &entry->ipv4.orig.dest_port;
11162306a36Sopenharmony_ci			fallthrough;
11262306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
11362306a36Sopenharmony_ci			ai.src = &entry->ipv4.orig.src_ip;
11462306a36Sopenharmony_ci			ai.dest = &entry->ipv4.orig.dest_ip;
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
11762306a36Sopenharmony_ci			ai.src_port = &entry->ipv6.src_port;
11862306a36Sopenharmony_ci			ai.dest_port = &entry->ipv6.dest_port;
11962306a36Sopenharmony_ci			fallthrough;
12062306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
12162306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV6_6RD:
12262306a36Sopenharmony_ci			ai.src = &entry->ipv6.src_ip;
12362306a36Sopenharmony_ci			ai.dest = &entry->ipv6.dest_ip;
12462306a36Sopenharmony_ci			ai.ipv6 = true;
12562306a36Sopenharmony_ci			break;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		seq_printf(m, " orig=");
12962306a36Sopenharmony_ci		mtk_print_addr_info(m, &ai);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		switch (type) {
13262306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
13362306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
13462306a36Sopenharmony_ci			ai.src_port = &entry->ipv4.new.src_port;
13562306a36Sopenharmony_ci			ai.dest_port = &entry->ipv4.new.dest_port;
13662306a36Sopenharmony_ci			fallthrough;
13762306a36Sopenharmony_ci		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
13862306a36Sopenharmony_ci			ai.src = &entry->ipv4.new.src_ip;
13962306a36Sopenharmony_ci			ai.dest = &entry->ipv4.new.dest_ip;
14062306a36Sopenharmony_ci			seq_printf(m, " new=");
14162306a36Sopenharmony_ci			mtk_print_addr_info(m, &ai);
14262306a36Sopenharmony_ci			break;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
14662306a36Sopenharmony_ci			l2 = &entry->ipv6.l2;
14762306a36Sopenharmony_ci			ib2 = entry->ipv6.ib2;
14862306a36Sopenharmony_ci		} else {
14962306a36Sopenharmony_ci			l2 = &entry->ipv4.l2;
15062306a36Sopenharmony_ci			ib2 = entry->ipv4.ib2;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		*((__be32 *)h_source) = htonl(l2->src_mac_hi);
15462306a36Sopenharmony_ci		*((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
15562306a36Sopenharmony_ci		*((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
15662306a36Sopenharmony_ci		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		seq_printf(m, " eth=%pM->%pM etype=%04x"
15962306a36Sopenharmony_ci			      " vlan=%d,%d ib1=%08x ib2=%08x"
16062306a36Sopenharmony_ci			      " packets=%llu bytes=%llu\n",
16162306a36Sopenharmony_ci			   h_source, h_dest, ntohs(l2->etype),
16262306a36Sopenharmony_ci			   l2->vlan1, l2->vlan2, entry->ib1, ib2,
16362306a36Sopenharmony_ci			   acct ? acct->packets : 0, acct ? acct->bytes : 0);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int
17062306a36Sopenharmony_cimtk_ppe_debugfs_foe_all_show(struct seq_file *m, void *private)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return mtk_ppe_debugfs_foe_show(m, private, false);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mtk_ppe_debugfs_foe_all);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int
17762306a36Sopenharmony_cimtk_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	return mtk_ppe_debugfs_foe_show(m, private, true);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mtk_ppe_debugfs_foe_bind);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct dentry *root;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	snprintf(ppe->dirname, sizeof(ppe->dirname), "ppe%d", index);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	root = debugfs_create_dir(ppe->dirname, NULL);
19062306a36Sopenharmony_ci	debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_all_fops);
19162306a36Sopenharmony_ci	debugfs_create_file("bind", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_bind_fops);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
195