blob: 465054d76d0427ef08e64581466b662ab679a391 [file] [log] [blame]
/*
* 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);
}