feat(plat/mediatek/mt8186): add CPU hotplug

Implement PSCI platform operations to support CPU hotplug and MCDI.

TEST=bringup 8 CPUs successfully on kernel stage.
BUG=b:202871018

Change-Id: Ibd5423b70b3ca3f91edaa48d7ca5bc094e751510
Signed-off-by: Garmin.Chang <Garmin.Chang@mediatek.com>
diff --git a/plat/mediatek/mt8186/drivers/spmc/mtspmc.c b/plat/mediatek/mt8186/drivers/spmc/mtspmc.c
new file mode 100644
index 0000000..91ef096
--- /dev/null
+++ b/plat/mediatek/mt8186/drivers/spmc/mtspmc.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <mcucfg.h>
+#include <mtspmc.h>
+#include <mtspmc_private.h>
+#include <plat/common/platform.h>
+
+void mcucfg_disable_gic_wakeup(unsigned int cluster, unsigned int cpu)
+{
+	mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(cpu));
+}
+
+void mcucfg_enable_gic_wakeup(unsigned int cluster, unsigned int cpu)
+{
+	mmio_clrbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(cpu));
+	/* Clear cpu's cpc sw hint */
+	mmio_write_32(CPC_MCUSYS_CPU_ON_SW_HINT_CLR, BIT(cpu));
+}
+
+void mcucfg_set_bootaddr(unsigned int cluster, unsigned int cpu, uintptr_t bootaddr)
+{
+	assert(cluster == 0U);
+
+	mmio_write_32(per_cpu(cluster, cpu, MCUCFG_BOOTADDR), bootaddr);
+}
+
+uintptr_t mcucfg_get_bootaddr(unsigned int cluster, unsigned int cpu)
+{
+	assert(cluster == 0U);
+
+	return (uintptr_t)mmio_read_32(per_cpu(cluster, cpu, MCUCFG_BOOTADDR));
+}
+
+void mcucfg_init_archstate(unsigned int cluster, unsigned int cpu, bool arm64)
+{
+	uint32_t reg;
+
+	assert(cluster == 0U);
+
+	reg = per_cluster(cluster, MCUCFG_INITARCH);
+
+	if (arm64) {
+		mmio_setbits_32(reg, MCUCFG_INITARCH_CPU_BIT(cpu));
+	} else {
+		mmio_clrbits_32(reg, MCUCFG_INITARCH_CPU_BIT(cpu));
+	}
+}
+
+/*
+ * Return subsystem's power state.
+ *
+ * @mask: mask to SPM_CPU_PWR_STATUS to query the power state
+ *        of one subsystem.
+ * RETURNS:
+ * 0 (the subsys was powered off)
+ * 1 (the subsys was powered on)
+ */
+bool spm_get_powerstate(uint32_t mask)
+{
+	return (mmio_read_32(SPM_CPU_PWR_STATUS) & mask);
+}
+
+bool spm_get_cluster_powerstate(unsigned int cluster)
+{
+	assert(cluster == 0U);
+
+	return spm_get_powerstate(MP0_CPUTOP);
+}
+
+bool spm_get_cpu_powerstate(unsigned int cluster, unsigned int cpu)
+{
+	uint32_t mask = BIT(cpu);
+
+	assert(cluster == 0U);
+
+	return spm_get_powerstate(mask);
+}
+
+int spmc_init(void)
+{
+	unsigned int cpu = plat_my_core_pos();
+
+
+	INFO("SPM: enable CPC mode\n");
+
+	mmio_write_32(SPM_POWERON_CONFIG_EN, PROJECT_CODE | BCLK_CG_EN);
+
+	mmio_setbits_32(per_cpu(0, 1, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 2, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 3, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 4, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 5, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 6, SPM_CPU_PWR), PWR_RST_B);
+	mmio_setbits_32(per_cpu(0, 7, SPM_CPU_PWR), PWR_RST_B);
+
+	mmio_clrbits_32(SPM_MCUSYS_PWR_CON, RESETPWRON_CONFIG);
+	mmio_clrbits_32(SPM_MP0_CPUTOP_PWR_CON, RESETPWRON_CONFIG);
+	mmio_clrbits_32(per_cpu(0, 0, SPM_CPU_PWR), RESETPWRON_CONFIG);
+
+	mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, CPC_CTRL_ENABLE);
+
+	/* Clear bootup cpu's cpc sw hint */
+	mmio_write_32(CPC_MCUSYS_CPU_ON_SW_HINT_CLR, BIT(cpu));
+
+	return 0;
+}
+
+/*
+ * Power on a core with specified cluster and core index
+ *
+ * @cluster: the cluster ID of the CPU which to be powered on
+ * @cpu: the CPU ID of the CPU which to be powered on
+ */
+void spm_poweron_cpu(unsigned int cluster, unsigned int cpu)
+{
+	/* info CPC that CPU hotplug on */
+	mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, SSPM_ALL_PWR_CTRL_EN);
+
+	/* Set mp0_spmc_pwr_on_cpuX = 1 */
+	mmio_setbits_32(per_cpu(cluster, cpu, SPM_CPU_PWR), PWR_ON);
+
+	/* wait for power on ack */
+	while (!spm_get_cpu_powerstate(cluster, cpu))
+		;
+
+	/* info CPC that CPU hotplug off */
+	mmio_clrbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, SSPM_ALL_PWR_CTRL_EN);
+}
+
+/*
+ * Power off a core with specified cluster and core index
+ *
+ * @cluster: the cluster ID of the CPU which to be powered off
+ * @cpu: the CPU ID of the CPU which to be powered off
+ */
+void spm_poweroff_cpu(unsigned int cluster, unsigned int cpu)
+{
+	/* Set mp0_spmc_pwr_on_cpuX = 0 */
+	mmio_clrbits_32(per_cpu(cluster, cpu, SPM_CPU_PWR), PWR_ON);
+}
+
+/*
+ * Power off a cluster with specified index
+ *
+ * @cluster: the cluster index which to be powered off
+ */
+void spm_poweroff_cluster(unsigned int cluster)
+{
+	/* No need to power on/off cluster on single cluster platform */
+	assert(false);
+}
+
+/*
+ * Power on a cluster with specified index
+ *
+ * @cluster: the cluster index which to be powered on
+ */
+void spm_poweron_cluster(unsigned int cluster)
+{
+	/* No need to power on/off cluster on single cluster platform */
+	assert(false);
+}
diff --git a/plat/mediatek/mt8186/drivers/spmc/mtspmc.h b/plat/mediatek/mt8186/drivers/spmc/mtspmc.h
new file mode 100644
index 0000000..768599b
--- /dev/null
+++ b/plat/mediatek/mt8186/drivers/spmc/mtspmc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MTSPMC_H
+#define MTSPMC_H
+
+#include <stdint.h>
+
+int spmc_init(void);
+
+void spm_poweron_cpu(unsigned int cluster, unsigned int cpu);
+void spm_poweroff_cpu(unsigned int cluster, unsigned int cpu);
+
+void spm_poweroff_cluster(unsigned int cluster);
+void spm_poweron_cluster(unsigned int cluster);
+
+bool spm_get_cpu_powerstate(unsigned int cluster, unsigned int cpu);
+bool spm_get_cluster_powerstate(unsigned int cluster);
+bool spm_get_powerstate(uint32_t mask);
+
+void mcucfg_init_archstate(unsigned int cluster, unsigned int cpu, bool arm64);
+void mcucfg_set_bootaddr(unsigned int cluster, unsigned int cpu, uintptr_t bootaddr);
+uintptr_t mcucfg_get_bootaddr(unsigned int cluster, unsigned int cpu);
+
+void mcucfg_disable_gic_wakeup(unsigned int cluster, unsigned int cpu);
+void mcucfg_enable_gic_wakeup(unsigned int cluster, unsigned int cpu);
+
+#endif /* MTSPMC_H */
diff --git a/plat/mediatek/mt8186/drivers/spmc/mtspmc_private.h b/plat/mediatek/mt8186/drivers/spmc/mtspmc_private.h
new file mode 100644
index 0000000..472b54c
--- /dev/null
+++ b/plat/mediatek/mt8186/drivers/spmc/mtspmc_private.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MTSPMC_PRIVATE_H
+#define MTSPMC_PRIVATE_H
+
+#include <lib/utils_def.h>
+#include <platform_def.h>
+
+unsigned long read_cpuectlr(void);
+void write_cpuectlr(unsigned long cpuectlr);
+
+unsigned long read_cpupwrctlr_el1(void);
+void write_cpupwrctlr_el1(unsigned long cpuectlr);
+
+/* per_cpu/cluster helper */
+struct per_cpu_reg {
+	unsigned int cluster_addr;
+	unsigned int cpu_stride;
+};
+
+#define per_cpu(cluster, cpu, reg)	\
+	(reg[cluster].cluster_addr + (cpu << reg[cluster].cpu_stride))
+
+#define per_cluster(cluster, reg)	(reg[cluster].cluster_addr)
+
+#define SPM_REG(ofs)			(uint32_t)(SPM_BASE + (ofs))
+#define MCUCFG_REG(ofs)			(uint32_t)(MCUCFG_BASE + (ofs))
+#define INFRACFG_AO_REG(ofs)		(uint32_t)(INFRACFG_AO_BASE + (ofs))
+
+/* SPMC related registers */
+#define SPM_POWERON_CONFIG_EN		SPM_REG(0x000)
+/* bit-fields of SPM_POWERON_CONFIG_EN */
+#define PROJECT_CODE			(U(0xb16) << 16)
+#define BCLK_CG_EN			BIT(0)
+
+#define SPM_PWR_STATUS			SPM_REG(0x16c)
+#define SPM_PWR_STATUS_2ND		SPM_REG(0x170)
+#define SPM_CPU_PWR_STATUS		SPM_REG(0x174)
+
+/* bit-fields of SPM_PWR_STATUS */
+#define MD				BIT(0)
+#define CONN				BIT(1)
+#define DDRPHY				BIT(2)
+#define DISP				BIT(3)
+#define MFG				BIT(4)
+#define ISP				BIT(5)
+#define INFRA				BIT(6)
+#define VDEC				BIT(7)
+#define MP0_CPUTOP			BIT(8)
+#define MP0_CPU0			BIT(9)
+#define MP0_CPU1			BIT(10)
+#define MP0_CPU2			BIT(11)
+#define MP0_CPU3			BIT(12)
+#define MCUSYS				BIT(14)
+#define MP0_CPU4			BIT(15)
+#define MP0_CPU5			BIT(16)
+#define MP0_CPU6			BIT(17)
+#define MP0_CPU7			BIT(18)
+#define VEN				BIT(21)
+
+/* SPMC related registers */
+#define SPM_MCUSYS_PWR_CON		SPM_REG(0x200)
+#define SPM_MP0_CPUTOP_PWR_CON		SPM_REG(0x204)
+#define SPM_MP0_CPU0_PWR_CON		SPM_REG(0x208)
+#define SPM_MP0_CPU1_PWR_CON		SPM_REG(0x20c)
+#define SPM_MP0_CPU2_PWR_CON		SPM_REG(0x210)
+#define SPM_MP0_CPU3_PWR_CON		SPM_REG(0x214)
+#define SPM_MP0_CPU4_PWR_CON		SPM_REG(0x218)
+#define SPM_MP0_CPU5_PWR_CON		SPM_REG(0x21c)
+#define SPM_MP0_CPU6_PWR_CON		SPM_REG(0x220)
+#define SPM_MP0_CPU7_PWR_CON		SPM_REG(0x224)
+
+/* bit-fields of SPM_*_PWR_CON */
+#define PWR_ON_ACK			BIT(31)
+#define VPROC_EXT_OFF			BIT(7)
+#define DORMANT_EN			BIT(6)
+#define RESETPWRON_CONFIG		BIT(5)
+#define PWR_CLK_DIS			BIT(4)
+#define PWR_ON				BIT(2)
+#define PWR_RST_B			BIT(0)
+
+/* per_cpu registers for SPM_MP0_CPU_PWR_CON */
+static const struct per_cpu_reg SPM_CPU_PWR[] = {
+	{ .cluster_addr = SPM_MP0_CPU0_PWR_CON, .cpu_stride = 2U }
+};
+
+/* per_cluster registers for SPM_MP0_CPUTOP_PWR_CON */
+static const struct per_cpu_reg SPM_CLUSTER_PWR[] = {
+	{ .cluster_addr = SPM_MP0_CPUTOP_PWR_CON, .cpu_stride = 0U }
+};
+
+/* MCUCFG related registers */
+#define MCUCFG_MP0_CLUSTER_CFG5		MCUCFG_REG(0xc8e4)
+/* reset vectors */
+#define MCUCFG_MP0_CLUSTER_CFG8		MCUCFG_REG(0xc900)
+#define MCUCFG_MP0_CLUSTER_CFG10	MCUCFG_REG(0xc908)
+#define MCUCFG_MP0_CLUSTER_CFG12	MCUCFG_REG(0xc910)
+#define MCUCFG_MP0_CLUSTER_CFG14	MCUCFG_REG(0xc918)
+#define MCUCFG_MP0_CLUSTER_CFG16	MCUCFG_REG(0xc920)
+#define MCUCFG_MP0_CLUSTER_CFG18	MCUCFG_REG(0xc928)
+#define MCUCFG_MP0_CLUSTER_CFG20	MCUCFG_REG(0xc930)
+#define MCUCFG_MP0_CLUSTER_CFG22	MCUCFG_REG(0xc938)
+
+/* per_cpu registers for MCUCFG_MP0_CLUSTER_CFG */
+static const struct per_cpu_reg MCUCFG_BOOTADDR[] = {
+	{ .cluster_addr = MCUCFG_MP0_CLUSTER_CFG8, .cpu_stride = 3U }
+};
+
+/* per_cpu registers for MCUCFG_MP0_CLUSTER_CFG5 */
+static const struct per_cpu_reg MCUCFG_INITARCH[] = {
+	{ .cluster_addr = MCUCFG_MP0_CLUSTER_CFG5, .cpu_stride = 0U }
+};
+
+#define MCUCFG_INITARCH_CPU_BIT(cpu)	BIT(16U + cpu)
+/* CPC control */
+#define MCUCFG_CPC_FLOW_CTRL_CFG	MCUCFG_REG(0xa814)
+#define MCUCFG_CPC_SPMC_PWR_STATUS	MCUCFG_REG(0xa840)
+
+/* bit-fields of CPC_FLOW_CTRL_CFG */
+#define CPC_CTRL_ENABLE			BIT(16)
+#define SSPM_ALL_PWR_CTRL_EN		BIT(13) /* for cpu-hotplug */
+#define GIC_WAKEUP_IGNORE(cpu)		BIT(21 + cpu)
+
+/* bit-fields of CPC_SPMC_PWR_STATUS */
+#define CORE_SPMC_PWR_ON_ACK		GENMASK(11, 0)
+
+/* APB module infracfg_ao */
+#define INFRA_TOPAXI_PROTECTEN		INFRACFG_AO_REG(0x0220)
+#define INFRA_TOPAXI_PROTECTEN_STA0	INFRACFG_AO_REG(0x0224)
+#define INFRA_TOPAXI_PROTECTEN_STA1	INFRACFG_AO_REG(0x0228)
+#define INFRA_TOPAXI_PROTECTEN_SET	INFRACFG_AO_REG(0x02a0)
+#define INFRA_TOPAXI_PROTECTEN_CLR	INFRACFG_AO_REG(0x02a4)
+#define INFRA_TOPAXI_PROTECTEN_1	INFRACFG_AO_REG(0x0250)
+#define INFRA_TOPAXI_PROTECTEN_STA0_1	INFRACFG_AO_REG(0x0254)
+#define INFRA_TOPAXI_PROTECTEN_STA1_1	INFRACFG_AO_REG(0x0258)
+#define INFRA_TOPAXI_PROTECTEN_1_SET	INFRACFG_AO_REG(0x02a8)
+#define INFRA_TOPAXI_PROTECTEN_1_CLR	INFRACFG_AO_REG(0x02ac)
+
+/* bit-fields of INFRA_TOPAXI_PROTECTEN */
+#define MP0_SPMC_PROT_STEP1_0_MASK	BIT(12)
+#define MP0_SPMC_PROT_STEP1_1_MASK	(BIT(26) | BIT(12))
+
+/* SPARK */
+#define VOLTAGE_04			U(0x40)
+#define VOLTAGE_05			U(0x60)
+
+#define PTP3_CPU0_SPMC_SW_CFG		MCUCFG_REG(0x200)
+#define CPU0_ILDO_CONTROL5		MCUCFG_REG(0x334)
+#define CPU0_ILDO_CONTROL8		MCUCFG_REG(0x340)
+
+/* bit-fields of CPU0_ILDO_CONTROL5 */
+#define ILDO_RET_VOSEL			GENMASK(7, 0)
+
+/* bit-fields of PTP3_CPU_SPMC_SW_CFG */
+#define SW_SPARK_EN			BIT(0)
+
+/* bit-fields of CPU0_ILDO_CONTROL8 */
+#define ILDO_BYPASS_B			BIT(0)
+
+static const struct per_cpu_reg MCUCFG_SPARK[] = {
+	{ .cluster_addr = PTP3_CPU0_SPMC_SW_CFG, .cpu_stride = 11U }
+};
+
+static const struct per_cpu_reg ILDO_CONTROL5[] = {
+	{ .cluster_addr = CPU0_ILDO_CONTROL5, .cpu_stride = 11U }
+};
+
+static const struct per_cpu_reg ILDO_CONTROL8[] = {
+	{ .cluster_addr = CPU0_ILDO_CONTROL8, .cpu_stride = 11U }
+};
+
+#endif /* MTSPMC_PRIVATE_H */
diff --git a/plat/mediatek/mt8186/include/mcucfg.h b/plat/mediatek/mt8186/include/mcucfg.h
new file mode 100644
index 0000000..78a01a8
--- /dev/null
+++ b/plat/mediatek/mt8186/include/mcucfg.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MCUCFG_H
+#define MCUCFG_H
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+#endif /* __ASSEMBLER__ */
+
+#include <platform_def.h>
+
+#define MCUCFG_REG(ofs)			(uint32_t)(MCUCFG_BASE + (ofs))
+
+#define MP2_MISC_CONFIG_BOOT_ADDR_L(cpu) (MCUCFG_REG(0x2290) + ((cpu) * 8))
+#define MP2_MISC_CONFIG_BOOT_ADDR_H(cpu) (MCUCFG_REG(0x2294) + ((cpu) * 8))
+
+#define MP2_CPUCFG			MCUCFG_REG(0x2208)
+
+#define MP2_CPU0_STANDBYWFE		BIT(4)
+#define MP2_CPU1_STANDBYWFE		BIT(5)
+
+#define MP0_CPUTOP_SPMC_CTL		MCUCFG_REG(0x788)
+#define MP1_CPUTOP_SPMC_CTL		MCUCFG_REG(0x78C)
+#define MP1_CPUTOP_SPMC_SRAM_CTL	MCUCFG_REG(0x790)
+
+#define sw_spark_en			BIT(0)
+#define sw_no_wait_for_q_channel	BIT(1)
+#define sw_fsm_override			BIT(2)
+#define sw_logic_pre1_pdb		BIT(3)
+#define sw_logic_pre2_pdb		BIT(4)
+#define sw_logic_pdb			BIT(5)
+#define sw_iso				BIT(6)
+#define sw_sram_sleepb			(U(0x3F) << 7)
+#define sw_sram_isointb			BIT(13)
+#define sw_clk_dis			BIT(14)
+#define sw_ckiso			BIT(15)
+#define sw_pd				(U(0x3F) << 16)
+#define sw_hot_plug_reset		BIT(22)
+#define sw_pwr_on_override_en		BIT(23)
+#define sw_pwr_on			BIT(24)
+#define sw_coq_dis			BIT(25)
+#define logic_pdbo_all_off_ack		BIT(26)
+#define logic_pdbo_all_on_ack		BIT(27)
+#define logic_pre2_pdbo_all_on_ack	BIT(28)
+#define logic_pre1_pdbo_all_on_ack	BIT(29)
+
+
+#define CPUSYSx_CPUx_SPMC_CTL(cluster, cpu) \
+	(MCUCFG_REG(0x1c30) + cluster * 0x2000 + cpu * 4)
+
+#define CPUSYS0_CPU0_SPMC_CTL		MCUCFG_REG(0x1c30)
+#define CPUSYS0_CPU1_SPMC_CTL		MCUCFG_REG(0x1c34)
+#define CPUSYS0_CPU2_SPMC_CTL		MCUCFG_REG(0x1c38)
+#define CPUSYS0_CPU3_SPMC_CTL		MCUCFG_REG(0x1c3C)
+
+#define CPUSYS1_CPU0_SPMC_CTL		MCUCFG_REG(0x3c30)
+#define CPUSYS1_CPU1_SPMC_CTL		MCUCFG_REG(0x3c34)
+#define CPUSYS1_CPU2_SPMC_CTL		MCUCFG_REG(0x3c38)
+#define CPUSYS1_CPU3_SPMC_CTL		MCUCFG_REG(0x3c3C)
+
+#define cpu_sw_spark_en			BIT(0)
+#define cpu_sw_no_wait_for_q_channel	BIT(1)
+#define cpu_sw_fsm_override		BIT(2)
+#define cpu_sw_logic_pre1_pdb		BIT(3)
+#define cpu_sw_logic_pre2_pdb		BIT(4)
+#define cpu_sw_logic_pdb		BIT(5)
+#define cpu_sw_iso			BIT(6)
+#define cpu_sw_sram_sleepb		BIT(7)
+#define cpu_sw_sram_isointb		BIT(8)
+#define cpu_sw_clk_dis			BIT(9)
+#define cpu_sw_ckiso			BIT(10)
+#define cpu_sw_pd			(U(0x1F) << 11)
+#define cpu_sw_hot_plug_reset		BIT(16)
+#define cpu_sw_powr_on_override_en	BIT(17)
+#define cpu_sw_pwr_on			BIT(18)
+#define cpu_spark2ldo_allswoff		BIT(19)
+#define cpu_pdbo_all_on_ack		BIT(20)
+#define cpu_pre2_pdbo_allon_ack		BIT(21)
+#define cpu_pre1_pdbo_allon_ack		BIT(22)
+
+/* CPC related registers */
+#define CPC_MCUSYS_CPC_OFF_THRES		MCUCFG_REG(0xa714)
+#define CPC_MCUSYS_PWR_CTRL			MCUCFG_REG(0xa804)
+#define CPC_MCUSYS_CPC_FLOW_CTRL_CFG		MCUCFG_REG(0xa814)
+#define CPC_MCUSYS_LAST_CORE_REQ		MCUCFG_REG(0xa818)
+#define CPC_MCUSYS_MP_LAST_CORE_RESP		MCUCFG_REG(0xa81c)
+#define CPC_MCUSYS_LAST_CORE_RESP		MCUCFG_REG(0xa824)
+#define CPC_MCUSYS_PWR_ON_MASK			MCUCFG_REG(0xa828)
+#define CPC_MCUSYS_CPU_ON_SW_HINT_SET		MCUCFG_REG(0xa8a8)
+#define CPC_MCUSYS_CPU_ON_SW_HINT_CLR		MCUCFG_REG(0xa8ac)
+#define CPC_MCUSYS_CPC_DBG_SETTING		MCUCFG_REG(0xab00)
+#define CPC_MCUSYS_CPC_KERNEL_TIME_L_BASE	MCUCFG_REG(0xab04)
+#define CPC_MCUSYS_CPC_KERNEL_TIME_H_BASE	MCUCFG_REG(0xab08)
+#define CPC_MCUSYS_CPC_SYSTEM_TIME_L_BASE	MCUCFG_REG(0xab0c)
+#define CPC_MCUSYS_CPC_SYSTEM_TIME_H_BASE	MCUCFG_REG(0xab10)
+#define CPC_MCUSYS_TRACE_SEL			MCUCFG_REG(0xab14)
+#define CPC_MCUSYS_TRACE_DATA			MCUCFG_REG(0xab20)
+#define CPC_MCUSYS_CLUSTER_COUNTER		MCUCFG_REG(0xab70)
+#define CPC_MCUSYS_CLUSTER_COUNTER_CLR		MCUCFG_REG(0xab74)
+#define SPARK2LDO				MCUCFG_REG(0x2700)
+/* APB module mcucfg */
+#define MP0_CA7_CACHE_CONFIG		MCUCFG_REG(0x000)
+#define MP0_AXI_CONFIG			MCUCFG_REG(0x02C)
+#define MP0_MISC_CONFIG0		MCUCFG_REG(0x030)
+#define MP0_MISC_CONFIG1		MCUCFG_REG(0x034)
+#define MP0_MISC_CONFIG2		MCUCFG_REG(0x038)
+#define MP0_MISC_CONFIG_BOOT_ADDR(cpu)	(MP0_MISC_CONFIG2 + ((cpu) * 8))
+#define MP0_MISC_CONFIG3		MCUCFG_REG(0x03C)
+#define MP0_MISC_CONFIG9		MCUCFG_REG(0x054)
+#define MP0_CA7_MISC_CONFIG		MCUCFG_REG(0x064)
+
+#define MP0_RW_RSVD0			MCUCFG_REG(0x06C)
+
+
+#define MP1_CA7_CACHE_CONFIG		MCUCFG_REG(0x200)
+#define MP1_AXI_CONFIG			MCUCFG_REG(0x22C)
+#define MP1_MISC_CONFIG0		MCUCFG_REG(0x230)
+#define MP1_MISC_CONFIG1		MCUCFG_REG(0x234)
+#define MP1_MISC_CONFIG2		MCUCFG_REG(0x238)
+#define MP1_MISC_CONFIG_BOOT_ADDR(cpu)	(MP1_MISC_CONFIG2 + ((cpu) * 8))
+#define MP1_MISC_CONFIG3		MCUCFG_REG(0x23C)
+#define MP1_MISC_CONFIG9		MCUCFG_REG(0x254)
+#define MP1_CA7_MISC_CONFIG		MCUCFG_REG(0x264)
+
+#define CCI_ADB400_DCM_CONFIG		MCUCFG_REG(0x740)
+#define SYNC_DCM_CONFIG			MCUCFG_REG(0x744)
+
+#define MP0_CLUSTER_CFG0		MCUCFG_REG(0xC8D0)
+
+#define MP0_SPMC			MCUCFG_REG(0x788)
+#define MP1_SPMC			MCUCFG_REG(0x78C)
+#define MP2_AXI_CONFIG			MCUCFG_REG(0x220C)
+#define MP2_AXI_CONFIG_ACINACTM		BIT(0)
+#define MP2_AXI_CONFIG_AINACTS		BIT(4)
+
+#define MPx_AXI_CONFIG_ACINACTM			BIT(4)
+#define MPx_AXI_CONFIG_AINACTS			BIT(5)
+#define MPx_CA7_MISC_CONFIG_standbywfil2	BIT(28)
+
+#define MP0_CPU0_STANDBYWFE		BIT(20)
+#define MP0_CPU1_STANDBYWFE		BIT(21)
+#define MP0_CPU2_STANDBYWFE		BIT(22)
+#define MP0_CPU3_STANDBYWFE		BIT(23)
+
+#define MP1_CPU0_STANDBYWFE		BIT(20)
+#define MP1_CPU1_STANDBYWFE		BIT(21)
+#define MP1_CPU2_STANDBYWFE		BIT(22)
+#define MP1_CPU3_STANDBYWFE		BIT(23)
+
+#define CPUSYS0_SPARKVRETCNTRL		MCUCFG_REG(0x1c00)
+#define CPUSYS0_SPARKEN			MCUCFG_REG(0x1c04)
+#define CPUSYS0_AMUXSEL			MCUCFG_REG(0x1c08)
+#define CPUSYS1_SPARKVRETCNTRL		MCUCFG_REG(0x3c00)
+#define CPUSYS1_SPARKEN			MCUCFG_REG(0x3c04)
+#define CPUSYS1_AMUXSEL			MCUCFG_REG(0x3c08)
+
+#define MP2_PWR_RST_CTL			MCUCFG_REG(0x2008)
+#define MP2_PTP3_CPUTOP_SPMC0		MCUCFG_REG(0x22A0)
+#define MP2_PTP3_CPUTOP_SPMC1		MCUCFG_REG(0x22A4)
+
+#define MP2_COQ				MCUCFG_REG(0x22BC)
+#define MP2_COQ_SW_DIS			BIT(0)
+
+#define MP2_CA15M_MON_SEL		MCUCFG_REG(0x2400)
+#define MP2_CA15M_MON_L			MCUCFG_REG(0x2404)
+
+#define CPUSYS2_CPU0_SPMC_CTL		MCUCFG_REG(0x2430)
+#define CPUSYS2_CPU1_SPMC_CTL		MCUCFG_REG(0x2438)
+#define CPUSYS2_CPU0_SPMC_STA		MCUCFG_REG(0x2434)
+#define CPUSYS2_CPU1_SPMC_STA		MCUCFG_REG(0x243C)
+
+#define MP0_CA7L_DBG_PWR_CTRL		MCUCFG_REG(0x068)
+#define MP1_CA7L_DBG_PWR_CTRL		MCUCFG_REG(0x268)
+#define BIG_DBG_PWR_CTRL		MCUCFG_REG(0x75C)
+
+#define MP2_SW_RST_B			BIT(0)
+#define MP2_TOPAON_APB_MASK		BIT(1)
+
+#define B_SW_HOT_PLUG_RESET		BIT(30)
+
+#define B_SW_PD_OFFSET			(18U)
+#define B_SW_PD				(U(0x3f) << B_SW_PD_OFFSET)
+
+#define B_SW_SRAM_SLEEPB_OFFSET		(12U)
+#define B_SW_SRAM_SLEEPB		(U(0x3f) << B_SW_SRAM_SLEEPB_OFFSET)
+
+#define B_SW_SRAM_ISOINTB		BIT(9)
+#define B_SW_ISO			BIT(8)
+#define B_SW_LOGIC_PDB			BIT(7)
+#define B_SW_LOGIC_PRE2_PDB		BIT(6)
+#define B_SW_LOGIC_PRE1_PDB		BIT(5)
+#define B_SW_FSM_OVERRIDE		BIT(4)
+#define B_SW_PWR_ON			BIT(3)
+#define B_SW_PWR_ON_OVERRIDE_EN		BIT(2)
+
+#define B_FSM_STATE_OUT_OFFSET		(6U)
+#define B_FSM_STATE_OUT_MASK		(U(0x1f) << B_FSM_STATE_OUT_OFFSET)
+#define B_SW_LOGIC_PDBO_ALL_OFF_ACK	BIT(5)
+#define B_SW_LOGIC_PDBO_ALL_ON_ACK	BIT(4)
+#define B_SW_LOGIC_PRE2_PDBO_ALL_ON_ACK	BIT(3)
+#define B_SW_LOGIC_PRE1_PDBO_ALL_ON_ACK	BIT(2)
+
+#define B_FSM_OFF			(0U << B_FSM_STATE_OUT_OFFSET)
+#define B_FSM_ON			(1U << B_FSM_STATE_OUT_OFFSET)
+#define B_FSM_RET			(2U << B_FSM_STATE_OUT_OFFSET)
+
+#ifndef __ASSEMBLER__
+/* cpu boot mode */
+enum {
+	MP0_CPUCFG_64BIT_SHIFT = 12U,
+	MP1_CPUCFG_64BIT_SHIFT = 28U,
+	MP0_CPUCFG_64BIT = U(0xf) << MP0_CPUCFG_64BIT_SHIFT,
+	MP1_CPUCFG_64BIT = U(0xf) << MP1_CPUCFG_64BIT_SHIFT
+};
+
+enum {
+	MP1_DIS_RGU0_WAIT_PD_CPUS_L1_ACK_SHIFT = 0U,
+	MP1_DIS_RGU1_WAIT_PD_CPUS_L1_ACK_SHIFT = 4U,
+	MP1_DIS_RGU2_WAIT_PD_CPUS_L1_ACK_SHIFT = 8U,
+	MP1_DIS_RGU3_WAIT_PD_CPUS_L1_ACK_SHIFT = 12U,
+	MP1_DIS_RGU_NOCPU_WAIT_PD_CPUS_L1_ACK_SHIFT = 16U,
+
+	MP1_DIS_RGU0_WAIT_PD_CPUS_L1_ACK =
+		U(0xf) << MP1_DIS_RGU0_WAIT_PD_CPUS_L1_ACK_SHIFT,
+	MP1_DIS_RGU1_WAIT_PD_CPUS_L1_ACK =
+		U(0xf) << MP1_DIS_RGU1_WAIT_PD_CPUS_L1_ACK_SHIFT,
+	MP1_DIS_RGU2_WAIT_PD_CPUS_L1_ACK =
+		U(0xf) << MP1_DIS_RGU2_WAIT_PD_CPUS_L1_ACK_SHIFT,
+	MP1_DIS_RGU3_WAIT_PD_CPUS_L1_ACK =
+		U(0xf) << MP1_DIS_RGU3_WAIT_PD_CPUS_L1_ACK_SHIFT,
+	MP1_DIS_RGU_NOCPU_WAIT_PD_CPUS_L1_ACK =
+		U(0xf) << MP1_DIS_RGU_NOCPU_WAIT_PD_CPUS_L1_ACK_SHIFT
+};
+
+enum {
+	MP1_AINACTS_SHIFT = 4U,
+	MP1_AINACTS = 1U << MP1_AINACTS_SHIFT
+};
+
+enum {
+	MP1_SW_CG_GEN_SHIFT = 12U,
+	MP1_SW_CG_GEN = 1U << MP1_SW_CG_GEN_SHIFT
+};
+
+enum {
+	MP1_L2RSTDISABLE_SHIFT = 14U,
+	MP1_L2RSTDISABLE = 1U << MP1_L2RSTDISABLE_SHIFT
+};
+#endif /* __ASSEMBLER__ */
+
+#endif  /* MCUCFG_H */
diff --git a/plat/mediatek/mt8186/include/plat_mtk_lpm.h b/plat/mediatek/mt8186/include/plat_mtk_lpm.h
new file mode 100644
index 0000000..347f358
--- /dev/null
+++ b/plat/mediatek/mt8186/include/plat_mtk_lpm.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_MTK_LPM_H
+#define PLAT_MTK_LPM_H
+
+#include <lib/psci/psci.h>
+#include <lib/utils_def.h>
+
+#define MT_IRQ_REMAIN_MAX	U(32)
+#define MT_IRQ_REMAIN_CAT_LOG	BIT(31)
+
+struct mt_irqremain {
+	unsigned int count;
+	unsigned int irqs[MT_IRQ_REMAIN_MAX];
+	unsigned int wakeupsrc_cat[MT_IRQ_REMAIN_MAX];
+	unsigned int wakeupsrc[MT_IRQ_REMAIN_MAX];
+};
+
+#define PLAT_RC_STATUS_READY		BIT(0)
+#define PLAT_RC_STATUS_FEATURE_EN	BIT(1)
+#define PLAT_RC_STATUS_UART_NONSLEEP	BIT(31)
+
+struct mt_lpm_tz {
+	int (*pwr_prompt)(unsigned int cpu, const psci_power_state_t *state);
+	int (*pwr_reflect)(unsigned int cpu, const psci_power_state_t *state);
+
+	int (*pwr_cpu_on)(unsigned int cpu, const psci_power_state_t *state);
+	int (*pwr_cpu_dwn)(unsigned int cpu, const psci_power_state_t *state);
+
+	int (*pwr_cluster_on)(unsigned int cpu,
+					const psci_power_state_t *state);
+	int (*pwr_cluster_dwn)(unsigned int cpu,
+					const psci_power_state_t *state);
+
+	int (*pwr_mcusys_on)(unsigned int cpu, const psci_power_state_t *state);
+	int (*pwr_mcusys_on_finished)(unsigned int cpu,
+					const psci_power_state_t *state);
+	int (*pwr_mcusys_dwn)(unsigned int cpu,
+					const psci_power_state_t *state);
+};
+
+const struct mt_lpm_tz *mt_plat_cpu_pm_init(void);
+
+#endif /* PLAT_MTK_LPM_H */
diff --git a/plat/mediatek/mt8186/include/plat_pm.h b/plat/mediatek/mt8186/include/plat_pm.h
new file mode 100644
index 0000000..436db34
--- /dev/null
+++ b/plat/mediatek/mt8186/include/plat_pm.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_PM_H
+#define PLAT_PM_H
+
+#include <lib/utils_def.h>
+
+#define MT_PLAT_PWR_STATE_CPU			U(1)
+#define MT_PLAT_PWR_STATE_CLUSTER		U(2)
+#define MT_PLAT_PWR_STATE_MCUSYS		U(3)
+#define MT_PLAT_PWR_STATE_SUSPEND2IDLE		U(8)
+#define MT_PLAT_PWR_STATE_SYSTEM_SUSPEND	U(9)
+
+#define MTK_LOCAL_STATE_RUN			U(0)
+#define MTK_LOCAL_STATE_RET			U(1)
+#define MTK_LOCAL_STATE_OFF			U(2)
+
+#define MTK_AFFLVL_CPU				U(0)
+#define MTK_AFFLVL_CLUSTER			U(1)
+#define MTK_AFFLVL_MCUSYS			U(2)
+#define MTK_AFFLVL_SYSTEM			U(3)
+
+#define IS_CLUSTER_OFF_STATE(s)					\
+		is_local_state_off(s->pwr_domain_state[MTK_AFFLVL_CLUSTER])
+#define IS_MCUSYS_OFF_STATE(s)					\
+		is_local_state_off(s->pwr_domain_state[MTK_AFFLVL_MCUSYS])
+#define IS_SYSTEM_SUSPEND_STATE(s)				\
+		is_local_state_off(s->pwr_domain_state[MTK_AFFLVL_SYSTEM])
+
+#define IS_PLAT_SUSPEND_ID(stateid)				\
+		((stateid == MT_PLAT_PWR_STATE_SUSPEND2IDLE)	\
+		|| (stateid == MT_PLAT_PWR_STATE_SYSTEM_SUSPEND))
+
+#endif /* PLAT_PM_H */
diff --git a/plat/mediatek/mt8186/include/platform_def.h b/plat/mediatek/mt8186/include/platform_def.h
index 5a56556..479a8d4 100644
--- a/plat/mediatek/mt8186/include/platform_def.h
+++ b/plat/mediatek/mt8186/include/platform_def.h
@@ -19,6 +19,8 @@
 #define MTK_DEV_RNG2_BASE	MT_GIC_BASE
 #define MTK_DEV_RNG2_SIZE	0x600000
 
