[][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/trm-mcu.c b/package-21.02/kernel/tops/src/trm-mcu.c
new file mode 100644
index 0000000..4550122
--- /dev/null
+++ b/package-21.02/kernel/tops/src/trm-mcu.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Alvin Kuo <alvin.kuog@mediatek.com>
+ *         Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "internal.h"
+#include "mcu.h"
+#include "trm-fs.h"
+#include "trm-mcu.h"
+#include "trm.h"
+
+#define TOPS_OCD_RETRY_TIMES		(3)
+
+#define TOPS_OCD_DCRSET			(0x200C)
+#define ENABLE_OCD			(1 << 0)
+#define DEBUG_INT			(1 << 1)
+
+#define TOPS_OCD_DSR			(0x2010)
+#define EXEC_DONE			(1 << 0)
+#define EXEC_EXCE			(1 << 1)
+#define EXEC_BUSY			(1 << 2)
+#define STOPPED				(1 << 4)
+#define DEBUG_PEND_HOST			(1 << 17)
+
+#define TOPS_OCD_DDR			(0x2014)
+
+#define TOPS_OCD_DIR0EXEC		(0x201C)
+
+struct tops_ocd_dev {
+	void __iomem *base;
+	u32 base_offset;
+	struct clk *debugsys_clk;
+};
+
+static struct tops_ocd_dev ocd;
+
+struct core_dump_fram cd_frams[CORE_TOPS_NUM];
+
+static inline void ocd_write(struct tops_ocd_dev *ocd, u32 reg, u32 val)
+{
+	writel(val, ocd->base + ocd->base_offset + reg);
+}
+
+static inline u32 ocd_read(struct tops_ocd_dev *ocd, u32 reg)
+{
+	return readl(ocd->base + ocd->base_offset + reg);
+}
+
+static inline void ocd_set(struct tops_ocd_dev *ocd, u32 reg, u32 mask)
+{
+	setbits(ocd->base + ocd->base_offset + reg, mask);
+}
+
+static inline void ocd_clr(struct tops_ocd_dev *ocd, u32 reg, u32 mask)
+{
+	clrbits(ocd->base + ocd->base_offset + reg, mask);
+}
+
+static int core_exec_instr(u32 instr)
+{
+	u32 rty = 0;
+	int ret;
+
+	ocd_set(&ocd, TOPS_OCD_DSR, EXEC_DONE);
+	ocd_set(&ocd, TOPS_OCD_DSR, EXEC_EXCE);
+
+	ocd_write(&ocd, TOPS_OCD_DIR0EXEC, instr);
+
+	while ((ocd_read(&ocd, TOPS_OCD_DSR) & EXEC_BUSY)) {
+		if (rty++ < TOPS_OCD_RETRY_TIMES) {
+			usleep_range(1000, 1500);
+		} else {
+			TRM_ERR("run instruction(0x%x) timeout\n", instr);
+			ret = -1;
+			goto out;
+		}
+	}
+
+	ret = ocd_read(&ocd, TOPS_OCD_DSR) & EXEC_EXCE ? -1 : 0;
+	if (ret)
+		TRM_ERR("run instruction(0x%x) fail\n", instr);
+
+out:
+	return ret;
+}
+
+static int core_dump(struct core_dump_fram *cd_fram)
+{
+	cd_fram->magic = CORE_DUMP_FRAM_MAGIC;
+	cd_fram->num_areg = XCHAL_NUM_AREG;
+
+	/*
+	 * save
+	 * PC, PS, WINDOWSTART, WINDOWBASE,
+	 * EPC1, EXCCAUSE, EXCVADDR, EXCSAVE1
+	 */
+	core_exec_instr(0x13f500);
+
+	core_exec_instr(0x03b500);
+	core_exec_instr(0x136800);
+	cd_fram->pc = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03e600);
+	core_exec_instr(0x136800);
+	cd_fram->ps = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x034900);
+	core_exec_instr(0x136800);
+	cd_fram->windowstart = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x034800);
+	core_exec_instr(0x136800);
+	cd_fram->windowbase = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03b100);
+	core_exec_instr(0x136800);
+	cd_fram->epc1 = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03e800);
+	core_exec_instr(0x136800);
+	cd_fram->exccause = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03ee00);
+	core_exec_instr(0x136800);
+	cd_fram->excvaddr = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03d100);
+	core_exec_instr(0x136800);
+	cd_fram->excsave1 = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x03f500);
+
+	/*
+	 * save
+	 * a0, a1, a2, a3, a4, a5, a6, a7
+	 */
+	core_exec_instr(0x136800);
+	cd_fram->areg[0] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136810);
+	cd_fram->areg[1] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136820);
+	cd_fram->areg[2] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136830);
+	cd_fram->areg[3] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136840);
+	cd_fram->areg[4] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136850);
+	cd_fram->areg[5] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136860);
+	cd_fram->areg[6] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136870);
+	cd_fram->areg[7] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	/*
+	 * save
+	 * a8, a9, a10, a11, a12, a13, a14, a15
+	 */
+	core_exec_instr(0x136880);
+	cd_fram->areg[8] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136890);
+	cd_fram->areg[9] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368a0);
+	cd_fram->areg[10] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368b0);
+	cd_fram->areg[11] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368c0);
+	cd_fram->areg[12] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368d0);
+	cd_fram->areg[13] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368e0);
+	cd_fram->areg[14] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368f0);
+	cd_fram->areg[15] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x408020);
+
+	/*
+	 * save
+	 * a16, a17, a18, a19, a20, a21, a22, a23
+	 */
+	core_exec_instr(0x136880);
+	cd_fram->areg[16] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136890);
+	cd_fram->areg[17] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368a0);
+	cd_fram->areg[18] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368b0);
+	cd_fram->areg[19] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368c0);
+	cd_fram->areg[20] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368d0);
+	cd_fram->areg[21] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368e0);
+	cd_fram->areg[22] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368f0);
+	cd_fram->areg[23] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x408020);
+
+	/*
+	 * save
+	 * a24, a25, a26, a27, a28, a29, a30, a31
+	 */
+	core_exec_instr(0x136880);
+	cd_fram->areg[24] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x136890);
+	cd_fram->areg[25] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368a0);
+	cd_fram->areg[26] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368b0);
+	cd_fram->areg[27] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368c0);
+	cd_fram->areg[28] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368d0);
+	cd_fram->areg[29] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368e0);
+	cd_fram->areg[30] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x1368f0);
+	cd_fram->areg[31] = ocd_read(&ocd, TOPS_OCD_DDR);
+
+	core_exec_instr(0x408040);
+
+	core_exec_instr(0xf1e000);
+
+	return 0;
+}
+
+static int __mtk_trm_mcu_core_dump(enum core_id core)
+{
+	u32 rty = 0;
+	int ret;
+
+	ocd.base_offset = (core == CORE_MGMT) ? (0x0) : (0x5000 + (core * 0x4000));
+
+	/* enable OCD */
+	ocd_set(&ocd, TOPS_OCD_DCRSET, ENABLE_OCD);
+
+	/* assert debug interrupt to core */
+	ocd_set(&ocd, TOPS_OCD_DCRSET, DEBUG_INT);
+
+	/* wait core into stopped state */
+	while (!(ocd_read(&ocd, TOPS_OCD_DSR) & STOPPED)) {
+		if (rty++ < TOPS_OCD_RETRY_TIMES) {
+			usleep_range(10000, 15000);
+		} else {
+			TRM_ERR("wait core(%d) into stopped state timeout\n", core);
+			ret = -1;
+			goto out;
+		}
+	}
+
+	/* deassert debug interrupt to core */
+	ocd_set(&ocd, TOPS_OCD_DSR, DEBUG_PEND_HOST);
+
+	/* dump core's registers and let core into running state */
+	ret = core_dump(&cd_frams[core]);
+
+out:
+	return ret;
+}
+
+int mtk_trm_mcu_core_dump(void)
+{
+	enum core_id core;
+	int ret;
+
+	ret = clk_prepare_enable(ocd.debugsys_clk);
+	if (ret) {
+		TRM_ERR("debugsys clk enable failed: %d\n", ret);
+		goto out;
+	}
+
+	memset(cd_frams, 0, sizeof(cd_frams));
+
+	for (core = CORE_OFFLOAD_0; core <= CORE_MGMT; core++) {
+		ret = __mtk_trm_mcu_core_dump(core);
+		if (ret)
+			break;
+	}
+
+	clk_disable_unprepare(ocd.debugsys_clk);
+
+out:
+	return ret;
+}
+
+static int mtk_tops_ocd_probe(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	int ret;
+
+	trm_dev = &pdev->dev;
+
+	ocd.debugsys_clk = devm_clk_get(trm_dev, "debugsys");
+	if (IS_ERR(ocd.debugsys_clk)) {
+		TRM_ERR("get debugsys clk failed: %ld\n", PTR_ERR(ocd.debugsys_clk));
+		return PTR_ERR(ocd.debugsys_clk);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tops-ocd-base");
+	if (!res)
+		return -ENXIO;
+
+	ocd.base = devm_ioremap(trm_dev, res->start, resource_size(res));
+	if (!ocd.base)
+		return -ENOMEM;
+
+	ret = mtk_trm_fs_init();
+	if (ret)
+		return ret;
+
+	TRM_INFO("tops-ocd init done\n");
+
+	return 0;
+}
+
+static int mtk_tops_ocd_remove(struct platform_device *pdev)
+{
+	mtk_trm_fs_deinit();
+
+	return 0;
+}
+
+static struct of_device_id mtk_tops_ocd_match[] = {
+	{ .compatible = "mediatek,tops-ocd", },
+	{ },
+};
+
+static struct platform_driver mtk_tops_ocd_driver = {
+	.probe = mtk_tops_ocd_probe,
+	.remove = mtk_tops_ocd_remove,
+	.driver = {
+		.name = "mediatek,tops-ocd",
+		.owner = THIS_MODULE,
+		.of_match_table = mtk_tops_ocd_match,
+	},
+};
+
+int __init mtk_tops_trm_mcu_init(void)
+{
+	return platform_driver_register(&mtk_tops_ocd_driver);
+}
+
+void __exit mtk_tops_trm_mcu_exit(void)
+{
+	platform_driver_unregister(&mtk_tops_ocd_driver);
+}