feat(mt8196): add MMinfra support

Add MMinfra support for MT8196.

Change-Id: I5504764d05fecace4f0d3981785ff1bc8ae13d00
Signed-off-by: Yidi Lin <yidilin@chromium.org>
diff --git a/plat/mediatek/drivers/mminfra/mminfra_common.h b/plat/mediatek/drivers/mminfra/mminfra_common.h
new file mode 100644
index 0000000..93820c7
--- /dev/null
+++ b/plat/mediatek/drivers/mminfra/mminfra_common.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MMINFRA_COMMON_H
+#define MMINFRA_COMMON_H
+
+#define mminfra_info(fmt, args...)	INFO("[mminfra] %s: "fmt"\n", __func__, ##args)
+#define mminfra_err(fmt, args...)	ERROR("[mminfra] %s: "fmt"\n", __func__, ##args)
+
+#endif
diff --git a/plat/mediatek/drivers/mminfra/mminfra_stub.c b/plat/mediatek/drivers/mminfra/mminfra_stub.c
deleted file mode 100644
index dc37280..0000000
--- a/plat/mediatek/drivers/mminfra/mminfra_stub.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) 2025, MediaTek Inc. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <drivers/mminfra_public.h>
-
-int mminfra_get_if_in_use(void)
-{
-	return 0;
-}
-
-int mminfra_put(void)
-{
-	return 0;
-}
diff --git a/plat/mediatek/drivers/mminfra/mt8196/mminfra.c b/plat/mediatek/drivers/mminfra/mt8196/mminfra.c
new file mode 100644
index 0000000..1086c75
--- /dev/null
+++ b/plat/mediatek/drivers/mminfra/mt8196/mminfra.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <drivers/mminfra_public.h>
+#include <mminfra.h>
+#include <mtk_mmap_pool.h>
+
+static const mmap_region_t mminfra_plat_mmap[] MTK_MMAP_SECTION = {
+	MAP_REGION_FLAT(MMINFRA_HW_VOTER_BASE, PAGE_SIZE, MT_DEVICE | MT_RW | MT_SECURE),
+	{0}
+};
+DECLARE_MTK_MMAP_REGIONS(mminfra_plat_mmap);
+
+static struct mtk_mminfra_pwr_ctrl mminfra_pwr_ctrl = {
+	.hw_voter = {
+		.base = MMINFRA_HW_VOTER_BASE,
+		.set_ofs = 0x104,
+		.clr_ofs = 0x108,
+		.en_ofs = 0x100,
+		.en_shift = 0x1,
+		.done_bits = VLP_AO_RSVD6,
+	},
+	.hw_sema = {
+		.base = SPM_BASE,
+		.offset = SPM_SEMAPHORE_M1,
+		.offset_all = {
+			SPM_SEMAPHORE_M0,
+			SPM_SEMAPHORE_M1,
+			SPM_SEMAPHORE_M2,
+			SPM_SEMAPHORE_M3,
+			SPM_SEMAPHORE_M4,
+			SPM_SEMAPHORE_M5,
+			SPM_SEMAPHORE_M6,
+			SPM_SEMAPHORE_M7,
+		},
+		.set_val = SPM_SEMA_MMINFRA,
+	},
+	.active = true,
+	.ref_cnt = 0,
+};
+
+static int spm_semaphore_get(uint32_t base, uint32_t set_val)
+{
+	int  cnt = SEMA_RETRY_CNT;
+	uint32_t val;
+
+	val = mmio_read_32(base);
+	if ((val & set_val) == set_val) {
+		mminfra_err("hw_sem was already got, base:0x%x=0x%x, set_val:0x%x\n",
+			    base, val, set_val);
+		return -1;
+	}
+
+	while (cnt > 0) {
+		mmio_write_32(base, set_val);
+		udelay(10);
+		if ((mmio_read_32(base) & set_val) == set_val)
+			return 0;
+		cnt--;
+	}
+
+	mminfra_err("timeout! base:0x%x, set_val:0x%x\n", base, set_val);
+	return -1;
+}
+
+static int spm_semaphore_release(uint32_t base, uint32_t set_val)
+{
+	int cnt = SEMA_RETRY_CNT;
+	uint32_t val;
+
+	val = mmio_read_32(base);
+	if ((val & set_val) != set_val) {
+		mminfra_err("hw_sem was already released, base:0x%x=0x%x, set_val:0x%x\n",
+			    base, val, set_val);
+		return -1;
+	}
+	do {
+		mmio_write_32(base, set_val);
+		udelay(10);
+		if (cnt-- < 0) {
+			if ((mmio_read_32(base) & set_val) != set_val)
+				return 0;
+			mminfra_err("timeout! base:0x%x, set_val:0x%x\n", base, set_val);
+			return -1;
+		}
+	} while ((mmio_read_32(base) & set_val) == set_val);
+
+	return 0;
+}
+
+static int mminfra_hw_sema_ctrl(struct mminfra_hw_sema *hw_sema, bool is_get)
+{
+	int i, ret;
+
+	if (!hw_sema)
+		return 0;
+
+	if (is_get)
+		ret = spm_semaphore_get(hw_sema->base + hw_sema->offset, hw_sema->set_val);
+	else
+		ret = spm_semaphore_release(hw_sema->base + hw_sema->offset, hw_sema->set_val);
+
+	if (ret)
+		for (i = 0; i < SPM_SEMA_MMINFRA_NR; i++)
+			mminfra_err("0x%x=0x%x\n", hw_sema->base + hw_sema->offset_all[i],
+				    mmio_read_32(hw_sema->base + hw_sema->offset_all[i]));
+
+	return ret;
+}
+
+static bool is_mminfra_ready(struct mminfra_hw_voter *hw_voter)
+{
+	if (!hw_voter)
+		return false;
+
+	return !!(mmio_read_32(hw_voter->done_bits) & MMINFRA_DONE);
+}
+
+static int mminfra_hwv_power_ctrl(struct mminfra_hw_voter *hw_voter, bool is_on)
+{
+	uint32_t vote_ofs, vote_mask, vote_ack;
+	uint32_t val = 0, cnt;
+
+	vote_mask = BIT(hw_voter->en_shift);
+	vote_ofs = is_on ? hw_voter->set_ofs : hw_voter->clr_ofs;
+	vote_ack = is_on ? vote_mask : 0x0;
+
+	/* Vote on off */
+	cnt = 0;
+	do {
+		mmio_write_32(hw_voter->base + vote_ofs, vote_mask);
+		udelay(MTK_POLL_HWV_VOTE_US);
+		val = mmio_read_32(hw_voter->base + hw_voter->en_ofs);
+		if ((val & vote_mask) == vote_ack)
+			break;
+
+		if (cnt > MTK_POLL_HWV_VOTE_CNT) {
+			mminfra_err("vote mminfra timeout, is_on:%d, 0x%x=0x%x\n",
+				    is_on, hw_voter->base + hw_voter->en_ofs, val);
+			return -1;
+		}
+		cnt++;
+	} while (1);
+
+	if (!is_on)
+		return 0;
+
+	/* Confirm done bits */
+	cnt = 0;
+	while (cnt < MTK_POLL_DONE_RETRY) {
+		if (is_mminfra_ready(hw_voter))
+			return 0;
+		udelay(MTK_POLL_DONE_DELAY_US);
+		cnt++;
+	}
+
+	mminfra_err("polling mminfra done timeout, 0x%x=0x%x\n",
+		    hw_voter->done_bits, val);
+	return -1;
+}
+
+int mminfra_get_if_in_use(void)
+{
+	int ret, is_on = MMINFRA_RET_POWER_OFF;
+
+	if (!mminfra_pwr_ctrl.active) {
+		mminfra_err("not ready\n");
+		return MMINFRA_RET_POWER_OFF;
+	}
+
+	spin_lock(&mminfra_pwr_ctrl.lock);
+	if (mminfra_pwr_ctrl.ref_cnt > 0) {
+		mminfra_pwr_ctrl.ref_cnt++;
+		is_on = MMINFRA_RET_POWER_ON;
+		spin_unlock(&mminfra_pwr_ctrl.lock);
+		return is_on;
+	}
+
+	ret = mminfra_hw_sema_ctrl(&mminfra_pwr_ctrl.hw_sema, true);
+	if (ret)
+		goto err;
+
+	/* Check if mminfra is in use */
+	if (is_mminfra_ready(&mminfra_pwr_ctrl.hw_voter)) {
+		ret = mminfra_hwv_power_ctrl(&mminfra_pwr_ctrl.hw_voter, true);
+		if (ret) {
+			mminfra_err("vote for mminfra fail, ret=%d\n", ret);
+			goto err;
+		}
+		mminfra_pwr_ctrl.ref_cnt++;
+		is_on = MMINFRA_RET_POWER_ON;
+	} else {
+		is_on = MMINFRA_RET_POWER_OFF;
+	}
+
+	ret = mminfra_hw_sema_ctrl(&mminfra_pwr_ctrl.hw_sema, false);
+	if (ret)
+		goto err;
+	ret = is_on; /* Return power is on or off. */
+err:
+	spin_unlock(&mminfra_pwr_ctrl.lock);
+	return ret;
+}
+
+int mminfra_put(void)
+{
+	if (!mminfra_pwr_ctrl.active) {
+		mminfra_err("not ready\n");
+		return 0;
+	}
+
+	spin_lock(&mminfra_pwr_ctrl.lock);
+	mminfra_pwr_ctrl.ref_cnt--;
+	if (mminfra_pwr_ctrl.ref_cnt > 0)
+		goto out;
+
+	mminfra_hwv_power_ctrl(&mminfra_pwr_ctrl.hw_voter, false);
+out:
+	spin_unlock(&mminfra_pwr_ctrl.lock);
+	return 0;
+}
diff --git a/plat/mediatek/drivers/mminfra/mt8196/mminfra.h b/plat/mediatek/drivers/mminfra/mt8196/mminfra.h
new file mode 100644
index 0000000..fcee08c
--- /dev/null
+++ b/plat/mediatek/drivers/mminfra/mt8196/mminfra.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MMINFRA_H
+#define MMINFRA_H
+
+#include <lib/spinlock.h>
+
+#include "../mminfra_common.h"
+#include <platform_def.h>
+
+#define VLP_AO_RSVD6			(MTK_VLP_TRACER_MON_BASE + 0x918)
+#define MMINFRA_DONE			(1U << 0)
+
+#define SPM_SEMA_MMINFRA                (1U << 5)
+#define SPM_SEMA_MMINFRA_NR             (8)
+
+#define SEMA_RETRY_CNT			(500)
+
+#define SPM_SEMAPHORE_M0		(0x69C)
+#define SPM_SEMAPHORE_M1		(0x6A0)
+#define SPM_SEMAPHORE_M2		(0x6A4)
+#define SPM_SEMAPHORE_M3		(0x6A8)
+#define SPM_SEMAPHORE_M4		(0x6AC)
+#define SPM_SEMAPHORE_M5		(0x6B0)
+#define SPM_SEMAPHORE_M6		(0x6B4)
+#define SPM_SEMAPHORE_M7		(0x6B8)
+
+#define MMINFRA_HW_VOTER_BASE		(0x31A80000)
+#define MTK_POLL_HWV_VOTE_US		(2)
+#define MTK_POLL_HWV_VOTE_CNT		(2500)
+#define MTK_POLL_DONE_DELAY_US		(1)
+#define MTK_POLL_DONE_RETRY		(3000)
+
+struct mminfra_hw_sema {
+	uint32_t base;
+	uint32_t offset;
+	uint32_t offset_all[SPM_SEMA_MMINFRA_NR];
+	uint32_t set_val;
+};
+
+struct mminfra_hw_voter {
+	uint32_t base;
+	uint32_t set_ofs;
+	uint32_t clr_ofs;
+	uint32_t en_ofs;
+	uint32_t en_shift;
+	uint32_t done_bits;
+};
+
+struct mtk_mminfra_pwr_ctrl {
+	spinlock_t lock;
+	struct mminfra_hw_voter hw_voter;
+	struct mminfra_hw_sema hw_sema;
+	uint32_t ref_cnt;
+	bool active;
+};
+
+#endif
diff --git a/plat/mediatek/drivers/mminfra/rules.mk b/plat/mediatek/drivers/mminfra/rules.mk
index f3a6822..cd5d607 100644
--- a/plat/mediatek/drivers/mminfra/rules.mk
+++ b/plat/mediatek/drivers/mminfra/rules.mk
@@ -8,10 +8,9 @@
 
 MODULE := mminfra
 
