diff options
Diffstat (limited to 'plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.c')
-rw-r--r-- | plat/mediatek/mt8195/drivers/apusys/apupwr_clkctl.c | 341 |
1 files changed, 341 insertions, 0 deletions
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); +} |