Merge changes I0c1f7d6c,I3bec0b58,If24cf213 into integration

* changes:
  feat(plat/mediatek/apu): add mt8195 APU clock and pll SiP call
  feat(plat/mediatek/apu): add mt8195 APU mcu boot and stop SiP call
  feat(plat/mediatek/apu): add mt8195 APU iommap regions
diff --git a/plat/mediatek/mt8195/aarch64/platform_common.c b/plat/mediatek/mt8195/aarch64/platform_common.c
index 4792746..2b95171 100644
--- a/plat/mediatek/mt8195/aarch64/platform_common.c
+++ b/plat/mediatek/mt8195/aarch64/platform_common.c
@@ -21,6 +21,14 @@
 			MT_DEVICE | MT_RW | MT_SECURE),
 	MAP_REGION_FLAT(eDP_SEC_BASE, eDP_SEC_SIZE,
 			MT_DEVICE | MT_RW | MT_SECURE),
+	MAP_REGION_FLAT(APUSYS_SCTRL_REVISER_BASE, APUSYS_SCTRL_REVISER_SIZE,
+			MT_DEVICE | MT_RW | MT_SECURE),
+	MAP_REGION_FLAT(APUSYS_APU_S_S_4_BASE, APUSYS_APU_S_S_4_SIZE,
+			MT_DEVICE | MT_RW | MT_SECURE),
+	MAP_REGION_FLAT(APUSYS_APU_PLL_BASE, APUSYS_APU_PLL_SIZE,
+			MT_DEVICE | MT_RW | MT_SECURE),
+	MAP_REGION_FLAT(APUSYS_APU_ACC_BASE, APUSYS_APU_ACC_SIZE,
+			MT_DEVICE | MT_RW | MT_SECURE),
 	{ 0 }
 };
 
