feat(plat/mediatek/apu): add mt8195 APU clock and pll SiP call

The clock and pll of mt8195 can be locked into security access
by device apc. Add clock and pll related SiP call for the access
from Kernel space.

Change-Id: I0c1f7d6c6abdd3b976492a0b776dc5b1d1f1512b
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);
+}