[][openwrt][mt7988][tops][TOPS Alpha release]

[Description]
Add alpha version of TOPS(tunnel offload processor system) and tops-tool
package.

TOPS package supports tunnel protocol HW offload. The support offload
tunnel protocols for Alpha version are L2oGRE and L2TPv2.
Notice that, TOPS only guarantees that inner packets are TCP. It is still
unstable for UDP inner packet flow.

tops-tool package provides several debug features such as logger, coredump
for TOPS.

[Release-log]
N/A

Change-Id: Iab6e4a89bebbe42c967f28e0c9e9c0611673f354
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7852683
diff --git a/package-21.02/kernel/tops/src/ctrl.c b/package-21.02/kernel/tops/src/ctrl.c
new file mode 100644
index 0000000..e425e68
--- /dev/null
+++ b/package-21.02/kernel/tops/src/ctrl.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include <linux/device.h>
+
+#include "firmware.h"
+#include "hpdma.h"
+#include "internal.h"
+#include "mcu.h"
+#include "netsys.h"
+#include "trm.h"
+#include "tunnel.h"
+#include "wdt.h"
+
+static const char *tops_role_name[__TOPS_ROLE_TYPE_MAX] = {
+	[TOPS_ROLE_TYPE_MGMT] = "tops-mgmt",
+	[TOPS_ROLE_TYPE_CLUSTER] = "tops-offload",
+};
+
+static ssize_t mtk_tops_fw_info_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	enum tops_role_type rtype;
+	struct tm tm = {0};
+	const char *value;
+	const char *prop;
+	int len = 0;
+	u32 nattr;
+	u32 i;
+
+	for (rtype = TOPS_ROLE_TYPE_MGMT; rtype < __TOPS_ROLE_TYPE_MAX; rtype++) {
+		mtk_tops_fw_get_built_date(rtype, &tm);
+
+		len += snprintf(buf + len, PAGE_SIZE - len,
+				"%s FW information:\n", tops_role_name[rtype]);
+		len += snprintf(buf + len, PAGE_SIZE - len,
+				"Git revision:\t%llx\n",
+				mtk_tops_fw_get_git_commit_id(rtype));
+		len += snprintf(buf + len, PAGE_SIZE - len,
+				"Build date:\t%04ld/%02d/%02d %02d:%02d:%02d\n",
+				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+				tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+		nattr = mtk_tops_fw_attr_get_num(rtype);
+
+		for (i = 0; i < nattr; i++) {
+			prop = mtk_tops_fw_attr_get_property(rtype, i);
+			if (!prop)
+				continue;
+
+			value = mtk_tops_fw_attr_get_value(rtype, prop);
+
+			len += snprintf(buf + len, PAGE_SIZE - len,
+					"%s:\t%s\n", prop, value);
+		}
+		len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+	}
+
+	return len;
+}
+
+static int mtk_tops_ctrl_fetch_port(const char *buf, int *ofs, u16 *port)
+{
+	int nchar = 0;
+	int ret;
+	u16 p = 0;
+
+	ret = sscanf(buf + *ofs, "%hu %n", &p, &nchar);
+	if (ret != 1)
+		return -EPERM;
+
+	*port = htons(p);
+
+	*ofs += nchar;
+
+	return nchar;
+}
+
+static int mtk_tops_ctrl_fetch_ip(const char *buf, int *ofs, u32 *ip)
+{
+	int nchar = 0;
+	int ret = 0;
+	u8 tmp[4];
+
+	ret = sscanf(buf + *ofs, "%hhu.%hhu.%hhu.%hhu %n",
+		&tmp[3], &tmp[2], &tmp[1], &tmp[0], &nchar);
+	if (ret != 4)
+		return -EPERM;
+
+	*ip = tmp[0] | tmp[1] << 8 | tmp[2] << 16 | tmp[3] << 24;
+
+	*ofs += nchar;
+
+	return nchar;
+}
+
+static int mtk_tops_ctrl_fetch_mac(const char *buf, int *ofs, u8 *mac)
+{
+	int nchar = 0;
+	int ret = 0;
+
+	ret = sscanf(buf + *ofs, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %n",
+		&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5], &nchar);
+	if (ret != 6)
+		return -EPERM;
+
+	*ofs += nchar;
+
+	return 0;
+}
+
+static int mtk_tops_ctrl_add_tnl(const char *buf)
+{
+	struct tops_tnl_params tnl_params = {0};
+	struct tops_tnl_info *tnl_info;
+	struct tops_tnl_type *tnl_type;
+	char tnl_type_name[21] = {0};
+	int ofs = 0;
+	int ret = 0;
+
+	ret = sscanf(buf, "%20s %n", tnl_type_name, &ofs);
+	if (ret != 1)
+		return -EPERM;
+
+	tnl_type = mtk_tops_tnl_type_get_by_name(tnl_type_name);
+	if (unlikely(!tnl_type || !tnl_type->tnl_debug_param_setup))
+		return -ENODEV;
+
+	ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.daddr);
+	if (ret < 0)
+		return ret;
+
+	ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.saddr);
+	if (ret < 0)
+		return ret;
+
+	ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.dip);
+	if (ret < 0)
+		return ret;
+
+	ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.sip);
+	if (ret < 0)
+		return ret;
+
+	ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.dport);
+	if (ret < 0)
+		return ret;
+
+	ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.sport);
+	if (ret < 0)
+		return ret;
+
+	ret = tnl_type->tnl_debug_param_setup(buf, &ofs, &tnl_params);
+	if (ret < 0)
+		return ret;
+
+	tnl_params.flag |= TNL_DECAP_ENABLE;
+	tnl_params.flag |= TNL_ENCAP_ENABLE;
+	tnl_params.tops_entry_proto = tnl_type->tops_entry;
+
+	tnl_info = mtk_tops_tnl_info_alloc();
+	if (IS_ERR(tnl_info))
+		return -ENOMEM;
+
+	tnl_info->flag |= TNL_INFO_DEBUG;
+	memcpy(&tnl_info->cache, &tnl_params, sizeof(struct tops_tnl_params));
+
+	mtk_tops_tnl_info_hash(tnl_info);
+
+	mtk_tops_tnl_info_submit(tnl_info);
+
+	return 0;
+}
+
+static ssize_t mtk_tops_tnl_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	char cmd[21] = {0};
+	int nchar = 0;
+	int ret = 0;
+
+	ret = sscanf(buf, "%20s %n", cmd, &nchar);
+
+	if (ret != 1)
+		return -EPERM;
+
+	if (!strcmp(cmd, "NEW_TNL")) {
+		ret = mtk_tops_ctrl_add_tnl(buf + nchar);
+		if (ret)
+			return ret;
+	}
+
+	return count;
+}
+
+static int mtk_tops_trm_fetch_setting(const char *buf,
+				      int *ofs,
+				      char *name,
+				      u32 *offset,
+				      u32 *size,
+				      u8 *enable)
+{
+	int nchar = 0;
+	int ret = 0;
+
+	ret = sscanf(buf + *ofs, "%31s %x %x %hhx %n",
+		name, offset, size, enable, &nchar);
+	if (ret != 4)
+		return -EPERM;
+
+	*ofs += nchar;
+
+	return nchar;
+}
+
+static ssize_t mtk_tops_trm_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	char name[TRM_CONFIG_NAME_MAX_LEN] = { 0 };
+	char cmd[21] = { 0 };
+	int nchar = 0;
+	int ret = 0;
+	u32 offset;
+	u8 enable;
+	u32 size;
+
+	ret = sscanf(buf, "%20s %n", cmd, &nchar);
+	if (ret != 1)
+		return -EPERM;
+
+	if (!strcmp(cmd, "trm_dump")) {
+		ret = mtk_trm_dump(TRM_RSN_NULL);
+		if (ret)
+			return ret;
+	} else if (!strcmp(cmd, "trm_cfg_setup")) {
+		ret = mtk_tops_trm_fetch_setting(buf, &nchar,
+			name, &offset, &size, &enable);
+		if (ret < 0)
+			return ret;
+
+		ret = mtk_trm_cfg_setup(name, offset, size, enable);
+		if (ret)
+			return ret;
+	}
+
+	return count;
+}
+
+static ssize_t mtk_tops_wdt_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	char cmd[21] = {0};
+	u32 core = 0;
+	u32 i;
+	int ret;
+
+	ret = sscanf(buf, "%20s %x", cmd, &core);
+	if (ret != 2)
+		return -EPERM;
+
+	core &= CORE_TOPS_MASK;
+	if (!strcmp(cmd, "WDT_TO")) {
+		for (i = 0; i < CORE_TOPS_NUM; i++) {
+			if (core & 0x1)
+				mtk_tops_wdt_trigger_timeout(i);
+			core >>= 1;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR_RO(mtk_tops_fw_info);
+static DEVICE_ATTR_WO(mtk_tops_tnl);
+static DEVICE_ATTR_WO(mtk_tops_trm);
+static DEVICE_ATTR_WO(mtk_tops_wdt);
+
+static struct attribute *mtk_tops_attributes[] = {
+	&dev_attr_mtk_tops_fw_info.attr,
+	&dev_attr_mtk_tops_tnl.attr,
+	&dev_attr_mtk_tops_trm.attr,
+	&dev_attr_mtk_tops_wdt.attr,
+	NULL,
+};
+
+static const struct attribute_group mtk_tops_attr_group = {
+	.name = "mtk_tops",
+	.attrs = mtk_tops_attributes,
+};
+
+int mtk_tops_ctrl_init(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &mtk_tops_attr_group);
+	if (ret) {
+		TOPS_ERR("create sysfs failed\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+void mtk_tops_ctrl_deinit(struct platform_device *pdev)
+{
+	sysfs_remove_group(&pdev->dev.kobj, &mtk_tops_attr_group);
+}