diff --git a/plat/mediatek/mt8195/drivers/apusys/apupll.c b/plat/mediatek/mt8195/drivers/apusys/apupll.c
new file mode 100644
index 0000000..0eb8d4a
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/apupll.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/spinlock.h>
+
+#include <apupwr_clkctl.h>
+#include <apupwr_clkctl_def.h>
+#include <mtk_plat_common.h>
+#include <platform_def.h>
+
+uint32_t mixed_con0_addr[APUPLL_MAX] = {
+	APU_PLL4H_PLL1_CON0,
+	APU_PLL4H_PLL2_CON0,
+	APU_PLL4H_PLL3_CON0,
+	APU_PLL4H_PLL4_CON0,
+};
+
+uint32_t mixed_con1_addr[APUPLL_MAX] = {
+	APU_PLL4H_PLL1_CON1,
+	APU_PLL4H_PLL2_CON1,
+	APU_PLL4H_PLL3_CON1,
+	APU_PLL4H_PLL4_CON1,
+};
+
+uint32_t mixed_con3_addr[APUPLL_MAX] = {
+	APU_PLL4H_PLL1_CON3,
+	APU_PLL4H_PLL2_CON3,
+	APU_PLL4H_PLL3_CON3,
+	APU_PLL4H_PLL4_CON3,
+};
+
+uint32_t fhctl_dds_addr[APUPLL_MAX] = {
+	APU_PLL4H_FHCTL0_DDS,
+	APU_PLL4H_FHCTL1_DDS,
+	APU_PLL4H_FHCTL2_DDS,
+	APU_PLL4H_FHCTL3_DDS,
+};
+
+uint32_t fhctl_dvfs_addr[APUPLL_MAX] = {
+	APU_PLL4H_FHCTL0_DVFS,
+	APU_PLL4H_FHCTL1_DVFS,
+	APU_PLL4H_FHCTL2_DVFS,
+	APU_PLL4H_FHCTL3_DVFS,
+};
+
+uint32_t fhctl_mon_addr[APUPLL_MAX] = {
+	APU_PLL4H_FHCTL0_MON,
+	APU_PLL4H_FHCTL1_MON,
+	APU_PLL4H_FHCTL2_MON,
+	APU_PLL4H_FHCTL3_MON,
+};
+
+uint32_t fhctl_cfg_addr[APUPLL_MAX] = {
+	APU_PLL4H_FHCTL0_CFG,
+	APU_PLL4H_FHCTL1_CFG,
+	APU_PLL4H_FHCTL2_CFG,
+	APU_PLL4H_FHCTL3_CFG,
+};
+
+static spinlock_t apupll_lock;
+static spinlock_t npupll_lock;
+static spinlock_t apupll_1_lock;
+static spinlock_t apupll_2_lock;
+static uint32_t pll_cnt[APUPLL_MAX];
+/**
+ * vd2pllidx() - voltage domain to pll idx.
+ * @domain: the voltage domain for getting pll index.
+ *
+ * Caller will get correspond pll index by different voltage domain.
+ * pll_idx[0] --> APUPLL (MDLA0/1)
+ * pll_idx[1] --> NPUPLL (VPU0/1)
+ * pll_idx[2] --> APUPLL1(CONN)
+ * pll_idx[3] --> APUPLL2(IOMMU)
+ * The longer description may have multiple paragraphs.
+ *
+ * Context: Any context.
+ * Return:
+ * * 0 ~ 3	- return the corresponding pll index
+ * * -EEXIST	- cannot find pll idex of the specific voltage domain
+ *
+ */
+static int32_t vd2pllidx(enum dvfs_voltage_domain domain)
+{
+	int32_t ret;
+
+	switch (domain) {
+	case V_VPU0:
+	case V_VPU1:
+		ret = NPUPLL;
+		break;
+	case V_MDLA0:
+	case V_MDLA1:
+		ret = APUPLL;
+		break;
+	case V_TOP_IOMMU:
+		ret = APUPLL2;
+		break;
+	case V_APU_CONN:
+		ret = APUPLL1;
+		break;
+	default:
+		ERROR("%s wrong voltage domain: %d\n", __func__, domain);
+		ret = -EEXIST; /* non-exist */
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * pllidx2name() - return names of specific pll index.
+ * @pll_idx: input for specific pll index.
+ *
+ * Given pll index, this function will return name of it.
+ *
+ * Context: Any context.
+ * Return: Names of pll_idx, if found, otherwise will return "NULL"
+ */
+static const char *pllidx2name(int32_t pll_idx)
+{
+	static const char *const names[] = {
+		[APUPLL] = "PLL4H_PLL1",
+		[NPUPLL] = "PLL4H_PLL2",
+		[APUPLL1] = "PLL4H_PLL3",
+		[APUPLL2] = "PLL4H_PLL4",
+		[APUPLL_MAX] = "NULL",
+	};
+
+	if (pll_idx >= APUPLL_MAX) {
+		pll_idx = APUPLL_MAX;
+	}
+
+	return names[pll_idx];
+}
+
+/**
+ * _fhctl_mon_done() - poll whether fhctl HW mode is done.
+ * @pll_idx: input for specific pll index.
+ * @tar_dds: target dds for fhctl_mon to be.
+ *
+ * Given pll index, this function will continue to poll whether fhctl_mon
+ * has reached the expected value within 80us.
+ *
+ * Context: Any context.
+ * Return:
+ * * 0 - OK for fhctl_mon == tar_dds
+ * * -ETIMEDOUT - fhctl_mon not reach tar_dds
+ */
+static int32_t _fhctl_mon_done(uint32_t pll_idx, unsigned long tar_dds)
+{
+	unsigned long mon_dds;
+	uint64_t timeout = timeout_init_us(PLL_READY_TIME_20US);
+	int32_t ret = 0;
+
+	tar_dds &= DDS_MASK;
+	do {
+		mon_dds = apupwr_readl(fhctl_mon_addr[pll_idx]) & DDS_MASK;
+		if (mon_dds == tar_dds) {
+			break;
+		}
+
+		if (timeout_elapsed(timeout)) {
+			ERROR("%s monitor DDS 0x%08lx != expect 0x%08lx\n",
+				  pllidx2name(pll_idx), mon_dds, tar_dds);
+			ret = -ETIMEDOUT;
+			break;
+		}
+	} while (mon_dds != tar_dds);
+
+	return ret;
+}
+
+/**
+ * _pll_get_postdiv_reg() - return current post dividor of pll_idx
+ * @pll_idx: input for specific pll index.
+ *
+ * Given pll index, this function will return its current post dividor.
+ *
+ * Context: Any context.
+ * Return: post dividor of current pll_idx.
+ *
+ */
+static uint32_t _pll_get_postdiv_reg(uint32_t pll_idx)
+{
+	int32_t pll_postdiv_reg = 0;
+	uint32_t val;
+
+	val = apupwr_readl(mixed_con1_addr[pll_idx]);
+	pll_postdiv_reg = (val >> POSDIV_SHIFT) & POSDIV_MASK;
+	return pll_postdiv_reg;
+}
+
+/**
+ * _set_postdiv_reg() - set pll_idx's post dividor.
+ * @pll_idx: Which PLL to enable/disable
+ * @post_div: the register value of post dividor to be wrtten.
+ *
+ * Below are lists of post dividor register value and its meaning:
+ * [31]     APUPLL_SDM_PCW_CHG
+ * [26:24]  APUPLL_POSDIV
+ * [21:0]   APUPLL_SDM_PCW (8bit integer + 14bit fraction)
+ * expected freq range ----- divider-------post divider in reg:
+ * >1500M   (1500/ 1)         -> 1        -> 0(2 to the zero power)
+ * > 750M   (1500/ 2)         -> 2        -> 1(2 to the 1st  power)
+ * > 375M   (1500/ 4)         -> 4        -> 2(2 to the 2nd  power)
+ * > 187.5M (1500/ 8)         -> 8        -> 3(2 to the 3rd  power)
+ * > 93.75M (1500/16)         -> 16       -> 4(2 to the 4th  power)
+ *
+ * Context: Any context.
+ */
+static void _set_postdiv_reg(uint32_t pll_idx, uint32_t post_div)
+{
+	apupwr_clrbits(POSDIV_MASK << POSDIV_SHIFT, mixed_con1_addr[pll_idx]);
+	apupwr_setbits((post_div & POSDIV_MASK) << POSDIV_SHIFT,
+			mixed_con1_addr[pll_idx]);
+}
+
+/**
+ * _cal_pll_data() - input freq, calculate correspond post dividor and dds.
+ * @pd: address of output post dividor.
+ * @dds: address of output dds.
+ * @freq: input frequency.
+ *
+ * Given freq, this function will calculate correspond post dividor and dds.
+ *
+ * Context: Any context.
+ * Return:
+ * * 0 - done for calculating post dividor and dds.
+ */
+static int32_t _cal_pll_data(uint32_t *pd, uint32_t *dds, uint32_t freq)
+{
+	uint32_t vco, postdiv_val = 1, postdiv_reg = 0;
+	uint32_t pcw_val;
+
+	vco = freq;
+	postdiv_val = 1;
+	postdiv_reg = 0;
+	while (vco <= FREQ_VCO_MIN) {
+		postdiv_val = postdiv_val << 1;
+		postdiv_reg = postdiv_reg + 1;
+		vco = vco << 1;
+	}
+
+	pcw_val = vco * (1 << PCW_FRACTIONAL_SHIFT);
+	pcw_val = pcw_val / FREQ_FIN;
+
+	if (postdiv_reg == 0) { /* Fvco * 2 with post_divider = 2 */
+		pcw_val = pcw_val * 2;
+		postdiv_val = postdiv_val << 1;
+		postdiv_reg = postdiv_reg + 1;
+	} /* Post divider is 1 is not available */
+	*pd = postdiv_reg;
+	*dds = pcw_val | RG_PLL_SDM_PCW_CHG;
+
+	return 0;
+}
+
+/**
+ * _pll_en() - enable/disable RG_PLL_EN of CON1 for pll[pll_idx]
+ * @pll_idx: Which PLL to enable/disable
+ * @on: 1 -> enable, 0 -> disable.
+ *
+ * This funciton will only change RG_PLL_EN of CON1 for pll[pll_idx].
+ *
+ * Context: Any context.
+ */
+static void _pll_en(uint32_t pll_idx, bool on)
+{
+	if (on) {
+		apupwr_setbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
+	} else {
+		apupwr_clrbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
+	}
+}
+
+/**
+ * _pll_pwr() - enable/disable PLL_SDM_PWR_ON of CON3 for pll[pll_idx]
+ * @pll_idx: Which PLL to enable/disable
+ * @on: 1 -> enable, 0 -> disable.
+ *
+ * This funciton will only change PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
+ *
+ * Context: Any context.
+ */
+static void _pll_pwr(uint32_t pll_idx, bool on)
+{
+	if (on) {
+		apupwr_setbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
+	} else {
+		apupwr_clrbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
+	}
+}
+
+/**
+ * _pll_iso() - enable/disable PLL_SDM_ISO_EN of CON3 for pll[pll_idx]
+ * @pll_idx: Which PLL to enable/disable
+ * @enable: 1 -> turn on isolation, 0 -> turn off isolation.
+ *
+ * This funciton will turn on/off pll isolation by
+ * changing PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
+ *
+ * Context: Any context.
+ */
+static void _pll_iso(uint32_t pll_idx, bool enable)
+{
+	if (enable) {
+		apupwr_setbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
+	} else {
+		apupwr_clrbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
+	}
+}
+
+/**
+ * _pll_switch() - entry point to turn whole PLL on/off
+ * @pll_idx: Which PLL to enable/disable
+ * @on: 1 -> enable, 0 -> disable.
+ * @fhctl_en: enable or disable fhctl function
+ *
+ * This is the entry poing for controlling pll and fhctl funciton on/off.
+ * Caller can chose only enable pll instead of fhctl function.
+ *
+ * Context: Any context.
+ * Return:
+ * * 0		- done for enable pll or fhctl as well.
+ */
+static int32_t _pll_switch(uint32_t pll_idx, bool on, bool fhctl_en)
+{
+	int32_t ret = 0;
+
+	if (pll_idx >= APUPLL_MAX) {
+		ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (on) {
+		_pll_pwr(pll_idx, true);
+		udelay(PLL_CMD_READY_TIME_1US);
+		_pll_iso(pll_idx, false);
+		udelay(PLL_CMD_READY_TIME_1US);
+		_pll_en(pll_idx, true);
+		udelay(PLL_READY_TIME_20US);
+	} else {
+		_pll_en(pll_idx, false);
+		_pll_iso(pll_idx, true);
+		_pll_pwr(pll_idx, false);
+	}
+
+err:
+	return ret;
+}
+
+/**
+ * apu_pll_enable() - API for smc function to enable/disable pll
+ * @pll_idx: Which pll to enable/disable.
+ * @enable: 1 -> enable, 0 -> disable.
+ * @fhctl_en: enable or disable fhctl function
+ *
+ * pll_idx[0] --> APUPLL (MDLA0/1)
+ * pll_idx[1] --> NPUPLL (VPU0/1)
+ * pll_idx[2] --> APUPLL1(CONN)
+ * pll_idx[3] --> APUPLL2(IOMMU)
+ * The differences between _pll_switch are:
+ * 1. Atomic update pll reference cnt to protect double enable pll &
+ * close pll during user is not zero.
+ *
+ * Context: Any context.
+ * Return:
+ * * 0 - done for enable pll or fhctl as well.
+ */
+int32_t apu_pll_enable(int32_t pll_idx, bool enable, bool fhctl_en)
+{
+	int32_t ret = 0;
+
+	if (pll_idx >= APUPLL_MAX) {
+		ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (enable) {
+		switch (pll_idx) {
+		case APUPLL:
+			spin_lock(&apupll_lock);
+			if (pll_cnt[APUPLL] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			pll_cnt[APUPLL]++;
+			spin_unlock(&apupll_lock);
+			break;
+		case NPUPLL:
+			spin_lock(&npupll_lock);
+			if (pll_cnt[NPUPLL] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			pll_cnt[NPUPLL]++;
+			spin_unlock(&npupll_lock);
+			break;
+		case APUPLL1:
+			spin_lock(&apupll_1_lock);
+			if (pll_cnt[APUPLL1] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			pll_cnt[APUPLL1]++;
+			spin_unlock(&apupll_1_lock);
+			break;
+		case APUPLL2:
+			spin_lock(&apupll_2_lock);
+			if (pll_cnt[APUPLL2] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			pll_cnt[APUPLL2]++;
+			spin_unlock(&apupll_2_lock);
+			break;
+		default:
+			ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
+			ret = -EINVAL;
+			break;
+		}
+	} else {
+		switch (pll_idx) {
+		case APUPLL:
+			spin_lock(&apupll_lock);
+			if (pll_cnt[APUPLL]) {
+				pll_cnt[APUPLL]--;
+			}
+			if (pll_cnt[APUPLL] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			spin_unlock(&apupll_lock);
+			break;
+		case NPUPLL:
+			spin_lock(&npupll_lock);
+			if (pll_cnt[NPUPLL]) {
+				pll_cnt[NPUPLL]--;
+			}
+			if (pll_cnt[NPUPLL] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			spin_unlock(&npupll_lock);
+			break;
+		case APUPLL1:
+			spin_lock(&apupll_1_lock);
+			if (pll_cnt[APUPLL1]) {
+				pll_cnt[APUPLL1]--;
+			}
+			if (pll_cnt[APUPLL1] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			spin_unlock(&apupll_1_lock);
+			break;
+		case APUPLL2:
+			spin_lock(&apupll_2_lock);
+			if (pll_cnt[APUPLL2]) {
+				pll_cnt[APUPLL2]--;
+			}
+			if (pll_cnt[APUPLL2] == 0) {
+				_pll_switch(pll_idx, enable, fhctl_en);
+			}
+			spin_unlock(&apupll_2_lock);
+			break;
+		default:
+			ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+err:
+	return ret;
+}
+
+/**
+ * anpu_pll_set_rate() - API for smc function to set rate of voltage domain.
+ * @domain: Which pll of correspond voltage domain to change rate.
+ * @mode: which mode to use when set_rate
+ * @freq: which frequency to set.
+ *
+ * For V_VPU0/1, it will only allow 1 of them to modify NPUPLL
+ * such that there will be no race condition happen.
+ *
+ * For V_MDLA0/1, it will only allow 1 of them to modify APUPLL1
+ * such that there will be no race condition happen.
+ *
+ * There are 3 kinds of modes to set pll's rate.
+ * 1. pure sw mode: (CON0_PCW)
+ *        fhctl function is off and change rate by programming CON1_PCW.
+ * 2. fhctl sw mode: (FHCTL_SW)
+ *        fhctl function is on and change rate by programming fhctl_dds.
+ *        (post dividor is still need to program CON1_PCW)
+ * 3. fhctl hw mode: (FHCTL_HW)
+ *        fhctl function is on and change rate by programming fhctl_dvfs.
+ *        (post dividor is still need to program CON1_PCW)
+ *
+ * Context: Any context.
+ * Return:
+ * * 0 - done for set rate of voltage domain.
+ */
+int32_t anpu_pll_set_rate(enum dvfs_voltage_domain domain,
+		      enum pll_set_rate_mode mode, int32_t freq)
+{
+	uint32_t pd, old_pd, dds;
+	int32_t pll_idx, ret = 0;
+
+	pll_idx = vd2pllidx(domain);
+	if (pll_idx < 0) {
+		ret = pll_idx;
+		goto err;
+	}
+
+	_cal_pll_data(&pd, &dds, freq / 1000);
+
+	INFO("%s %s new post_div=%d, target dds=0x%08x(%dMhz) mode = %d\n",
+	     __func__, pllidx2name(pll_idx), pd, dds, freq / 1000, mode);
+
+	/* spin_lock for NPULL, since vpu0/1 share npupll */
+	if (domain == V_VPU0 || domain == V_VPU1) {
+		spin_lock(&npupll_lock);
+	}
+
+	/* spin_lock for APUPLL, since mdla0/1 shate apupll */
+	if (domain == V_MDLA0 || domain == V_MDLA1) {
+		spin_lock(&apupll_lock);
+	}
+
+	switch (mode) {
+	case CON0_PCW:
+		pd = RG_PLL_SDM_PCW_CHG |
+		     (pd & POSDIV_MASK) << POSDIV_SHIFT | dds;
+		apupwr_writel(pd, mixed_con1_addr[pll_idx]);
+		udelay(PLL_READY_TIME_20US);
+		break;
+	case FHCTL_SW:
+		/* pll con0 disable */
+		_pll_en(pll_idx, false);
+		apupwr_writel(dds, fhctl_dds_addr[pll_idx]);
+		_set_postdiv_reg(pll_idx, pd);
+		apupwr_setbits(PLL_TGL_ORG, fhctl_dds_addr[pll_idx]);
+		udelay(PLL_CMD_READY_TIME_1US);
+		/* pll con0 enable */
+		_pll_en(pll_idx, true);
+		udelay(PLL_READY_TIME_20US);
+		break;
+	case FHCTL_HW:
+		old_pd = _pll_get_postdiv_reg(pll_idx);
+		if (pd > old_pd) {
+			_set_postdiv_reg(pll_idx, pd);
+			apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
+		} else {
+			apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
+			_set_postdiv_reg(pll_idx, pd);
+		}
+		ret = _fhctl_mon_done(pll_idx, dds);
+		break;
+	default:
+		ERROR("%s input wrong mode: %d\n", __func__, mode);
+		ret = -EINVAL;
+		break;
+	}
+
+	/* spin_lock for NPULL, since vpu0/1 share npupll */
+	if (domain == V_VPU0 || domain == V_VPU1) {
+		spin_unlock(&npupll_lock);
+	}
+
+	/* spin_lock for APUPLL, since mdla0/1 share apupll */
+	if (domain == V_MDLA0 || domain == V_MDLA1) {
+		spin_unlock(&apupll_lock);
+	}
+
+err:
+	return ret;
+}
diff --git a/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.c b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.c
new file mode 100644
index 0000000..465054d
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+
+#include <apupwr_clkctl.h>
+#include <apupwr_clkctl_def.h>
+#include <mtk_plat_common.h>
+#include <platform_def.h>
+
+/* 8195 use PCW mode to change freq directly */
+enum pll_set_rate_mode PLL_MODE = CON0_PCW;
+
+char *buck_domain_str[APUSYS_BUCK_DOMAIN_NUM] = {
+	"V_VPU0",
+	"V_VPU1",
+	"V_MDLA0",
+	"V_MDLA1",
+	"V_APU_CONN",
+	"V_TOP_IOMMU",
+	"V_VCORE",
+};
+
+uint32_t aacc_set[APUSYS_BUCK_DOMAIN_NUM] = {
+	APU_ACC_CONFG_SET1, APU_ACC_CONFG_SET2,
+	APU_ACC_CONFG_SET4, APU_ACC_CONFG_SET5,
+	APU_ACC_CONFG_SET0, APU_ACC_CONFG_SET7
+};
+
+uint32_t aacc_clr[APUSYS_BUCK_DOMAIN_NUM] = {
+	APU_ACC_CONFG_CLR1, APU_ACC_CONFG_CLR2,
+	APU_ACC_CONFG_CLR4, APU_ACC_CONFG_CLR5,
+	APU_ACC_CONFG_CLR0, APU_ACC_CONFG_CLR7
+};
+
+struct reg_seq {
+	uint32_t address;
+	uint32_t val;
+};
+
+static const struct reg_seq init_acc_cfg[] = {
+	{ APU_ACC_CONFG_SET0, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR0, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET0, BIT(BIT_SEL_APU_DIV2) },
+	{ APU_ACC_CONFG_SET7, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR7, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET7, BIT(BIT_SEL_APU_DIV2) },
+	{ APU_ACC_CONFG_SET1, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR1, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET1, BIT(BIT_SEL_APU_DIV2) },
+	{ APU_ACC_CONFG_SET2, BIT(BIT_INVEN_OUT) },
+	{ APU_ACC_CONFG_SET2, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR2, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET2, BIT(BIT_SEL_APU_DIV2) },
+	{ APU_ACC_CONFG_SET4, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR4, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET4, BIT(BIT_SEL_APU_DIV2) },
+	{ APU_ACC_CONFG_SET5, BIT(BIT_INVEN_OUT) },
+	{ APU_ACC_CONFG_SET5, BIT(BIT_SEL_APU) },
+	{ APU_ACC_CONFG_CLR5, BIT(BIT_CGEN_SOC) },
+	{ APU_ACC_CONFG_SET5, BIT(BIT_SEL_APU_DIV2) },
+};
+
+int32_t apupwr_smc_acc_init_all(void)
+{
+	int32_t i;
+
+	for (i = 0; i < ARRAY_SIZE(init_acc_cfg); i++) {
+		apupwr_writel(init_acc_cfg[i].val,
+			      init_acc_cfg[i].address);
+	}
+
+	/* Deault ACC will raise APU_DIV_2 */
+	apupwr_smc_pll_set_rate(BUCK_VCONN_DOMAIN_DEFAULT_FREQ,
+				true, V_APU_CONN);
+
+	apupwr_smc_pll_set_rate(BUCK_VCONN_DOMAIN_DEFAULT_FREQ,
+				true, V_TOP_IOMMU);
+
+	apupwr_smc_pll_set_rate(BUCK_VVPU_DOMAIN_DEFAULT_FREQ,
+				true, V_VPU0);
+
+	apupwr_smc_pll_set_rate(BUCK_VMDLA_DOMAIN_DEFAULT_FREQ,
+				true, V_MDLA0);
+
+	return 0;
+}
+
+void apupwr_smc_acc_top(bool enable)
+{
+	if (enable) {
+		apupwr_writel(BIT(BIT_CGEN_APU), aacc_set[V_APU_CONN]);
+		apupwr_writel(BIT(BIT_CGEN_APU), aacc_set[V_TOP_IOMMU]);
+	} else {
+		apupwr_writel(BIT(BIT_CGEN_APU), aacc_clr[V_APU_CONN]);
+		apupwr_writel(BIT(BIT_CGEN_APU), aacc_clr[V_TOP_IOMMU]);
+	}
+}
+
+/*
+ * acc_clk_set_parent:ACC MUX select
+ * 0. freq parameters here, only ACC clksrc is valid
+ * 1. Switch between APUPLL <=> Parking (F26M, PARK)
+ * 2. Turn on/off CG_F26M, CG_PARK, CG_SOC, but no CG_APU
+ * 3. Clear APU Div2 while Parking
+ * 4. Only use clksrc of APUPLL while ACC CG_APU is on
+ */
+int32_t apupwr_smc_acc_set_parent(uint32_t freq, uint32_t domain)
+{
+	uint32_t acc_set = 0;
+	uint32_t acc_clr = 0;
+	int32_t ret = 0;
+
+	if (freq > DVFS_FREQ_ACC_APUPLL) {
+		ERROR("%s wrong clksrc: %d\n", __func__, freq);
+		ret = -EIO;
+		goto err;
+	}
+
+	switch (domain) {
+	case V_VPU1:
+	case V_VPU0:
+	case V_MDLA1:
+	case V_MDLA0:
+	case V_APU_CONN:
+	case V_TOP_IOMMU:
+		acc_set = aacc_set[domain];
+		acc_clr = aacc_clr[domain];
+		break;
+	default:
+		ret = -EIO;
+		break;
+	}
+
+	/* Select park source */
+	switch (freq) {
+	case DVFS_FREQ_ACC_PARKING:
+		/* Select park source */
+		apupwr_writel(BIT(BIT_SEL_PARK), acc_set);
+		apupwr_writel(BIT(BIT_SEL_F26M), acc_clr);
+		/* Enable park cg */
+		apupwr_writel(BIT(BIT_CGEN_PARK), acc_set);
+		apupwr_writel(BIT(BIT_CGEN_F26M) | BIT(BIT_CGEN_SOC), acc_clr);
+		/* Select park path */
+		apupwr_writel(BIT(BIT_SEL_APU), acc_clr);
+		/* clear apu div 2 */
+		apupwr_writel(BIT(BIT_SEL_APU_DIV2), acc_clr);
+		break;
+
+	case DVFS_FREQ_ACC_APUPLL:
+		/* Select park path */
+		apupwr_writel(BIT(BIT_SEL_APU), acc_set);
+		/* Clear park cg */
+		apupwr_writel(BIT(BIT_CGEN_PARK) | BIT(BIT_CGEN_F26M) |
+			      BIT(BIT_CGEN_SOC), acc_clr);
+		break;
+
+	case DVFS_FREQ_ACC_SOC:
+		/* Select park source */
+		apupwr_writel(BIT(BIT_SEL_PARK), acc_clr);
+		apupwr_writel(BIT(BIT_SEL_F26M), acc_clr);
+		/* Enable park cg */
+		apupwr_writel(BIT(BIT_CGEN_SOC), acc_set);
+		apupwr_writel(BIT(BIT_CGEN_F26M) | BIT(BIT_CGEN_PARK), acc_clr);
+		/* Select park path */
+		apupwr_writel(BIT(BIT_SEL_APU), acc_clr);
+		/* clear apu div 2 */
+		apupwr_writel(BIT(BIT_SEL_APU_DIV2), acc_clr);
+		break;
+
+	case DVFS_FREQ_ACC_26M:
+	case DVFS_FREQ_NOT_SUPPORT:
+	default:
+		/* Select park source */
+		apupwr_writel(BIT(BIT_SEL_F26M), acc_set);
+		apupwr_writel(BIT(BIT_SEL_PARK), acc_clr);
+		/* Enable park cg */
+		apupwr_writel(BIT(BIT_CGEN_F26M), acc_set);
+		apupwr_writel(BIT(BIT_CGEN_PARK) | BIT(BIT_CGEN_SOC), acc_clr);
+		/* Select park path */
+		apupwr_writel(BIT(BIT_SEL_APU), acc_clr);
+		/* clear apu div 2 */
+		apupwr_writel(BIT(BIT_SEL_APU_DIV2), acc_clr);
+		ERROR("[APUPWR] %s wrong ACC clksrc : %d, force assign 26M\n",
+		      __func__, freq);
+		break;
+	}
+
+err:
+	return ret;
+}
+
+int32_t apupwr_smc_pll_set_rate(uint32_t freq, bool div2, uint32_t domain)
+{
+	int32_t ret = 0;
+	uint32_t acc_set0 = 0, acc_set1 = 0;
+
+	if (freq > DVFS_FREQ_MAX) {
+		ERROR("%s wrong freq: %d\n", __func__, freq);
+		ret = -EIO;
+		goto err;
+	}
+
+	/*
+	 * Switch to Parking src
+	 * 1. Need to switch out all ACCs sharing the same apupll
+	 */
+	switch (domain) {
+	case V_MDLA0:
+	case V_MDLA1:
+		acc_set0 = APU_ACC_CONFG_SET4;
+		acc_set1 = APU_ACC_CONFG_SET5;
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_MDLA0);
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_MDLA1);
+		break;
+	case V_VPU0:
+	case V_VPU1:
+		acc_set0 = APU_ACC_CONFG_SET1;
+		acc_set1 = APU_ACC_CONFG_SET2;
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_VPU0);
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_VPU1);
+		break;
+	case V_APU_CONN:
+		acc_set0 = APU_ACC_CONFG_SET0;
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_APU_CONN);
+		break;
+	case V_TOP_IOMMU:
+		acc_set0 = APU_ACC_CONFG_SET7;
+		ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_PARKING,
+						V_TOP_IOMMU);
+		break;
+	default:
+		ERROR("[APUPWR] %s %d invalid domain (%d)\n",
+		      __func__, __LINE__, domain);
+		ret = -EIO;
+		goto err;
+	}
+
+	anpu_pll_set_rate(domain, PLL_MODE, (div2) ? (freq * 2) : freq);
+
+	if (div2) {
+		apupwr_writel(BIT(BIT_SEL_APU_DIV2), acc_set0);
+		if (acc_set1) {
+			apupwr_writel(BIT(BIT_SEL_APU_DIV2), acc_set1);
+		}
+	}
+
+	/*
+	 * Switch back to APUPLL
+	 * Only switch back to APUPLL while CG_APU on
+	 * And clksrc is not APUPLL
+	 */
+	switch (domain) {
+	case V_VPU0:
+	case V_VPU1:
+		if ((apupwr_readl(acc_set0) & BIT(BIT_CGEN_APU)) &&
+		     !(apupwr_readl(acc_set0) & BIT(BIT_SEL_APU))) {
+			ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_APUPLL,
+							V_VPU0);
+		}
+		if ((apupwr_readl(acc_set1) & BIT(BIT_CGEN_APU)) &&
+		     !(apupwr_readl(acc_set1) & BIT(BIT_SEL_APU))) {
+			ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_APUPLL,
+							V_VPU1);
+		}
+		break;
+	case V_MDLA0:
+	case V_MDLA1:
+		if ((apupwr_readl(acc_set0) & BIT(BIT_CGEN_APU)) &&
+		     !(apupwr_readl(acc_set0) & BIT(BIT_SEL_APU))) {
+			ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_APUPLL,
+							V_MDLA0);
+		}
+		if ((apupwr_readl(acc_set1) & BIT(BIT_CGEN_APU)) &&
+		     !(apupwr_readl(acc_set1) & BIT(BIT_SEL_APU))) {
+			ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_APUPLL,
+							V_MDLA1);
+		}
+		break;
+	case V_APU_CONN:
+	case V_TOP_IOMMU:
+		if ((apupwr_readl(acc_set0) & BIT(BIT_CGEN_APU)) &&
+		    !(apupwr_readl(acc_set0) & BIT(BIT_SEL_APU))) {
+			ret = apupwr_smc_acc_set_parent(DVFS_FREQ_ACC_APUPLL,
+							domain);
+		}
+		break;
+	default:
+		ERROR("[APUPWR] %s %d invalid domain (%d)\n",
+		      __func__, __LINE__, domain);
+		ret = -EIO;
+		break;
+	}
+	INFO("[%s][%d] set domain %d to freq %d\n",
+	     __func__, __LINE__, domain, (div2) ? (freq * 2) : freq);
+
+err:
+	return ret;
+}
+
+int32_t apupwr_smc_bulk_pll(bool enable)
+{
+	int32_t ret = 0;
+	int32_t pll_idx;
+
+	if (enable) {
+		for (pll_idx = APUPLL; pll_idx < APUPLL_MAX; pll_idx++) {
+			ret = apu_pll_enable(pll_idx, enable, false);
+			if (ret != 0) {
+				goto err;
+			}
+		}
+	} else {
+		for (pll_idx = APUPLL2; pll_idx >= APUPLL; pll_idx--) {
+			ret = apu_pll_enable(pll_idx, enable, false);
+			if (ret != 0) {
+				goto err;
+			}
+		}
+	}
+
+err:
+	return ret;
+}
+
+void apupwr_smc_bus_prot_cg_on(void)
+{
+	apupwr_clrbits(AO_MD32_MNOC_MASK, APU_CSR_DUMMY_0);
+}
diff --git a/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.h b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.h
new file mode 100644
index 0000000..3b27c1b
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef APUPWR_CLKCTL_H
+#define APUPWR_CLKCTL_H
+
+#include <arch_helpers.h>
+#include <apupwr_clkctl_def.h>
+
+int32_t apupwr_smc_acc_init_all(void);
+void apupwr_smc_acc_top(bool enable);
+int32_t apupwr_smc_acc_set_parent(uint32_t freq, uint32_t domain);
+int32_t apupwr_smc_pll_set_rate(uint32_t pll, bool div2, uint32_t domain);
+int32_t apupwr_smc_bulk_pll(bool enable);
+void apupwr_smc_bus_prot_cg_on(void);
+
+int32_t apu_pll_enable(int32_t pll_idx, bool enable, bool fhctl_en);
+int32_t anpu_pll_set_rate(enum dvfs_voltage_domain domain,
+			  enum pll_set_rate_mode mode, int32_t freq);
+#endif /* APUPWR_CLKCTL_H */
diff --git a/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl_def.h b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl_def.h
new file mode 100644
index 0000000..6663ad9
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl_def.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef APUPWR_CLKCTL_DEF_H
+#define APUPWR_CLKCTL_DEF_H
+
+#include <lib/mmio.h>
+
+enum dvfs_voltage_domain {
+	V_VPU0			= 0,
+	V_VPU1			= 1,
+	V_MDLA0			= 2,
+	V_MDLA1			= 3,
+	V_APU_CONN		= 4,
+	V_TOP_IOMMU		= 5,
+	V_VCORE			= 6,
+	APUSYS_BUCK_DOMAIN_NUM	= 7,
+};
+
+enum dvfs_freq {
+	DVFS_FREQ_NOT_SUPPORT	= 0,
+	DVFS_FREQ_ACC_26M	= 1,
+	DVFS_FREQ_ACC_PARKING	= 2,
+	DVFS_FREQ_ACC_SOC	= 3,
+	DVFS_FREQ_ACC_APUPLL	= 4,
+	DVFS_FREQ_00_026000_F	= 26000,
+	DVFS_FREQ_00_208000_F	= 208000,
+	DVFS_FREQ_00_238000_F	= 238000,
+	DVFS_FREQ_00_273000_F	= 273000,
+	DVFS_FREQ_00_312000_F	= 312000,
+	DVFS_FREQ_00_358000_F	= 358000,
+	DVFS_FREQ_00_385000_F	= 385000,
+	DVFS_FREQ_00_499200_F	= 499200,
+	DVFS_FREQ_00_500000_F	= 500000,
+	DVFS_FREQ_00_525000_F	= 525000,
+	DVFS_FREQ_00_546000_F	= 546000,
+	DVFS_FREQ_00_594000_F	= 594000,
+	DVFS_FREQ_00_624000_F	= 624000,
+	DVFS_FREQ_00_688000_F	= 688000,
+	DVFS_FREQ_00_687500_F	= 687500,
+	DVFS_FREQ_00_728000_F	= 728000,
+	DVFS_FREQ_00_800000_F	= 800000,
+	DVFS_FREQ_00_832000_F	= 832000,
+	DVFS_FREQ_00_960000_F	= 960000,
+	DVFS_FREQ_00_1100000_F	= 1100000,
+};
+#define DVFS_FREQ_MAX (DVFS_FREQ_00_1100000_F)
+
+enum pll_set_rate_mode {
+	CON0_PCW		= 0,
+	FHCTL_SW		= 1,
+	FHCTL_HW		= 2,
+	PLL_SET_RATE_MODE_MAX	= 3,
+};
+
+enum apupll {
+	APUPLL		= 0,
+	NPUPLL		= 1,
+	APUPLL1		= 2,
+	APUPLL2		= 3,
+	APUPLL_MAX	= 4,
+};
+
+#define BUCK_VVPU_DOMAIN_DEFAULT_FREQ	(DVFS_FREQ_00_273000_F)
+#define BUCK_VMDLA_DOMAIN_DEFAULT_FREQ	(DVFS_FREQ_00_312000_F)
+#define BUCK_VCONN_DOMAIN_DEFAULT_FREQ	(DVFS_FREQ_00_208000_F)
+
+#define apupwr_writel(VAL, REG)		mmio_write_32((uintptr_t)REG, VAL)
+#define apupwr_writel_relax(VAL, REG)	mmio_write_32_relax((uintptr_t)REG, VAL)
+#define apupwr_readl(REG)		mmio_read_32((uintptr_t)REG)
+#define apupwr_clrbits(VAL, REG)	mmio_clrbits_32((uintptr_t)REG, VAL)
+#define apupwr_setbits(VAL, REG)	mmio_setbits_32((uintptr_t)REG, VAL)
+#define apupwr_clrsetbits(CLR_VAL, SET_VAL, REG)	\
+			  mmio_clrsetbits_32((uintptr_t)REG, CLR_VAL, SET_VAL)
+
+/* PLL and related register */
+#define APU_PLL_BASE		(APUSYS_APU_PLL_BASE)
+#define APU_PLL4H_PLL1_CON0	(APU_PLL_BASE + 0x008)
+#define APU_PLL4H_PLL1_CON1	(APU_PLL_BASE + 0x00C)
+#define APU_PLL4H_PLL1_CON3	(APU_PLL_BASE + 0x014)
+
+#define APU_PLL4H_PLL2_CON0	(APU_PLL_BASE + 0x018)
+#define APU_PLL4H_PLL2_CON1	(APU_PLL_BASE + 0x01C)
+#define APU_PLL4H_PLL2_CON3	(APU_PLL_BASE + 0x024)
+
+#define APU_PLL4H_PLL3_CON0	(APU_PLL_BASE + 0x028)
+#define APU_PLL4H_PLL3_CON1	(APU_PLL_BASE + 0x02C)
+#define APU_PLL4H_PLL3_CON3	(APU_PLL_BASE + 0x034)
+
+#define APU_PLL4H_PLL4_CON0	(APU_PLL_BASE + 0x038)
+#define APU_PLL4H_PLL4_CON1	(APU_PLL_BASE + 0x03C)
+#define APU_PLL4H_PLL4_CON3	(APU_PLL_BASE + 0x044)
+
+#define APU_PLL4H_FHCTL_HP_EN		(APU_PLL_BASE + 0x0E00)
+#define APU_PLL4H_FHCTL_UNITSLOPE_EN	(APU_PLL_BASE + 0x0E04)
+#define APU_PLL4H_FHCTL_CLK_CON		(APU_PLL_BASE + 0x0E08)
+#define APU_PLL4H_FHCTL_RST_CON		(APU_PLL_BASE + 0x0E0C)
+#define APU_PLL4H_FHCTL_SLOPE0		(APU_PLL_BASE + 0x0E10)
+#define APU_PLL4H_FHCTL_SLOPE1		(APU_PLL_BASE + 0x0E14)
+#define APU_PLL4H_FHCTL_DSSC_CFG	(APU_PLL_BASE + 0x0E18)
+#define APU_PLL4H_FHCTL_DSSC0_CON	(APU_PLL_BASE + 0x0E1C)
+#define APU_PLL4H_FHCTL_DSSC1_CON	(APU_PLL_BASE + 0x0E20)
+#define APU_PLL4H_FHCTL_DSSC2_CON	(APU_PLL_BASE + 0x0E24)
+#define APU_PLL4H_FHCTL_DSSC3_CON	(APU_PLL_BASE + 0x0E28)
+#define APU_PLL4H_FHCTL_DSSC4_CON	(APU_PLL_BASE + 0x0E2C)
+#define APU_PLL4H_FHCTL_DSSC5_CON	(APU_PLL_BASE + 0x0E30)
+#define APU_PLL4H_FHCTL_DSSC6_CON	(APU_PLL_BASE + 0x0E34)
+#define APU_PLL4H_FHCTL_DSSC7_CON	(APU_PLL_BASE + 0x0E38)
+#define APU_PLL4H_FHCTL0_CFG		(APU_PLL_BASE + 0x0E3C)
+#define APU_PLL4H_FHCTL0_UPDNLMT	(APU_PLL_BASE + 0x0E40)
+#define APU_PLL4H_FHCTL0_DDS		(APU_PLL_BASE + 0x0E44)
+#define APU_PLL4H_FHCTL0_DVFS		(APU_PLL_BASE + 0x0E48)
+#define APU_PLL4H_FHCTL0_MON		(APU_PLL_BASE + 0x0E4C)
+#define APU_PLL4H_FHCTL1_CFG		(APU_PLL_BASE + 0x0E50)
+#define APU_PLL4H_FHCTL1_UPDNLMT	(APU_PLL_BASE + 0x0E54)
+#define APU_PLL4H_FHCTL1_DDS		(APU_PLL_BASE + 0x0E58)
+#define APU_PLL4H_FHCTL1_DVFS		(APU_PLL_BASE + 0x0E5C)
+#define APU_PLL4H_FHCTL1_MON		(APU_PLL_BASE + 0x0E60)
+#define APU_PLL4H_FHCTL2_CFG		(APU_PLL_BASE + 0x0E64)
+#define APU_PLL4H_FHCTL2_UPDNLMT	(APU_PLL_BASE + 0x0E68)
+#define APU_PLL4H_FHCTL2_DDS		(APU_PLL_BASE + 0x0E6C)
+#define APU_PLL4H_FHCTL2_DVFS		(APU_PLL_BASE + 0x0E70)
+#define APU_PLL4H_FHCTL2_MON		(APU_PLL_BASE + 0x0E74)
+#define APU_PLL4H_FHCTL3_CFG		(APU_PLL_BASE + 0x0E78)
+#define APU_PLL4H_FHCTL3_UPDNLMT	(APU_PLL_BASE + 0x0E7C)
+#define APU_PLL4H_FHCTL3_DDS		(APU_PLL_BASE + 0x0E80)
+#define APU_PLL4H_FHCTL3_DVFS		(APU_PLL_BASE + 0x0E84)
+#define APU_PLL4H_FHCTL3_MON		(APU_PLL_BASE + 0x0E88)
+
+/* PLL4H_PLLx_CON0 */
+#define RG_PLL_EN		BIT(0)
+
+/* PLL4H_PLLx_CON1 */
+#define RG_PLL_SDM_PCW_CHG	BIT(31)
+#define POSDIV_SHIFT		(24U)
+#define POSDIV_MASK		(0x7)
+
+/* PLL4H_PLLx_CON3 */
+#define DA_PLL_SDM_PWR_ON	BIT(0)
+#define DA_PLL_SDM_ISO_EN	BIT(1)
+
+/* FHCTLx_DDS */
+#define DDS_MASK		GENMASK_32(21, 0)
+#define PCW_FRACTIONAL_SHIFT	14U
+#define PLL_TGL_ORG		BIT(31)
+
+#define PLL_READY_TIME_20US	(20U)
+#define PLL_CMD_READY_TIME_1US	(1U)
+
+#define FREQ_VCO_MIN		(1500U) /* 1500MHz*/
+#define FREQ_FIN		(26U)	/* 26M*/
+
+/* ACC and related register */
+#define APU_ACC_BASE		(APUSYS_APU_ACC_BASE)
+#define APU_ACC_CONFG_SET0	(APU_ACC_BASE + 0x000)
+#define APU_ACC_CONFG_SET1	(APU_ACC_BASE + 0x004)
+#define APU_ACC_CONFG_SET2	(APU_ACC_BASE + 0x008)
+#define APU_ACC_CONFG_SET4	(APU_ACC_BASE + 0x010)
+#define APU_ACC_CONFG_SET5	(APU_ACC_BASE + 0x014)
+#define APU_ACC_CONFG_SET7	(APU_ACC_BASE + 0x01C)
+
+#define APU_ACC_CONFG_CLR0	(APU_ACC_BASE + 0x040)
+#define APU_ACC_CONFG_CLR1	(APU_ACC_BASE + 0x044)
+#define APU_ACC_CONFG_CLR2	(APU_ACC_BASE + 0x048)
+#define APU_ACC_CONFG_CLR4	(APU_ACC_BASE + 0x050)
+#define APU_ACC_CONFG_CLR5	(APU_ACC_BASE + 0x054)
+#define APU_ACC_CONFG_CLR7	(APU_ACC_BASE + 0x05C)
+
+#define APU_ACC_FM_CONFG_SET	(APU_ACC_BASE + 0x0C0)
+#define APU_ACC_FM_CONFG_CLR	(APU_ACC_BASE + 0x0C4)
+#define APU_ACC_FM_SEL		(APU_ACC_BASE + 0x0C8)
+#define APU_ACC_FM_CNT		(APU_ACC_BASE + 0x0CC)
+
+/* APU AO control */
+#define APU_AO_CTRL_BASE	(APUSYS_APU_S_S_4_BASE)
+#define APU_CSR_DUMMY_0		(APU_AO_CTRL_BASE + 0x24)
+
+#define AO_MD32_MNOC_MASK	(BIT(1) | BIT(0))
+
+#define BIT_CGEN_F26M		(0)
+#define BIT_CGEN_PARK		(1)
+#define BIT_CGEN_SOC		(2)
+#define BIT_CGEN_APU		(3)
+#define BIT_CGEN_OUT		(4)
+#define BIT_SEL_PARK		(8)
+#define BIT_SEL_F26M		(9)
+#define BIT_SEL_APU_DIV2	(10)
+#define BIT_SEL_APU		(11)
+#define BIT_SEL_PARK_SRC_OUT	(12)
+#define BIT_INVEN_OUT		(15)
+
+#endif /* APUPWR_CLKCTL_DEF_H*/
diff --git a/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.c b/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.c
new file mode 100644
index 0000000..3ed26a1
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <drivers/console.h>
+#include <lib/mmio.h>
+
+#include <apupwr_clkctl.h>
+#include <mtk_apusys.h>
+#include <plat/common/platform.h>
+
+int32_t apusys_kernel_ctrl(uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4,
+			    uint32_t *ret1)
+{
+	int32_t ret = 0L;
+	uint32_t request_ops;
+
+	request_ops = (uint32_t)x1;
+
+	switch (request_ops) {
+	case MTK_SIP_APU_START_MCU:
+		/* setup addr[33:32] in reviser */
+		mmio_write_32(REVISER_SECUREFW_CTXT, 0U);
+		mmio_write_32(REVISER_USDRFW_CTXT, 0U);
+
+		/* setup secure sideband */
+		mmio_write_32(AO_SEC_FW,
+			      (SEC_FW_NON_SECURE << SEC_FW_SHIFT_NS) |
+			      (0U << SEC_FW_DOMAIN_SHIFT));
+
+		/* setup boot address */
+		mmio_write_32(AO_MD32_BOOT_CTRL, 0U);
+
+		/* setup pre-define region */
+		mmio_write_32(AO_MD32_PRE_DEFINE,
+			      (PRE_DEFINE_CACHE_TCM << PRE_DEFINE_SHIFT_0G) |
+			      (PRE_DEFINE_CACHE << PRE_DEFINE_SHIFT_1G) |
+			      (PRE_DEFINE_CACHE << PRE_DEFINE_SHIFT_2G) |
+			      (PRE_DEFINE_CACHE << PRE_DEFINE_SHIFT_3G));
+
+		/* release runstall */
+		mmio_write_32(AO_MD32_SYS_CTRL, SYS_CTRL_RUN);
+
+		INFO("[APUSYS] rev(0x%08x,0x%08x)\n",
+		     mmio_read_32(REVISER_SECUREFW_CTXT),
+		     mmio_read_32(REVISER_USDRFW_CTXT));
+		INFO("[APUSYS] ao(0x%08x,0x%08x,0x%08x,0x%08x,0x%08x)\n",
+		     mmio_read_32(AO_SEC_FW),
+		     mmio_read_32(AO_SEC_USR_FW),
+		     mmio_read_32(AO_MD32_BOOT_CTRL),
+		     mmio_read_32(AO_MD32_PRE_DEFINE),
+		     mmio_read_32(AO_MD32_SYS_CTRL));
+		break;
+	case MTK_SIP_APU_STOP_MCU:
+		/* hold runstall */
+		mmio_write_32(AO_MD32_SYS_CTRL, SYS_CTRL_STALL);
+
+		INFO("[APUSYS] md32_boot_ctrl=0x%08x,runstall=0x%08x\n",
+		     mmio_read_32(AO_MD32_BOOT_CTRL),
+		     mmio_read_32(AO_MD32_SYS_CTRL));
+		break;
+	case MTK_SIP_APUPWR_BUS_PROT_CG_ON:
+		apupwr_smc_bus_prot_cg_on();
+		break;
+	case MTK_SIP_APUPWR_BULK_PLL:
+		ret = apupwr_smc_bulk_pll((bool)x2);
+		break;
+	case MTK_SIP_APUPWR_ACC_INIT_ALL:
+		ret = apupwr_smc_acc_init_all();
+		break;
+	case MTK_SIP_APUPWR_ACC_TOP:
+		apupwr_smc_acc_top((bool)x2);
+		break;
+	default:
+		ERROR("%s, unknown request_ops=0x%x\n", __func__, request_ops);
+		break;
+	}
+
+	return ret;
+}
diff --git a/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.h b/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.h
new file mode 100644
index 0000000..639abd3
--- /dev/null
+++ b/plat/mediatek/mt8195/drivers/apusys/mtk_apusys.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MTK_APUSYS_H
+#define MTK_APUSYS_H
+
+#include <stdint.h>
+
+/* setup the SMC command ops */
+#define MTK_SIP_APU_START_MCU		(0x00U)
+#define MTK_SIP_APU_STOP_MCU		(0x01U)
+#define MTK_SIP_APUPWR_BUS_PROT_CG_ON	(0x02U)
+#define MTK_SIP_APUPWR_BULK_PLL		(0x03U)
+#define MTK_SIP_APUPWR_ACC_INIT_ALL	(0x04U)
+#define MTK_SIP_APUPWR_ACC_TOP		(0x05U)
+
+/* AO Register */
+#define AO_MD32_PRE_DEFINE	(APUSYS_APU_S_S_4_BASE + 0x00)
+#define AO_MD32_BOOT_CTRL	(APUSYS_APU_S_S_4_BASE + 0x04)
+#define AO_MD32_SYS_CTRL	(APUSYS_APU_S_S_4_BASE + 0x08)
+#define AO_SEC_FW		(APUSYS_APU_S_S_4_BASE + 0x10)
+#define AO_SEC_USR_FW		(APUSYS_APU_S_S_4_BASE + 0x14)
+
+#define PRE_DEFINE_CACHE_TCM	(0x3U)
+#define PRE_DEFINE_CACHE	(0x2U)
+#define PRE_DEFINE_SHIFT_0G	(0U)
+#define PRE_DEFINE_SHIFT_1G	(2U)
+#define PRE_DEFINE_SHIFT_2G	(4U)
+#define PRE_DEFINE_SHIFT_3G	(6U)
+
+#define SEC_FW_NON_SECURE	(1U)
+#define SEC_FW_SHIFT_NS		(4U)
+#define SEC_FW_DOMAIN_SHIFT	(0U)
+
+#define SEC_USR_FW_NON_SECURE	(1U)
+#define SEC_USR_FW_SHIFT_NS	(4U)
+#define SEC_USR_FW_DOMAIN_SHIFT	(0U)
+
+#define SYS_CTRL_RUN		(0U)
+#define SYS_CTRL_STALL		(1U)
+
+/* Reviser Register */
+#define REVISER_SECUREFW_CTXT	(APUSYS_SCTRL_REVISER_BASE + 0x100)
+#define REVISER_USDRFW_CTXT	(APUSYS_SCTRL_REVISER_BASE + 0x104)
+
+int32_t apusys_kernel_ctrl(uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4,
+			   uint32_t *ret1);
+#endif /* MTK_APUSYS_H */
diff --git a/plat/mediatek/mt8195/include/plat_sip_calls.h b/plat/mediatek/mt8195/include/plat_sip_calls.h
index ce25c6f..5562a67 100644
--- a/plat/mediatek/mt8195/include/plat_sip_calls.h
+++ b/plat/mediatek/mt8195/include/plat_sip_calls.h
@@ -10,7 +10,7 @@
 /*******************************************************************************
  * Plat SiP function constants
  ******************************************************************************/
-#define MTK_PLAT_SIP_NUM_CALLS    4
+#define MTK_PLAT_SIP_NUM_CALLS    6
 
 /* DFD */
 #define MTK_SIP_KERNEL_DFD_AARCH32	0x82000205
@@ -20,4 +20,8 @@
 #define MTK_SIP_DP_CONTROL_AARCH32	0x82000523
 #define MTK_SIP_DP_CONTROL_AARCH64	0xC2000523
 
+/* APUSYS SMC call */
+#define MTK_SIP_APUSYS_CONTROL_AARCH32	0x8200051E
+#define MTK_SIP_APUSYS_CONTROL_AARCH64	0xC200051E
+
 #endif /* PLAT_SIP_CALLS_H */
diff --git a/plat/mediatek/mt8195/include/platform_def.h b/plat/mediatek/mt8195/include/platform_def.h
index 68301d6..d4f2f83 100644
--- a/plat/mediatek/mt8195/include/platform_def.h
+++ b/plat/mediatek/mt8195/include/platform_def.h
@@ -21,6 +21,16 @@
 #define MTK_MCDI_SRAM_BASE	0x11B000
 #define MTK_MCDI_SRAM_MAP_SIZE	0x1000
 
+#define APUSYS_BASE			0x19000000
+#define APUSYS_SCTRL_REVISER_BASE	0x19021000
+#define APUSYS_SCTRL_REVISER_SIZE	0x1000
+#define APUSYS_APU_S_S_4_BASE		0x190F2000
+#define APUSYS_APU_S_S_4_SIZE		0x1000
+#define APUSYS_APU_PLL_BASE		0x190F3000
+#define APUSYS_APU_PLL_SIZE		0x1000
+#define APUSYS_APU_ACC_BASE		0x190F4000
+#define APUSYS_APU_ACC_SIZE		0x1000
+
 #define TOPCKGEN_BASE           (IO_PHYS + 0x00000000)
 #define INFRACFG_AO_BASE        (IO_PHYS + 0x00001000)
 #define SPM_BASE		(IO_PHYS + 0x00006000)
diff --git a/plat/mediatek/mt8195/plat_sip_calls.c b/plat/mediatek/mt8195/plat_sip_calls.c
index ddc7502..7d3c512 100644
--- a/plat/mediatek/mt8195/plat_sip_calls.c
+++ b/plat/mediatek/mt8195/plat_sip_calls.c
@@ -9,6 +9,7 @@
 #include <mt_dp.h>
 #include <mt_spm.h>
 #include <mt_spm_vcorefs.h>
+#include <mtk_apusys.h>
 #include <mtk_sip_svc.h>
 #include <plat_dfd.h>
 #include "plat_sip_calls.h"
@@ -41,6 +42,11 @@
 		ret = dfd_smc_dispatcher(x1, x2, x3, x4);
 		SMC_RET1(handle, ret);
 		break;
+	case MTK_SIP_APUSYS_CONTROL_AARCH32:
+	case MTK_SIP_APUSYS_CONTROL_AARCH64:
+		ret = apusys_kernel_ctrl(x1, x2, x3, x4, &ret_val);
+		SMC_RET2(handle, ret, ret_val);
+		break;
 	default:
 		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
 		break;
diff --git a/plat/mediatek/mt8195/platform.mk b/plat/mediatek/mt8195/platform.mk
index ef7ff81..72c71fe 100644
--- a/plat/mediatek/mt8195/platform.mk
+++ b/plat/mediatek/mt8195/platform.mk
@@ -14,6 +14,7 @@
                  -I${MTK_PLAT}/common/drivers/timer/              \
                  -I${MTK_PLAT}/common/drivers/uart/               \
                  -I${MTK_PLAT}/common/lpm/                        \
+                 -I${MTK_PLAT_SOC}/drivers/apusys/                \
                  -I${MTK_PLAT_SOC}/drivers/dcm                    \
                  -I${MTK_PLAT_SOC}/drivers/dfd                    \
                  -I${MTK_PLAT_SOC}/drivers/dp/                    \
@@ -59,6 +60,9 @@
                 ${MTK_PLAT_SOC}/aarch64/platform_common.c             \
                 ${MTK_PLAT_SOC}/aarch64/plat_helpers.S                \
                 ${MTK_PLAT_SOC}/bl31_plat_setup.c                     \
+                ${MTK_PLAT_SOC}/drivers/apusys/apupll.c               \
+                ${MTK_PLAT_SOC}/drivers/apusys/apupwr_clkctl.c        \
+		${MTK_PLAT_SOC}/drivers/apusys/mtk_apusys.c           \
                 ${MTK_PLAT_SOC}/drivers/dcm/mtk_dcm.c                 \
                 ${MTK_PLAT_SOC}/drivers/dcm/mtk_dcm_utils.c           \
                 ${MTK_PLAT_SOC}/drivers/dfd/plat_dfd.c                \