+#define SPM_BASE		(IO_PHYS + 0x00006000)
+
 /*******************************************************************************
  * GPIO related constants
  ******************************************************************************/
diff --git a/plat/mediatek/mt8186/plat_pm.c b/plat/mediatek/mt8186/plat_pm.c
index 6e3ab37..b014297 100644
--- a/plat/mediatek/mt8186/plat_pm.c
+++ b/plat/mediatek/mt8186/plat_pm.c
@@ -4,16 +4,363 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+/* common headers */
+#include <assert.h>
+
+#include <arch_helpers.h>
 #include <common/debug.h>
 #include <lib/psci/psci.h>
 
+/* platform specific headers */
+#include <mt_gic_v3.h>
+#include <mtspmc.h>
+#include <plat/common/platform.h>
+#include <plat_mtk_lpm.h>
+#include <plat_params.h>
+#include <plat_pm.h>
+
+/*
+ * Cluster state request:
+ * [0] : The CPU requires cluster power down
+ * [1] : The CPU requires cluster power on
+ */
+#define coordinate_cluster(onoff)	write_clusterpwrdn_el1(onoff)
+#define coordinate_cluster_pwron()	coordinate_cluster(1)
+#define coordinate_cluster_pwroff()	coordinate_cluster(0)
+
+/* platform secure entry point */
+static uintptr_t secure_entrypoint;
+/* per-CPU power state */
+static unsigned int plat_power_state[PLATFORM_CORE_COUNT];
+
+/* platform CPU power domain - ops */
+static const struct mt_lpm_tz *plat_mt_pm;
+
+static inline int plat_mt_pm_invoke(int (*func)(unsigned int cpu,
+						const psci_power_state_t *state),
+				    int cpu, const psci_power_state_t *state)
+{
+	int ret = -1;
+
+	if (func != NULL) {
+		ret = func(cpu, state);
+	}
+	return ret;
+}
+
+/*
+ * Common MTK_platform operations to power on/off a
+ * CPU in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
+ */
+static void plat_cpu_pwrdwn_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	(void)plat_mt_pm_invoke(plat_mt_pm->pwr_cpu_dwn, cpu, state);
+
+	if ((psci_get_pstate_pwrlvl(req_pstate) >= MTK_AFFLVL_CLUSTER) ||
+			(req_pstate == 0U)) { /* hotplug off */
+		coordinate_cluster_pwroff();
+	}
+
+	/* Prevent interrupts from spuriously waking up this CPU */
+	mt_gic_rdistif_save();
+	gicv3_cpuif_disable(cpu);
+	gicv3_rdistif_off(cpu);
+}
+
+static void plat_cpu_pwron_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	(void)plat_mt_pm_invoke(plat_mt_pm->pwr_cpu_on, cpu, state);
+
+	coordinate_cluster_pwron();
+
+	/*
+	 * If mcusys does power down before then restore
+	 * all CPUs' GIC Redistributors
+	 */
+	if (IS_MCUSYS_OFF_STATE(state)) {
+		mt_gic_rdistif_restore_all();
+	} else {
+		gicv3_rdistif_on(cpu);
+		gicv3_cpuif_enable(cpu);
+		mt_gic_rdistif_init();
+		mt_gic_rdistif_restore();
+	}
+}
+
+/*
+ * Common MTK_platform operations to power on/off a
+ * cluster in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
+ */
+static void plat_cluster_pwrdwn_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	if (plat_mt_pm_invoke(plat_mt_pm->pwr_cluster_dwn, cpu, state) != 0) {
+		coordinate_cluster_pwron();
+
+		/*
+		 * TODO:
+		 * Return on fail and add a 'return' here before
+		 * adding any code following the if-block.
+		 */
+	}
+}
+
+static void plat_cluster_pwron_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	if (plat_mt_pm_invoke(plat_mt_pm->pwr_cluster_on, cpu, state) != 0) {
+		/*
+		 * TODO:
+		 * return on fail and add a 'return' here before
+		 * adding any code following the if-block.
+		 */
+	}
+}
+
+/*
+ * Common MTK_platform operations to power on/off a
+ * mcusys in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
+ */
+static void plat_mcusys_pwrdwn_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	if (plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_dwn, cpu, state) != 0) {
+		return;		/* return on fail */
+	}
+
+	mt_gic_distif_save();
+	gic_sgi_save_all();
+}
+
+static void plat_mcusys_pwron_common(unsigned int cpu,
+		const psci_power_state_t *state, unsigned int req_pstate)
+{
+	assert(cpu == plat_my_core_pos());
+	assert(plat_mt_pm != NULL);
+
+	if (plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_on, cpu, state) != 0) {
+		/* return on fail */
+		return;
+	}
+
+	mt_gic_init();
+	mt_gic_distif_restore();
+	gic_sgi_restore_all();
+
+	(void)plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_on_finished, cpu, state);
+}
+
+/* plat_psci_ops implementation */
+static void plat_cpu_standby(plat_local_state_t cpu_state)
+{
+	uint64_t scr;
+
+	scr = read_scr_el3();
+	write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
+
+	isb();
+	dsb();
+	wfi();
+
+	write_scr_el3(scr);
+}
+
+static int plat_power_domain_on(u_register_t mpidr)
+{
+	unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
+	unsigned int cluster = 0U;
+
+	if (cpu >= PLATFORM_CORE_COUNT) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	if (!spm_get_cluster_powerstate(cluster)) {
+		spm_poweron_cluster(cluster);
+	}
+
+	/* init CPU reset arch as AARCH64 */
+	mcucfg_init_archstate(cluster, cpu, true);
+	mcucfg_set_bootaddr(cluster, cpu, secure_entrypoint);
+	spm_poweron_cpu(cluster, cpu);
+
+	return PSCI_E_SUCCESS;
+}
+
+static void plat_power_domain_on_finish(const psci_power_state_t *state)
+{
+	unsigned long mpidr = read_mpidr_el1();
+	unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
+
+	assert(cpu < PLATFORM_CORE_COUNT);
+
+	/* Allow IRQs to wakeup this core in IDLE flow */
+	mcucfg_enable_gic_wakeup(0U, cpu);
+
+	if (IS_CLUSTER_OFF_STATE(state)) {
+		plat_cluster_pwron_common(cpu, state, 0U);
+	}
+
+	plat_cpu_pwron_common(cpu, state, 0U);
+}
+
+static void plat_power_domain_off(const psci_power_state_t *state)
+{
+	unsigned long mpidr = read_mpidr_el1();
+	unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
+
+	assert(cpu < PLATFORM_CORE_COUNT);
+
+	plat_cpu_pwrdwn_common(cpu, state, 0U);
+	spm_poweroff_cpu(0U, cpu);
+
+	/* prevent unintended IRQs from waking up the hot-unplugged core */
+	mcucfg_disable_gic_wakeup(0U, cpu);
+
+	if (IS_CLUSTER_OFF_STATE(state)) {
+		plat_cluster_pwrdwn_common(cpu, state, 0U);
+	}
+}
+
+static void plat_power_domain_suspend(const psci_power_state_t *state)
+{
+	unsigned int cpu = plat_my_core_pos();
+
+	assert(cpu < PLATFORM_CORE_COUNT);
+	assert(plat_mt_pm != NULL);
+
+	(void)plat_mt_pm_invoke(plat_mt_pm->pwr_prompt, cpu, state);
+
+	/* Perform the common CPU specific operations */
+	plat_cpu_pwrdwn_common(cpu, state, plat_power_state[cpu]);
+
+	if (IS_CLUSTER_OFF_STATE(state)) {
+		/* Perform the common cluster specific operations */
+		plat_cluster_pwrdwn_common(cpu, state, plat_power_state[cpu]);
+	}
+
+	if (IS_MCUSYS_OFF_STATE(state)) {
+		/* Perform the common mcusys specific operations */
+		plat_mcusys_pwrdwn_common(cpu, state, plat_power_state[cpu]);
+	}
+}
+
+static void plat_power_domain_suspend_finish(const psci_power_state_t *state)
+{
+	unsigned int cpu = plat_my_core_pos();
+
+	assert(cpu < PLATFORM_CORE_COUNT);
+	assert(plat_mt_pm != NULL);
+
+	if (IS_MCUSYS_OFF_STATE(state)) {
+		/* Perform the common mcusys specific operations */
+		plat_mcusys_pwron_common(cpu, state, plat_power_state[cpu]);
+	}
+
+	if (IS_CLUSTER_OFF_STATE(state)) {
+		/* Perform the common cluster specific operations */
+		plat_cluster_pwron_common(cpu, state, plat_power_state[cpu]);
+	}
+
+	/* Perform the common CPU specific operations */
+	plat_cpu_pwron_common(cpu, state, plat_power_state[cpu]);
+
+	(void)plat_mt_pm_invoke(plat_mt_pm->pwr_reflect, cpu, state);
+}
+
+static int plat_validate_power_state(unsigned int power_state,
+					psci_power_state_t *req_state)
+{
+	unsigned int pstate = psci_get_pstate_type(power_state);
+	unsigned int aff_lvl = psci_get_pstate_pwrlvl(power_state);
+	unsigned int cpu = plat_my_core_pos();
+
+	if (aff_lvl > PLAT_MAX_PWR_LVL) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	if (pstate == PSTATE_TYPE_STANDBY) {
+		req_state->pwr_domain_state[0] = PLAT_MAX_RET_STATE;
+	} else {
+		unsigned int i;
+		unsigned int pstate_id = psci_get_pstate_id(power_state);
+		plat_local_state_t s = MTK_LOCAL_STATE_OFF;
+
+		/* Use pstate_id to be power domain state */
+		if (pstate_id > s) {
+			s = (plat_local_state_t)pstate_id;
+		}
+
+		for (i = 0U; i <= aff_lvl; i++) {
+			req_state->pwr_domain_state[i] = s;
+		}
+	}
+
+	plat_power_state[cpu] = power_state;
+	return PSCI_E_SUCCESS;
+}
+
+static void plat_get_sys_suspend_power_state(psci_power_state_t *req_state)
+{
+	unsigned int lv;
+	unsigned int cpu = plat_my_core_pos();
+
+	for (lv = PSCI_CPU_PWR_LVL; lv <= PLAT_MAX_PWR_LVL; lv++) {
+		req_state->pwr_domain_state[lv] = PLAT_MAX_OFF_STATE;
+	}
+
+	plat_power_state[cpu] =
+			psci_make_powerstate(
+				MT_PLAT_PWR_STATE_SYSTEM_SUSPEND,
+				PSTATE_TYPE_POWERDOWN, PLAT_MAX_PWR_LVL);
+
+	flush_dcache_range((uintptr_t)
+			&plat_power_state[cpu],
+			sizeof(plat_power_state[cpu]));
+}
+
 static const plat_psci_ops_t plat_psci_ops = {
+	.cpu_standby			= plat_cpu_standby,
+	.pwr_domain_on			= plat_power_domain_on,
+	.pwr_domain_on_finish		= plat_power_domain_on_finish,
+	.pwr_domain_off			= plat_power_domain_off,
+	.pwr_domain_suspend		= plat_power_domain_suspend,
+	.pwr_domain_suspend_finish	= plat_power_domain_suspend_finish,
+	.validate_power_state		= plat_validate_power_state,
+	.get_sys_suspend_power_state	= plat_get_sys_suspend_power_state
 };
 
 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
 			const plat_psci_ops_t **psci_ops)
 {
 	*psci_ops = &plat_psci_ops;
+	secure_entrypoint = sec_entrypoint;
+
+	/*
+	 * init the warm reset config for boot CPU
+	 * reset arch as AARCH64
+	 * reset addr as function bl31_warm_entrypoint()
+	 */
+	mcucfg_init_archstate(0U, 0U, true);
+	mcucfg_set_bootaddr(0U, 0U, secure_entrypoint);
+
+	spmc_init();
 
 	return 0;
 }
