From 22fe08c04b4ccd6d3d96b0856620760e67dd92e8 Mon Sep 17 00:00:00 2001 From: Vince Liu Date: Wed, 27 Aug 2025 14:34:44 +0800 Subject: [PATCH] soc/mediatek/mt8189: Implement UFS power-off API for non-UFS SKUs On MT8189, UFS power is enabled by default. For SKUs that do not use UFS as storage, keeping UFS power enabled can cause suspend failures and unnecessary power consumption. This change implements a UFS power-off API to ensure UFS can be properly powered down on non-UFS SKUs. BUG=b:430421429 BRANCH=skywalker TEST=Suspend flow works correctly, and SoC power consumption is 34 mW, meeting expectations on Anakin. Signed-off-by: Vince Liu Signed-off-by: Irving-ch Lin Change-Id: Ib5ccbeaf951c3a095905e472bc096eeb2dee47a8 Reviewed-on: https://review.coreboot.org/c/coreboot/+/88976 Reviewed-by: Yidi Lin Tested-by: build bot (Jenkins) Reviewed-by: Yu-Ping Wu --- src/soc/mediatek/common/include/soc/mtcmos.h | 4 ++ src/soc/mediatek/common/mtcmos.c | 71 +++++++++++++++++++ .../mediatek/mt8189/include/soc/spm_mtcmos.h | 1 + src/soc/mediatek/mt8189/mtcmos.c | 63 ++++++++++++---- 4 files changed, 127 insertions(+), 12 deletions(-) diff --git a/src/soc/mediatek/common/include/soc/mtcmos.h b/src/soc/mediatek/common/include/soc/mtcmos.h index 15b4367fc4..a38d3f7ac2 100644 --- a/src/soc/mediatek/common/include/soc/mtcmos.h +++ b/src/soc/mediatek/common/include/soc/mtcmos.h @@ -5,6 +5,8 @@ struct bus_protect { void *clr_addr; + void *set_addr; + void *rdy_addr; u32 mask; }; @@ -28,9 +30,11 @@ struct power_domain_data { void mtcmos_set_scpd_ext_buck_iso(const struct power_domain_data *pd); void mtcmos_power_on(const struct power_domain_data *pd); +void mtcmos_power_off(const struct power_domain_data *pd); void mtcmos_adsp_power_on(void); void mtcmos_audio_power_on(void); void mtcmos_display_power_on(void); +void mtcmos_ufs_power_off(void); void mtcmos_protect_adsp_bus(void); void mtcmos_protect_audio_bus(void); diff --git a/src/soc/mediatek/common/mtcmos.c b/src/soc/mediatek/common/mtcmos.c index f69e84fab7..0172e24981 100644 --- a/src/soc/mediatek/common/mtcmos.c +++ b/src/soc/mediatek/common/mtcmos.c @@ -1,8 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +#include +#include #include #include #include +#include + +#define MTCMOS_TIMEOUT_US 500 +#define MTCMOS_ETIMEDOUT 25 enum { SRAM_ISOINT_B = 1U << 6, @@ -19,6 +25,20 @@ __weak void mtcmos_set_scpd_ext_buck_iso(const struct power_domain_data *pd) /* do nothing */ } +static int mtcmos_wait_for_state(u32 *reg, u32 mask, bool is_set) +{ + u32 expect = is_set ? mask : 0; + + if (!wait_us(MTCMOS_TIMEOUT_US, ((read32(reg) & mask) == expect))) { + printk(BIOS_ERR, + "%s (0x%p, %#x, %d) timeout after %d us, reg_val=%#x\n", + __func__, reg, mask, is_set, MTCMOS_TIMEOUT_US, read32(reg)); + return -MTCMOS_ETIMEDOUT; + } + + return 0; +} + static void release_bus_protection(const struct power_domain_data *pd) { int i; @@ -27,6 +47,19 @@ static void release_bus_protection(const struct power_domain_data *pd) write32(pd->bp_table[i].clr_addr, pd->bp_table[i].mask); } +static void set_bus_protection(const struct power_domain_data *pd) +{ + int i; + + for (i = pd->bp_steps - 1; i >= 0; i--) { + const struct bus_protect *bp = &pd->bp_table[i]; + assert(bp->set_addr && bp->rdy_addr); + write32(bp->set_addr, bp->mask); + mtcmos_wait_for_state(bp->rdy_addr, bp->mask, true); + } +} + + void mtcmos_power_on(const struct power_domain_data *pd) { u32 *pwr_status; @@ -70,6 +103,44 @@ void mtcmos_power_on(const struct power_domain_data *pd) release_bus_protection(pd); } +void mtcmos_power_off(const struct power_domain_data *pd) +{ + u32 *pwr_status; + u32 *pwr_status_2nd; + + write32(&mtk_spm->poweron_config_set, + (SPM_PROJECT_CODE << 16) | (1U << 0)); + + set_bus_protection(pd); + + if (pd->sram_pdn_mask) { + if (pd->caps & SCPD_SRAM_ISO) { + setbits32(pd->pwr_con, SRAM_CKISO); + clrbits32(pd->pwr_con, SRAM_ISOINT_B); + udelay(1); + } + setbits32(pd->pwr_con, pd->sram_pdn_mask); + mtcmos_wait_for_state(pd->pwr_con, pd->sram_ack_mask, true); + } + + setbits32(pd->pwr_con, PWR_ISO); + setbits32(pd->pwr_con, PWR_CLK_DIS); + clrbits32(pd->pwr_con, PWR_RST_B); + + if (pd->pwr_status && pd->pwr_status_2nd) { + pwr_status = pd->pwr_status; + pwr_status_2nd = pd->pwr_status_2nd; + } else { + pwr_status = &mtk_spm->pwr_status; + pwr_status_2nd = &mtk_spm->pwr_status_2nd; + } + + clrbits32(pd->pwr_con, PWR_ON); + mtcmos_wait_for_state(pwr_status, pd->pwr_sta_mask, false); + clrbits32(pd->pwr_con, PWR_ON_2ND); + mtcmos_wait_for_state(pwr_status_2nd, pd->pwr_sta_mask, false); +} + void mtcmos_display_power_on(void) { int i; diff --git a/src/soc/mediatek/mt8189/include/soc/spm_mtcmos.h b/src/soc/mediatek/mt8189/include/soc/spm_mtcmos.h index e68b531bb9..9c9b6d8afc 100644 --- a/src/soc/mediatek/mt8189/include/soc/spm_mtcmos.h +++ b/src/soc/mediatek/mt8189/include/soc/spm_mtcmos.h @@ -24,6 +24,7 @@ struct mtk_vlpcfg_regs { u32 bus_vlp_topaxi_protecten; u32 bus_vlp_topaxi_protecten_set; u32 bus_vlp_topaxi_protecten_clr; + u32 bus_vlp_topaxi_protecten_sta0; u32 bus_vlp_topaxi_protecten_sta1; }; check_member(mtk_vlpcfg_regs, vlp_test_ck_ctrl, 0x0004); diff --git a/src/soc/mediatek/mt8189/mtcmos.c b/src/soc/mediatek/mt8189/mtcmos.c index 8b89e58ad2..b212b72a59 100644 --- a/src/soc/mediatek/mt8189/mtcmos.c +++ b/src/soc/mediatek/mt8189/mtcmos.c @@ -11,24 +11,55 @@ #include static const struct bus_protect bp_ufs[] = { - {&mtk_vlpcfg->bus_vlp_topaxi_protecten_clr, BIT(6)}, - {&mtk_infracfg_ao->perisys_protect.clr, BIT(4)}, - {&mtk_vlpcfg->bus_vlp_topaxi_protecten_clr, BIT(5)}, + { + .clr_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_clr, + .set_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_set, + .rdy_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_sta1, + .mask = BIT(6), + }, + { + .clr_addr = &mtk_infracfg_ao->perisys_protect.clr, + .set_addr = &mtk_infracfg_ao->perisys_protect.set, + .rdy_addr = &mtk_infracfg_ao->perisys_protect.ready, + .mask = BIT(4), + }, + { + .clr_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_clr, + .set_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_set, + .rdy_addr = &mtk_vlpcfg->bus_vlp_topaxi_protecten_sta1, + .mask = BIT(5), + }, }; static const struct bus_protect bp_mminfra[] = { - {&mtk_infracfg_ao->emisys_protect.clr, BIT(20) | BIT(21)}, - {&mtk_infracfg_ao->infrasys_protect[0].clr, BIT(16)}, - {&mtk_infracfg_ao->mmsys_protect[1].clr, - BIT(0) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | - BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15)}, - {&mtk_infracfg_ao->infrasys_protect[1].clr, BIT(11)}, - {&mtk_infracfg_ao->mmsys_protect[1].clr, - BIT(1) | BIT(2) | BIT(3)}, + { + .clr_addr = &mtk_infracfg_ao->emisys_protect.clr, + .mask = BIT(20) | BIT(21), + }, + { + .clr_addr = &mtk_infracfg_ao->infrasys_protect[0].clr, + .mask = BIT(16), + }, + { + .clr_addr = &mtk_infracfg_ao->mmsys_protect[1].clr, + .mask = BIT(0) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | + BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15), + }, + { + .clr_addr = &mtk_infracfg_ao->infrasys_protect[1].clr, + .mask = BIT(11), + }, + { + .clr_addr = &mtk_infracfg_ao->mmsys_protect[1].clr, + .mask = BIT(1) | BIT(2) | BIT(3), + }, }; static const struct bus_protect bp_ssusb[] = { - {&mtk_infracfg_ao->perisys_protect.clr, BIT(7)}, + { + .clr_addr = &mtk_infracfg_ao->perisys_protect.clr, + .mask = BIT(7), + }, }; static const struct power_domain_data pd_plat[] = { @@ -81,6 +112,14 @@ void mtcmos_init(void) mtcmos_power_on(&pd_plat[i]); } +void mtcmos_ufs_power_off(void) +{ + /* ufs0_phy */ + mtcmos_power_off(&pd_plat[1]); + /* ufs0 */ + mtcmos_power_off(&pd_plat[0]); +} + void mtcmos_protect_audio_bus(void) { write32(&mtk_infracfg_ao->perisys_protect.clr, BIT(6));