/* * Copyright (c) 2022, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #define MT_LP_TZ_INFRA_REG(ofs) (INFRACFG_AO_BASE + ofs) #define MT_LP_TZ_MM_REG(ofs) (MMSYS_BASE + ofs) #define MT_LP_TZ_MDP_REG(ofs) (MDPSYS_BASE + ofs) #define MT_LP_TZ_SPM_REG(ofs) (SPM_BASE + ofs) #define MT_LP_TZ_TOPCK_REG(ofs) (TOPCKGEN_BASE + ofs) #define MT_LP_TZ_APMIXEDSYS(ofs) (APMIXEDSYS + ofs) #define SPM_PWR_STATUS MT_LP_TZ_SPM_REG(0x016C) #define SPM_PWR_STATUS_2ND MT_LP_TZ_SPM_REG(0x0170) #define INFRA_SW_CG0 MT_LP_TZ_INFRA_REG(0x0090) #define INFRA_SW_CG1 MT_LP_TZ_INFRA_REG(0x0094) #define INFRA_SW_CG2 MT_LP_TZ_INFRA_REG(0x00AC) #define INFRA_SW_CG3 MT_LP_TZ_INFRA_REG(0x00C8) #define INFRA_SW_CG4 MT_LP_TZ_INFRA_REG(0x00E8) #define INFRA_SW_CG5 MT_LP_TZ_INFRA_REG(0x00D8) #define MMSYS_CG_CON0 MT_LP_TZ_MM_REG(0x100) #define MMSYS_CG_CON1 MT_LP_TZ_MM_REG(0x110) #define MMSYS_CG_CON2 MT_LP_TZ_MM_REG(0x1A0) #define MMSYS_CG_CON3 MT_LP_TZ_MDP_REG(0x100) /* Check clkmux registers */ #define CLK_CFG(id) MT_LP_TZ_TOPCK_REG(0xe0 + id * 0x10) #define CLK_CHECK BIT(31) enum { CLKMUX_DISP = 0, CLKMUX_MDP = 1, CLKMUX_IMG1 = 2, CLKMUX_IMG2 = 3, NF_CLKMUX = 4, }; static bool is_clkmux_pdn(unsigned int clkmux_id) { unsigned int reg, val, idx; bool ret = false; if (clkmux_id & CLK_CHECK) { clkmux_id = (clkmux_id & ~CLK_CHECK); reg = clkmux_id / 4U; val = mmio_read_32(CLK_CFG(reg)); idx = clkmux_id % 4U; ret = (((val >> (idx * 8U)) & 0x80) != 0U); } return ret; } static struct mt_spm_cond_tables spm_cond_t; struct idle_cond_info { unsigned int subsys_mask; uintptr_t addr; bool bit_flip; unsigned int clkmux_id; }; #define IDLE_CG(mask, addr, bitflip, clkmux) \ {mask, (uintptr_t)addr, bitflip, clkmux} static struct idle_cond_info idle_cg_info[PLAT_SPM_COND_MAX] = { IDLE_CG(0xffffffff, SPM_PWR_STATUS, false, 0U), IDLE_CG(0x00000200, INFRA_SW_CG0, true, 0U), IDLE_CG(0x00000200, INFRA_SW_CG1, true, 0U), IDLE_CG(0x00000200, INFRA_SW_CG2, true, 0U), IDLE_CG(0x00000200, INFRA_SW_CG3, true, 0U), IDLE_CG(0x00000200, INFRA_SW_CG4, true, 0U), IDLE_CG(0x00000200, INFRA_SW_CG5, true, 0U), IDLE_CG(0x00200000, MMSYS_CG_CON0, true, (CLK_CHECK | CLKMUX_DISP)), IDLE_CG(0x00200000, MMSYS_CG_CON1, true, (CLK_CHECK | CLKMUX_DISP)), IDLE_CG(0x00200000, MMSYS_CG_CON2, true, (CLK_CHECK | CLKMUX_DISP)), IDLE_CG(0x00200000, MMSYS_CG_CON3, true, (CLK_CHECK | CLKMUX_MDP)), }; /* Check pll idle condition */ #define PLL_MFGPLL MT_LP_TZ_APMIXEDSYS(0x314) #define PLL_MMPLL MT_LP_TZ_APMIXEDSYS(0x254) #define PLL_UNIVPLL MT_LP_TZ_APMIXEDSYS(0x324) #define PLL_MSDCPLL MT_LP_TZ_APMIXEDSYS(0x38c) #define PLL_TVDPLL MT_LP_TZ_APMIXEDSYS(0x264) unsigned int mt_spm_cond_check(int state_id, const struct mt_spm_cond_tables *src, const struct mt_spm_cond_tables *dest, struct mt_spm_cond_tables *res) { unsigned int blocked = 0U; unsigned int i; bool is_system_suspend = IS_PLAT_SUSPEND_ID(state_id); if ((src == NULL) || (dest == NULL)) { blocked = SPM_COND_CHECK_FAIL; } else { for (i = 0U; i < PLAT_SPM_COND_MAX; i++) { if (res != NULL) { res->table_cg[i] = (src->table_cg[i] & dest->table_cg[i]); if (is_system_suspend && ((res->table_cg[i]) != 0U)) { INFO("suspend: %s block[%u](0x%lx) = 0x%08x\n", dest->name, i, idle_cg_info[i].addr, res->table_cg[i]); } if ((res->table_cg[i]) != 0U) { blocked |= BIT(i); } } else if ((src->table_cg[i] & dest->table_cg[i]) != 0U) { blocked |= BIT(i); break; } } if (res != NULL) { res->table_pll = (src->table_pll & dest->table_pll); if (res->table_pll != 0U) { blocked |= (res->table_pll << SPM_COND_BLOCKED_PLL_IDX) | SPM_COND_CHECK_BLOCKED_PLL; } } else if ((src->table_pll & dest->table_pll) != 0U) { blocked |= SPM_COND_CHECK_BLOCKED_PLL; } if (is_system_suspend && ((blocked) != 0U)) { INFO("suspend: %s total blocked = 0x%08x\n", dest->name, blocked); } } return blocked; } #define IS_MT_SPM_PWR_OFF(mask) \ (((mmio_read_32(SPM_PWR_STATUS) & mask) == 0U) && \ ((mmio_read_32(SPM_PWR_STATUS_2ND) & mask) == 0U)) int mt_spm_cond_update(struct mt_resource_constraint **con, int stateid, void *priv) { int res; uint32_t i; struct mt_resource_constraint *const *rc; /* read all cg state */ for (i = 0U; i < PLAT_SPM_COND_MAX; i++) { spm_cond_t.table_cg[i] = 0U; /* check mtcmos, if off set idle_value and clk to 0 disable */ if (IS_MT_SPM_PWR_OFF(idle_cg_info[i].subsys_mask)) { continue; } /* check clkmux */ if (is_clkmux_pdn(idle_cg_info[i].clkmux_id)) { continue; } spm_cond_t.table_cg[i] = idle_cg_info[i].bit_flip ? ~mmio_read_32(idle_cg_info[i].addr) : mmio_read_32(idle_cg_info[i].addr); } spm_cond_t.table_pll = 0U; if ((mmio_read_32(PLL_MFGPLL) & 0x1) != 0U) { spm_cond_t.table_pll |= PLL_BIT_MFGPLL; } if ((mmio_read_32(PLL_MMPLL) & 0x1) != 0U) { spm_cond_t.table_pll |= PLL_BIT_MMPLL; } if ((mmio_read_32(PLL_UNIVPLL) & 0x1) != 0U) { spm_cond_t.table_pll |= PLL_BIT_UNIVPLL; } if ((mmio_read_32(PLL_MSDCPLL) & 0x1) != 0U) { spm_cond_t.table_pll |= PLL_BIT_MSDCPLL; } if ((mmio_read_32(PLL_TVDPLL) & 0x1) != 0U) { spm_cond_t.table_pll |= PLL_BIT_TVDPLL; } spm_cond_t.priv = priv; for (rc = con; *rc != NULL; rc++) { if (((*rc)->update) == NULL) { continue; } res = (*rc)->update(stateid, PLAT_RC_UPDATE_CONDITION, (void const *)&spm_cond_t); if (res != MT_RM_STATUS_OK) { break; } } return 0; }