diff --git a/plat/mediatek/mt8186/platform.mk b/plat/mediatek/mt8186/platform.mk
index dd4b3af..31a030a 100644
--- a/plat/mediatek/mt8186/platform.mk
+++ b/plat/mediatek/mt8186/platform.mk
@@ -16,6 +16,7 @@
                  -I${MTK_PLAT_SOC}/drivers/gpio/               \
                  -I${MTK_PLAT_SOC}/drivers/pmic/                  \
                  -I${MTK_PLAT_SOC}/drivers/rtc/                   \
+                 -I${MTK_PLAT_SOC}/drivers/spmc/                  \
                  -I${MTK_PLAT_SOC}/include/
 
 GICV3_SUPPORT_GIC600        :=      1
@@ -55,6 +56,7 @@
                 ${MTK_PLAT_SOC}/drivers/gpio/mtgpio.c                 \
                 ${MTK_PLAT_SOC}/drivers/pmic/pmic.c                   \
                 ${MTK_PLAT_SOC}/drivers/rtc/rtc.c                     \
+                ${MTK_PLAT_SOC}/drivers/spmc/mtspmc.c                 \
                 ${MTK_PLAT_SOC}/plat_pm.c                             \
                 ${MTK_PLAT_SOC}/plat_sip_calls.c                      \
                 ${MTK_PLAT_SOC}/plat_topology.c