[][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/mbox.c b/package-21.02/kernel/tops/src/mbox.c
new file mode 100644
index 0000000..c10cbca
--- /dev/null
+++ b/package-21.02/kernel/tops/src/mbox.c
@@ -0,0 +1,574 @@
+// 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/io.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/ktime.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "mcu.h"
+#include "mbox.h"
+#include "internal.h"
+
+#define MBOX_SEND_TIMEOUT		(2000)
+
+struct mailbox_reg {
+	u32 cmd_set_reg;
+	u32 cmd_clr_reg;
+	u32 msg_reg;
+};
+
+struct mailbox_core {
+	struct list_head mdev_list;
+	u32 registered_cmd;
+	spinlock_t lock;
+};
+
+struct mailbox_hw {
+	struct mailbox_core core[MBOX_ACT_MAX][CORE_MAX];
+	struct device *dev;
+	void __iomem *base;
+};
+
+static struct mailbox_hw mbox;
+
+static inline void mbox_write(u32 reg, u32 val)
+{
+	writel(val, mbox.base + reg);
+}
+
+static inline void mbox_set(u32 reg, u32 mask)
+{
+	setbits(mbox.base + reg, mask);
+}
+
+static inline void mbox_clr(u32 reg, u32 mask)
+{
+	clrbits(mbox.base + reg, mask);
+}
+
+static inline void mbox_rmw(u32 reg, u32 mask, u32 val)
+{
+	clrsetbits(mbox.base + reg, mask, val);
+}
+
+static inline u32 mbox_read(u32 reg)
+{
+	return readl(mbox.base + reg);
+}
+
+static inline void mbox_fill_msg(enum mbox_msg_cnt cnt, struct mailbox_msg *msg,
+				 struct mailbox_reg *mbox_reg)
+{
+	if (cnt == MBOX_RET_MSG4)
+		goto send_msg4;
+	else if (cnt == MBOX_RET_MSG3)
+		goto send_msg3;
+	else if (cnt == MBOX_RET_MSG2)
+		goto send_msg2;
+	else if (cnt == MBOX_RET_MSG1)
+		goto send_msg1;
+	else
+		return;
+
+send_msg4:
+	mbox_write(mbox_reg->msg_reg + 0x10, msg->msg4);
+send_msg3:
+	mbox_write(mbox_reg->msg_reg + 0xC, msg->msg3);
+send_msg2:
+	mbox_write(mbox_reg->msg_reg + 0x8, msg->msg3);
+send_msg1:
+	mbox_write(mbox_reg->msg_reg + 0x4, msg->msg1);
+}
+
+static inline void mbox_clear_msg(enum mbox_msg_cnt cnt,
+				  struct mailbox_reg *mbox_reg)
+{
+	if (cnt == MBOX_NO_RET_MSG)
+		goto clear_msg4;
+	else if (cnt == MBOX_RET_MSG1)
+		goto clear_msg3;
+	else if (cnt == MBOX_RET_MSG2)
+		goto clear_msg2;
+	else if (cnt == MBOX_RET_MSG3)
+		goto clear_msg1;
+	else
+		return;
+
+clear_msg4:
+	mbox_write(mbox_reg->msg_reg + 0x4, 0);
+clear_msg3:
+	mbox_write(mbox_reg->msg_reg + 0x8, 0);
+clear_msg2:
+	mbox_write(mbox_reg->msg_reg + 0xC, 0);
+clear_msg1:
+	mbox_write(mbox_reg->msg_reg + 0x10, 0);
+}
+
+static void exec_mbox_handler(enum core_id core, struct mailbox_reg *mbox_reg)
+{
+	struct mailbox_core *mcore = &mbox.core[MBOX_RECV][core];
+	struct mailbox_dev *mdev = NULL;
+	struct mailbox_msg msg = {0};
+	enum mbox_msg_cnt ret = 0;
+	u32 cmd_id = 0;
+
+	cmd_id = mbox_read(mbox_reg->msg_reg);
+
+	list_for_each_entry(mdev, &mcore->mdev_list, list) {
+		if (mdev->cmd_id == cmd_id) {
+			if (!mdev->mbox_handler)
+				goto out;
+
+			/* setup msg for handler */
+			msg.msg1 = mbox_read(mbox_reg->msg_reg + 0x4);
+			msg.msg2 = mbox_read(mbox_reg->msg_reg + 0x8);
+			msg.msg3 = mbox_read(mbox_reg->msg_reg + 0xC);
+			msg.msg4 = mbox_read(mbox_reg->msg_reg + 0x10);
+
+			ret = mdev->mbox_handler(mdev, &msg);
+
+			mbox_fill_msg(ret, &msg, mbox_reg);
+
+			break;
+		}
+	}
+out:
+	mbox_write(mbox_reg->msg_reg, 0);
+	mbox_clear_msg(ret, mbox_reg);
+
+	/* clear cmd */
+	mbox_write(mbox_reg->cmd_clr_reg, 0xFFFFFFFF);
+}
+
+static irqreturn_t mtk_tops_mbox_handler(int irq, void *dev_id)
+{
+	struct mailbox_reg mreg = {0};
+	u32 cluster_reg = 0;
+	u32 top_reg = 0;
+
+	top_reg = mbox_read(TOPS_TOP_AP_SLOT);
+	cluster_reg = mbox_read(TOPS_CLUST0_AP_SLOT);
+
+	if (top_reg & MBOX_TOP_MBOX_FROM_CM) {
+		mreg.cmd_set_reg = TOPS_TOP_CM_TO_AP_CMD_SET;
+		mreg.cmd_clr_reg = TOPS_TOP_CM_TO_AP_CMD_CLR;
+		mreg.msg_reg = TOPS_TOP_CM_TO_AP_MSG_N(0);
+		exec_mbox_handler(CORE_MGMT, &mreg);
+	}
+	if (cluster_reg & MBOX_CLUST0_MBOX_FROM_C0) {
+		mreg.cmd_set_reg = TOPS_CLUST0_CX_TO_AP_CMD_SET(0);
+		mreg.cmd_clr_reg = TOPS_CLUST0_CX_TO_AP_CMD_CLR(0);
+		mreg.msg_reg = TOPS_CLUST0_CX_TO_AP_MSG_N(0, 0);
+		exec_mbox_handler(CORE_OFFLOAD_0, &mreg);
+	}
+	if (cluster_reg & MBOX_CLUST0_MBOX_FROM_C1) {
+		mreg.cmd_set_reg = TOPS_CLUST0_CX_TO_AP_CMD_SET(1);
+		mreg.cmd_clr_reg = TOPS_CLUST0_CX_TO_AP_CMD_CLR(1);
+		mreg.msg_reg = TOPS_CLUST0_CX_TO_AP_MSG_N(1, 0);
+		exec_mbox_handler(CORE_OFFLOAD_1, &mreg);
+	}
+	if (cluster_reg & MBOX_CLUST0_MBOX_FROM_C2) {
+		mreg.cmd_set_reg = TOPS_CLUST0_CX_TO_AP_CMD_SET(2);
+		mreg.cmd_clr_reg = TOPS_CLUST0_CX_TO_AP_CMD_CLR(2);
+		mreg.msg_reg = TOPS_CLUST0_CX_TO_AP_MSG_N(2, 0);
+		exec_mbox_handler(CORE_OFFLOAD_2, &mreg);
+	}
+	if (cluster_reg & MBOX_CLUST0_MBOX_FROM_C3) {
+		mreg.cmd_set_reg = TOPS_CLUST0_CX_TO_AP_CMD_SET(3);
+		mreg.cmd_clr_reg = TOPS_CLUST0_CX_TO_AP_CMD_CLR(3);
+		mreg.msg_reg = TOPS_CLUST0_CX_TO_AP_MSG_N(3, 0);
+		exec_mbox_handler(CORE_OFFLOAD_3, &mreg);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mbox_get_send_reg(struct mailbox_dev *mdev,
+			     struct mailbox_reg *mbox_reg)
+{
+	if (!mdev) {
+		dev_notice(mbox.dev, "no mdev specified!\n");
+		return -EINVAL;
+	}
+
+	if (mdev->core == CORE_MGMT) {
+		mbox_reg->cmd_set_reg = TOPS_TOP_AP_TO_CM_CMD_SET;
+		mbox_reg->cmd_clr_reg = TOPS_TOP_AP_TO_CM_CMD_CLR;
+		mbox_reg->msg_reg = TOPS_TOP_AP_TO_CM_MSG_N(0);
+	} else if (mdev->core == CORE_OFFLOAD_0) {
+		mbox_reg->cmd_set_reg = TOPS_CLUST0_AP_TO_CX_CMD_SET(0);
+		mbox_reg->cmd_clr_reg = TOPS_CLUST0_AP_TO_CX_CMD_CLR(0);
+		mbox_reg->msg_reg = TOPS_CLUST0_AP_TO_CX_MSG_N(0, 0);
+	} else if (mdev->core == CORE_OFFLOAD_1) {
+		mbox_reg->cmd_set_reg = TOPS_CLUST0_AP_TO_CX_CMD_SET(1);
+		mbox_reg->cmd_clr_reg = TOPS_CLUST0_AP_TO_CX_CMD_CLR(1);
+		mbox_reg->msg_reg = TOPS_CLUST0_AP_TO_CX_MSG_N(1, 0);
+	} else if (mdev->core == CORE_OFFLOAD_2) {
+		mbox_reg->cmd_set_reg = TOPS_CLUST0_AP_TO_CX_CMD_SET(2);
+		mbox_reg->cmd_clr_reg = TOPS_CLUST0_AP_TO_CX_CMD_CLR(2);
+		mbox_reg->msg_reg = TOPS_CLUST0_AP_TO_CX_MSG_N(2, 0);
+	} else if (mdev->core == CORE_OFFLOAD_3) {
+		mbox_reg->cmd_set_reg = TOPS_CLUST0_AP_TO_CX_CMD_SET(3);
+		mbox_reg->cmd_clr_reg = TOPS_CLUST0_AP_TO_CX_CMD_CLR(3);
+		mbox_reg->msg_reg = TOPS_CLUST0_AP_TO_CX_MSG_N(3, 0);
+	} else {
+		dev_notice(mbox.dev, "invalid mdev->core: %u\n", mdev->core);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void mbox_post_send(u32 msg_reg, struct mailbox_msg *msg,
+			   void *priv,
+			   mbox_ret_func_t ret_handler)
+{
+	if (!ret_handler)
+		goto out;
+
+	msg->msg1 = mbox_read(msg_reg + 0x4);
+	msg->msg2 = mbox_read(msg_reg + 0x8);
+	msg->msg3 = mbox_read(msg_reg + 0xC);
+	msg->msg4 = mbox_read(msg_reg + 0x10);
+
+	ret_handler(priv, msg);
+
+out:
+	mbox_write(msg_reg, 0);
+	mbox_write(msg_reg + 0x4, 0);
+	mbox_write(msg_reg + 0x8, 0);
+	mbox_write(msg_reg + 0xC, 0);
+	mbox_write(msg_reg + 0x10, 0);
+}
+
+static inline bool mbox_send_msg_chk_timeout(ktime_t start)
+{
+	return ktime_to_us(ktime_sub(ktime_get(), start)) > MBOX_SEND_TIMEOUT;
+}
+
+static inline int __mbox_send_msg_no_wait_irq(struct mailbox_dev *mdev,
+					      struct mailbox_msg *msg,
+					      struct mailbox_reg *mbox_reg)
+{
+	ktime_t start;
+
+	if (!mdev || !msg || !mbox_reg) {
+		dev_notice(mbox.dev, "missing some necessary parameters!\n");
+		return -EPERM;
+	}
+
+	start = ktime_get();
+
+	/* wait for all cmd cleared */
+	while (mbox_read(mbox_reg->cmd_set_reg)) {
+		if (mbox_send_msg_chk_timeout(start)) {
+			dev_notice(mbox.dev, "mbox occupied too long\n");
+			dev_notice(mbox.dev, "cmd set reg (0x%x): 0x%x\n",
+				mbox_reg->cmd_set_reg,
+				mbox_read(mbox_reg->cmd_set_reg));
+			dev_notice(mbox.dev, "msg1 reg (0x%x): 0x%x\n",
+				mbox_reg->msg_reg,
+				mbox_read(mbox_reg->msg_reg));
+			dev_notice(mbox.dev, "msg2 reg (0x%x): 0x%x\n",
+				mbox_reg->msg_reg,
+				mbox_read(mbox_reg->msg_reg + 0x4));
+			dev_notice(mbox.dev, "msg3 reg (0x%x): 0x%x\n",
+				mbox_reg->msg_reg,
+				mbox_read(mbox_reg->msg_reg + 0x8));
+			dev_notice(mbox.dev, "msg4 reg (0x%x): 0x%x\n",
+				mbox_reg->msg_reg,
+				mbox_read(mbox_reg->msg_reg + 0xC));
+			dev_notice(mbox.dev, "msg5 reg (0x%x): 0x%x\n",
+				mbox_reg->msg_reg,
+				mbox_read(mbox_reg->msg_reg + 0x10));
+			WARN_ON(1);
+		}
+	}
+
+	/* write msg */
+	mbox_write(mbox_reg->msg_reg, mdev->cmd_id);
+	mbox_write(mbox_reg->msg_reg + 0x4, msg->msg1);
+	mbox_write(mbox_reg->msg_reg + 0x8, msg->msg2);
+	mbox_write(mbox_reg->msg_reg + 0xC, msg->msg3);
+	mbox_write(mbox_reg->msg_reg + 0x10, msg->msg4);
+
+	/* write cmd */
+	mbox_write(mbox_reg->cmd_set_reg, BIT(mdev->cmd_id));
+
+	return 0;
+}
+
+int mbox_send_msg_no_wait_irq(struct mailbox_dev *mdev, struct mailbox_msg *msg)
+{
+	struct mailbox_reg mbox_reg = {0};
+	int ret = 0;
+
+	ret = mbox_get_send_reg(mdev, &mbox_reg);
+	if (ret)
+		return ret;
+
+	spin_lock(&mbox.core[MBOX_SEND][mdev->core].lock);
+
+	/* send cmd + msg */
+	ret = __mbox_send_msg_no_wait_irq(mdev, msg, &mbox_reg);
+
+	spin_unlock(&mbox.core[MBOX_SEND][mdev->core].lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(mbox_send_msg_no_wait_irq);
+
+int mbox_send_msg_no_wait(struct mailbox_dev *mdev, struct mailbox_msg *msg)
+{
+	struct mailbox_reg mbox_reg = {0};
+	unsigned long flag = 0;
+	int ret = 0;
+
+	ret = mbox_get_send_reg(mdev, &mbox_reg);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&mbox.core[MBOX_SEND][mdev->core].lock, flag);
+
+	/* send cmd + msg */
+	ret = __mbox_send_msg_no_wait_irq(mdev, msg, &mbox_reg);
+
+	spin_unlock_irqrestore(&mbox.core[MBOX_SEND][mdev->core].lock, flag);
+
+	return ret;
+}
+EXPORT_SYMBOL(mbox_send_msg_no_wait);
+
+int mbox_send_msg(struct mailbox_dev *mdev, struct mailbox_msg *msg, void *priv,
+		  mbox_ret_func_t ret_handler)
+{
+	struct mailbox_reg mbox_reg = {0};
+	unsigned long flag = 0;
+	ktime_t start;
+	int ret = 0;
+
+	ret = mbox_get_send_reg(mdev, &mbox_reg);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&mbox.core[MBOX_SEND][mdev->core].lock, flag);
+
+	/* send cmd + msg */
+	ret = __mbox_send_msg_no_wait_irq(mdev, msg, &mbox_reg);
+
+	start = ktime_get();
+
+	/* wait for cmd clear */
+	while (mbox_read(mbox_reg.cmd_set_reg) & BIT(mdev->cmd_id))
+		mbox_send_msg_chk_timeout(start);
+
+	/* execute return handler and clear message */
+	mbox_post_send(mbox_reg.msg_reg, msg, priv, ret_handler);
+
+	spin_unlock_irqrestore(&mbox.core[MBOX_SEND][mdev->core].lock, flag);
+
+	return ret;
+}
+EXPORT_SYMBOL(mbox_send_msg);
+
+static inline int mbox_ctrl_sanity_check(enum core_id core, enum mbox_act act)
+{
+	/* sanity check */
+	if (core >= CORE_MAX || act >= MBOX_ACT_MAX)
+		return -EINVAL;
+
+	/* mbox handler should not be register to core itself */
+	if (core == CORE_AP)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void __register_mbox_dev(struct mailbox_core *mcore,
+				struct mailbox_dev *mdev)
+{
+	struct mailbox_dev *cur = NULL;
+
+	INIT_LIST_HEAD(&mdev->list);
+
+	/* insert the mailbox_dev in order */
+	list_for_each_entry(cur, &mcore->mdev_list, list)
+		if (cur->cmd_id > mdev->cmd_id)
+			break;
+
+	list_add(&mdev->list, &cur->list);
+
+	mcore->registered_cmd |= (0x1 << mdev->cmd_id);
+}
+
+static void __unregister_mbox_dev(struct mailbox_core *mcore,
+				  struct mailbox_dev *mdev)
+{
+	struct mailbox_dev *cur = NULL;
+	struct mailbox_dev *next = NULL;
+
+	/* ensure the node being deleted is existed in the list */
+	list_for_each_entry_safe(cur, next, &mcore->mdev_list, list) {
+		if (cur->cmd_id == mdev->cmd_id && cur == mdev) {
+			list_del(&mdev->list);
+			break;
+		}
+	}
+
+	mcore->registered_cmd &= (~(0x1 << mdev->cmd_id));
+}
+
+int register_mbox_dev(enum mbox_act act, struct mailbox_dev *mdev)
+{
+	struct mailbox_core *mcore;
+	int ret = 0;
+
+	/* sanity check */
+	ret = mbox_ctrl_sanity_check(mdev->core, act);
+	if (ret)
+		return ret;
+
+	mcore = &mbox.core[act][mdev->core];
+
+	/* check cmd is occupied or not */
+	if (mcore->registered_cmd & (0x1 << mdev->cmd_id))
+		return -EBUSY;
+
+	__register_mbox_dev(mcore, mdev);
+
+	return 0;
+}
+EXPORT_SYMBOL(register_mbox_dev);
+
+int unregister_mbox_dev(enum mbox_act act, struct mailbox_dev *mdev)
+{
+	struct mailbox_core *mcore;
+	int ret = 0;
+
+	/* sanity check */
+	ret = mbox_ctrl_sanity_check(mdev->core, act);
+	if (ret)
+		return ret;
+
+	mcore = &mbox.core[act][mdev->core];
+
+	/* check cmd need to unregister or not */
+	if (!(mcore->registered_cmd & (0x1 << mdev->cmd_id)))
+		return 0;
+
+	__unregister_mbox_dev(mcore, mdev);
+
+	return 0;
+}
+EXPORT_SYMBOL(unregister_mbox_dev);
+
+void mtk_tops_mbox_clear_all_cmd(void)
+{
+	u32 i, j;
+
+	mbox_write(TOPS_TOP_AP_TO_CM_CMD_CLR, 0xFFFFFFFF);
+	mbox_write(TOPS_TOP_CM_TO_AP_CMD_CLR, 0xFFFFFFFF);
+
+	for (i = 0; i < CORE_OFFLOAD_NUM; i++) {
+		mbox_write(TOPS_CLUST0_CX_TO_CM_CMD_CLR(i), 0xFFFFFFFF);
+		mbox_write(TOPS_CLUST0_CM_TO_CX_CMD_CLR(i), 0xFFFFFFFF);
+		mbox_write(TOPS_CLUST0_CX_TO_AP_CMD_CLR(i), 0xFFFFFFFF);
+		mbox_write(TOPS_CLUST0_AP_TO_CX_CMD_CLR(i), 0xFFFFFFFF);
+
+		for (j = 0; j < CORE_OFFLOAD_NUM; j++) {
+			if (i == j)
+				continue;
+
+			mbox_write(TOPS_CLUST0_CX_TO_CY_CMD_CLR(i, j), 0xFFFFFFFF);
+		}
+	}
+}
+
+static int mtk_tops_mbox_probe(struct platform_device *pdev)
+{
+	struct device_node *tops = NULL;
+	struct resource res;
+	int irq = platform_get_irq_byname(pdev, "mbox");
+	int ret = 0;
+	u32 idx = 0;
+
+	mbox.dev = &pdev->dev;
+
+	tops = of_parse_phandle(pdev->dev.of_node, "tops", 0);
+	if (!tops) {
+		dev_err(mbox.dev, "can not find tops node\n");
+		return -ENODEV;
+	}
+
+	if (of_address_to_resource(tops, 0, &res))
+		return -ENXIO;
+
+	mbox.base = devm_ioremap(mbox.dev, res.start, resource_size(&res));
+	if (!mbox.base)
+		return -ENOMEM;
+
+	if (irq < 0) {
+		dev_err(mbox.dev, "get mbox irq failed\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq,
+			       mtk_tops_mbox_handler,
+			       IRQF_ONESHOT,
+			       pdev->name, NULL);
+	if (ret) {
+		dev_err(mbox.dev, "request mbox irq failed\n");
+		return ret;
+	}
+
+	for (idx = 0; idx < CORE_MAX; idx++) {
+		INIT_LIST_HEAD(&mbox.core[MBOX_SEND][idx].mdev_list);
+		INIT_LIST_HEAD(&mbox.core[MBOX_RECV][idx].mdev_list);
+		spin_lock_init(&mbox.core[MBOX_SEND][idx].lock);
+		spin_lock_init(&mbox.core[MBOX_RECV][idx].lock);
+	}
+
+	mtk_tops_mbox_clear_all_cmd();
+
+	return ret;
+}
+
+static int mtk_tops_mbox_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct of_device_id mtk_mbox_match[] = {
+	{ .compatible = "mediatek,tops-mbox", },
+	{ },
+};
+
+static struct platform_driver mtk_tops_mbox_driver = {
+	.probe = mtk_tops_mbox_probe,
+	.remove = mtk_tops_mbox_remove,
+	.driver = {
+		.name = "mediatek,tops-mbox",
+		.owner = THIS_MODULE,
+		.of_match_table = mtk_mbox_match,
+	},
+};
+
+int __init mtk_tops_mbox_init(void)
+{
+	return platform_driver_register(&mtk_tops_mbox_driver);
+}
+
+void __exit mtk_tops_mbox_exit(void)
+{
+	platform_driver_unregister(&mtk_tops_mbox_driver);
+}