-PLAT_INCLUDES += -I${MTK_PLAT}/include/drivers/
+PLAT_INCLUDES += -I$(MTK_PLAT)/include/drivers/
+PLAT_INCLUDES += -I$(MTK_PLAT)/drivers/mminfra/$(MTK_SOC)
 
-ifeq ($(MTKLIB_PATH),)
-LOCAL_SRCS-y := ${LOCAL_DIR}/mminfra_stub.c
-endif
+LOCAL_SRCS-y := $(LOCAL_DIR)/$(MTK_SOC)/mminfra.c
 
 $(eval $(call MAKE_MODULE,$(MODULE),$(LOCAL_SRCS-y),$(MTK_BL)))
diff --git a/plat/mediatek/mt8196/include/platform_def.h b/plat/mediatek/mt8196/include/platform_def.h
index 363c8cc..ad0bf68 100644
--- a/plat/mediatek/mt8196/include/platform_def.h
+++ b/plat/mediatek/mt8196/include/platform_def.h
@@ -341,4 +341,10 @@
 #define SSPM_CFGREG_BASE	(IO_PHYS + 0x0C300000 + SSPM_REG_OFFSET)
 #define SSPM_CFGREG_SIZE	(0x1000)
 
+/*******************************************************************************
+ * MMinfra related constants
+ ******************************************************************************/
+#define MTK_VLP_TRACER_MON_BASE		(IO_PHYS + 0x0c000000)
+#define MTK_VLP_TRACER_MON_REG_SIZE	(0x1000)
+
 #endif /* PLATFORM_DEF_H */