arm-trusted-firmware/plat/rockchip/rk3576/drivers/pmu/pmu.c
XiaoDong Huang 036935a814 feat(rk3576): support rk3576
rk3576 is an Octa-core soc with Cortex-a53/a72 inside.
This patch supports the following functions:
1. basic platform setup
2. power up/off cpus
3. suspend/resume cpus
4. suspend/resume system
5. reset system
6. power off system

Change-Id: I67a019822bd4af13e4a3cdd09cf06202f4922cc4
Signed-off-by: XiaoDong Huang <derrick.huang@rock-chips.com>
2025-02-24 15:07:43 +08:00

1069 lines
30 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2025, Rockchip Electronics Co., Ltd.
*/
#include <assert.h>
#include <errno.h>
#include <arch_helpers.h>
#include <bl31/bl31.h>
#include <common/debug.h>
#include <drivers/console.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>
#include <platform_def.h>
#include <pmu.h>
#include <cpus_on_fixed_addr.h>
#include <dmc_rk3576.h>
#include <plat_pm_helpers.h>
#include <plat_private.h>
#include <pm_pd_regs.h>
#include <secure.h>
#include <soc.h>
static struct psram_data_t *psram_sleep_cfg =
(struct psram_data_t *)&sys_sleep_flag_sram;
struct rk3576_sleep_ddr_data {
uint32_t cru_mode_con, secure_cru_mode;
uint32_t gpio0a_iomux_l, gpio0a_iomux_h, gpio0b_iomux_l;
uint32_t pmu2_bisr_glb_con;
uint32_t pmu2_c0_ack_sel_con0, pmu2_c1_ack_sel_con0, pmu2_c2_ack_sel_con0;
uint32_t pmu2_fast_pwr_con, pmu2_pwrgt_sft_con0;
uint32_t pmu0grf_soc_con0, pmu0grf_soc_con1, pmu0grf_soc_con5;
uint32_t pmu_pd_st, bus_idle_st;
uint32_t sys_sgrf_soc_con0;
uint32_t ddrgrf_cha_con2, ddrgrf_chb_con2;
};
static struct rk3576_sleep_ddr_data ddr_data;
void rockchip_plat_mmu_el3(void)
{
#ifdef PLAT_EXTRA_LD_SCRIPT
size_t sram_size;
sram_size = (char *)&__bl31_pmusram_text_end -
(char *)PMUSRAM_BASE;
mmap_add_region(PMUSRAM_BASE, PMUSRAM_BASE,
sram_size, MT_MEMORY | MT_RO | MT_SECURE);
sram_size = (char *)&__bl31_pmusram_data_end -
(char *)&__bl31_pmusram_data_start;
mmap_add_region((unsigned long)&__bl31_pmusram_data_start,
(unsigned long)&__bl31_pmusram_data_start,
sram_size, MT_DEVICE | MT_RW | MT_SECURE);
#endif
}
static int check_cpu_wfie(uint32_t cpu)
{
uint32_t loop = 0;
while ((mmio_read_32(SYS_GRF_BASE + SYSGRF_STATUS0) & BIT(cpu + 12)) == 0 &&
(mmio_read_32(PMU_BASE + PMU2_CLUSTER_PWR_ST) & BIT(cpu)) == 0 &&
(loop < WFEI_CHECK_LOOP)) {
udelay(1);
loop++;
}
if (loop >= WFEI_CHECK_LOOP) {
WARN("%s: error, cpu%d (0x%x 0x%x)!\n", __func__, cpu,
mmio_read_32(SYS_GRF_BASE + SYSGRF_STATUS0),
mmio_read_32(PMU_BASE + PMU2_CLUSTER_PWR_ST));
return -EINVAL;
}
return 0;
}
static inline uint32_t cpu_power_domain_st(uint32_t cpu)
{
return !!(mmio_read_32(PMU_BASE + PMU2_CLUSTER_PWR_ST) &
BIT(cpu + 16));
}
static int cpu_power_domain_ctr(uint32_t cpu, uint32_t pd_state)
{
uint32_t loop = 0;
int ret = 0;
mmio_write_32(PMU_BASE + PMU2_CPU_PWR_SFTCON(cpu),
BITS_WITH_WMASK(pd_state, 0x1, 0));
dsb();
while ((cpu_power_domain_st(cpu) != pd_state) && (loop < PD_CTR_LOOP)) {
udelay(1);
loop++;
}
if (cpu_power_domain_st(cpu) != pd_state) {
WARN("%s: %d, %d, error!\n", __func__, cpu, pd_state);
ret = -EINVAL;
}
return ret;
}
static inline uint32_t get_cpus_pwr_domain_cfg_info(uint32_t cpu_id)
{
uint32_t val;
if ((mmio_read_32(PMU_BASE + PMU2_CPU_PWR_SFTCON(cpu_id)) & BIT(0)) != 0)
return core_pwr_pd;
val = mmio_read_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id));
if ((val & BIT(pmu_cpu_pm_en)) != 0) {
if ((val & BIT(core_pwr_wfi_int)) != 0)
return core_pwr_wfi_int;
else if ((val & BIT(pmu_cpu_pm_sft_wakeup_en)) != 0)
return core_pwr_wfi_reset;
else
return core_pwr_wfi;
} else {
return -1;
}
}
static inline void set_cpus_pwr_domain_cfg_info(uint32_t cpu_id, uint32_t value)
{
}
static int cpus_power_domain_on(uint32_t cpu_id)
{
uint32_t cfg_info;
/*
* There are two ways to powering on or off on core.
* 1) Control it power domain into on or off in PMU_PWRDN_CON reg
* 2) Enable the core power manage in PMU_CORE_PM_CON reg,
* then, if the core enter into wfi, it power domain will be
* powered off automatically.
*/
cfg_info = get_cpus_pwr_domain_cfg_info(cpu_id);
if (cfg_info == core_pwr_pd) {
/* disable core_pm cfg */
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(0, 0xf, 0));
/* if the cores have be on, power off it firstly */
if (cpu_power_domain_st(cpu_id) == pmu_pd_on)
cpu_power_domain_ctr(cpu_id, pmu_pd_off);
cpu_power_domain_ctr(cpu_id, pmu_pd_on);
} else {
if (cpu_power_domain_st(cpu_id) == pmu_pd_on) {
WARN("%s: cpu%d is not in off,!\n", __func__, cpu_id);
return -EINVAL;
}
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(1, 0x1, pmu_cpu_pm_sft_wakeup_en));
dsb();
}
return 0;
}
static int cpus_power_domain_off(uint32_t cpu_id, uint32_t pd_cfg)
{
uint32_t core_pm_value;
if (cpu_power_domain_st(cpu_id) == pmu_pd_off)
return 0;
if (pd_cfg == core_pwr_pd) {
if (check_cpu_wfie(cpu_id))
return -EINVAL;
/* disable core_pm cfg */
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(0, 0xf, 0));
set_cpus_pwr_domain_cfg_info(cpu_id, pd_cfg);
cpu_power_domain_ctr(cpu_id, pmu_pd_off);
} else {
set_cpus_pwr_domain_cfg_info(cpu_id, pd_cfg);
core_pm_value = BIT(pmu_cpu_pm_en) | BIT(pmu_cpu_pm_dis_int);
if (pd_cfg == core_pwr_wfi_int)
core_pm_value |= BIT(pmu_cpu_pm_int_wakeup_en);
else if (pd_cfg == core_pwr_wfi_reset)
core_pm_value |= BIT(pmu_cpu_pm_sft_wakeup_en);
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(core_pm_value, 0xf, 0));
dsb();
}
return 0;
}
int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint)
{
uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
assert(cpu_id < PLATFORM_CORE_COUNT);
assert(cpuson_flags[cpu_id] == 0);
cpuson_flags[cpu_id] = PMU_CPU_HOTPLUG;
cpuson_entry_point[cpu_id] = entrypoint;
dsb();
cpus_power_domain_on(cpu_id);
return PSCI_E_SUCCESS;
}
int rockchip_soc_cores_pwr_dm_off(void)
{
uint32_t cpu_id = plat_my_core_pos();
cpus_power_domain_off(cpu_id, core_pwr_wfi);
return PSCI_E_SUCCESS;
}
int rockchip_soc_cores_pwr_dm_on_finish(void)
{
uint32_t cpu_id = plat_my_core_pos();
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(0, 0xf, 0));
return PSCI_E_SUCCESS;
}
int rockchip_soc_cores_pwr_dm_suspend(void)
{
uint32_t cpu_id = plat_my_core_pos();
assert(cpu_id < PLATFORM_CORE_COUNT);
assert(cpuson_flags[cpu_id] == 0);
cpuson_flags[cpu_id] = PMU_CPU_AUTO_PWRDN;
cpuson_entry_point[cpu_id] = plat_get_sec_entrypoint();
dsb();
cpus_power_domain_off(cpu_id, core_pwr_wfi_int);
return PSCI_E_SUCCESS;
}
int rockchip_soc_cores_pwr_dm_resume(void)
{
uint32_t cpu_id = plat_my_core_pos();
/* Disable core_pm */
mmio_write_32(PMU_BASE + PMU2_CPU_AUTO_PWR_CON(cpu_id),
BITS_WITH_WMASK(0, 0xf, 0));
return PSCI_E_SUCCESS;
}
void nonboot_cpus_off(void)
{
uint32_t boot_cpu, cpu;
boot_cpu = plat_my_core_pos();
/* turn off noboot cpus */
for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) {
if (cpu == boot_cpu)
continue;
cpus_power_domain_off(cpu, core_pwr_pd);
}
}
static __pmusramfunc void ddr_resume(void)
{
uint32_t key_upd_msk =
mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST) & BIT(pmu_pd_vop) ? 0x3 : 0x7;
/* hptimer is 24M here */
write_cntfrq_el0(24000000);
/* release cpu1~cpu7 to make pmu1_fsm exit */
mmio_write_32(CCI_GRF_BASE + CCIGRF_CON(4), 0xffffffff);
mmio_write_32(LITCORE_GRF_BASE + COREGRF_CPU_CON(1),
BITS_WITH_WMASK(0x77, 0xff, 4));
/* wait pmu1 fsm over */
while ((mmio_read_32(PMU_BASE + PMU1_PWR_FSM) & 0xf) != 0)
;
/* SOC_CON19.vop/center/cci_ddr_hash_key_update_en */
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(19), 0x00070000);
dsb();
/* SOC_CON19.vop/center/cci_ddr_hash_key_update_en */
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(19), 0x00070000 | key_upd_msk);
/* SOC_STATUS.center/cci_ddr_hash_key_shift_ready */
while (((mmio_read_32(SYS_SGRF_BASE + SYSSGRF_SOC_STATUS) >> 12) & key_upd_msk) != key_upd_msk)
;
/* CCI_BASE.ctrl_override_reg Attr:W1C addrmap strobe */
mmio_setbits_32(CCI_BASE + 0x0, 0x1 << 29);
/* SOC_CON19.vop/center/cci_ddr_hash_key_auto_update_en */
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(19), 0x00700070);
}
static uint32_t clk_save[CRU_CLKGATE_CON_CNT + PHP_CRU_CLKGATE_CON_CNT +
SECURE_CRU_CLKGATE_CON_CNT + SECURE_SCRU_CLKGATE_CON_CNT +
PMU1CRU_CLKGATE_CON_CNT + PMU1SCRU_CLKGATE_CON_CNT];
void clk_gate_con_disable(void)
{
int i;
for (i = 0; i < CRU_CLKGATE_CON_CNT; i++) {
/* Don't open wdt0 clk (cru_gate16[7:8] */
if (i == 16) {
mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(i),
0xfe7f0000);
} else {
mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(i),
0xffff0000);
}
}
for (i = 0; i < PHP_CRU_CLKGATE_CON_CNT; i++)
mmio_write_32(PHP_CRU_BASE + PHP_CRU_CLKGATE_CON(i), 0xffff0000);
for (i = 0; i < SECURE_CRU_CLKGATE_CON_CNT; i++)
mmio_write_32(SECURE_CRU_BASE + SECURE_CRU_CLKGATE_CON(i), 0xffff0000);
for (i = 0; i < SECURE_SCRU_CLKGATE_CON_CNT; i++)
mmio_write_32(SECURE_CRU_BASE + SECURE_SCRU_CLKGATE_CON(i), 0xffff0000);
for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++)
mmio_write_32(PMU1_CRU_BASE + PMU1CRU_CLKGATE_CON(i), 0xffff0000);
for (i = 0; i < PMU1SCRU_CLKGATE_CON_CNT; i++)
mmio_write_32(PMU1_CRU_BASE + PMU1SCRU_CLKGATE_CON(i), 0xffff0000);
}
void clk_gate_con_save(void)
{
int i, j = 0;
for (i = 0; i < CRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(i));
for (i = 0; i < PHP_CRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(PHP_CRU_BASE + PHP_CRU_CLKGATE_CON(i));
for (i = 0; i < SECURE_CRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(SECURE_CRU_BASE + SECURE_CRU_CLKGATE_CON(i));
for (i = 0; i < SECURE_SCRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(SECURE_CRU_BASE + SECURE_SCRU_CLKGATE_CON(i));
for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(PMU1_CRU_BASE + PMU1CRU_CLKGATE_CON(i));
for (i = 0; i < PMU1SCRU_CLKGATE_CON_CNT; i++, j++)
clk_save[j] = mmio_read_32(PMU1_CRU_BASE + PMU1SCRU_CLKGATE_CON(i));
}
void clk_gate_con_restore(void)
{
int i, j = 0;
for (i = 0; i < CRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
for (i = 0; i < PHP_CRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(PHP_CRU_BASE + PHP_CRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
for (i = 0; i < SECURE_CRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(SECURE_CRU_BASE + SECURE_CRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
for (i = 0; i < SECURE_SCRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(SECURE_CRU_BASE + SECURE_SCRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
for (i = 0; i < PMU1CRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(PMU1_CRU_BASE + PMU1CRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
for (i = 0; i < PMU1SCRU_CLKGATE_CON_CNT; i++, j++)
mmio_write_32(PMU1_CRU_BASE + PMU1SCRU_CLKGATE_CON(i),
WITH_16BITS_WMSK(clk_save[j]));
}
void pmu_bus_idle_req(uint32_t bus, uint32_t state)
{
uint32_t wait_cnt = 0;
mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_SFTCON(bus / 16),
BITS_WITH_WMASK(state, 0x1, bus % 16));
while (pmu_bus_idle_st(bus) != state ||
pmu_bus_idle_ack(bus) != state) {
if (++wait_cnt > BUS_IDLE_LOOP)
break;
udelay(1);
}
if (wait_cnt > BUS_IDLE_LOOP)
WARN("%s: can't wait state %d for bus %d (0x%x)\n",
__func__, state, bus,
mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST));
}
static inline uint32_t pmu_power_domain_st(uint32_t pd)
{
return mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST) & BIT(pd) ?
pmu_pd_off :
pmu_pd_on;
}
int pmu_power_domain_ctr(uint32_t pd, uint32_t pd_state)
{
uint32_t loop = 0;
int ret = 0;
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_SFTCON(pd / 16),
BITS_WITH_WMASK(pd_state, 0x1, pd % 16));
dsb();
while ((pmu_power_domain_st(pd) != pd_state) && (loop < PD_CTR_LOOP)) {
udelay(1);
loop++;
}
if (pmu_power_domain_st(pd) != pd_state) {
WARN("%s: %d, %d, (0x%x) error!\n", __func__, pd, pd_state,
mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST));
ret = -EINVAL;
}
return ret;
}
static int pmu_set_power_domain(uint32_t pd_id, uint32_t pd_state)
{
uint32_t state;
if (pmu_power_domain_st(pd_id) == pd_state)
goto out;
if (pd_state == pmu_pd_on)
pmu_power_domain_ctr(pd_id, pd_state);
state = (pd_state == pmu_pd_off) ? pmu_bus_idle : pmu_bus_active;
switch (pd_id) {
case pmu_pd_npu:
pmu_bus_idle_req(pmu_bus_id_npusys, state);
break;
case pmu_pd_secure:
pmu_bus_idle_req(pmu_bus_id_secure, state);
break;
case pmu_pd_nvm:
pmu_bus_idle_req(pmu_bus_id_nvm, state);
break;
case pmu_pd_sd_gmac:
pmu_bus_idle_req(pmu_bus_id_gmac, state);
break;
case pmu_pd_audio:
pmu_bus_idle_req(pmu_bus_id_audio, state);
break;
case pmu_pd_php:
pmu_bus_idle_req(pmu_bus_id_php, state);
break;
case pmu_pd_subphp:
break;
case pmu_pd_vop:
pmu_bus_idle_req(pmu_bus_id_vop, state);
break;
case pmu_pd_vop_smart:
break;
case pmu_pd_vop_clst:
break;
case pmu_pd_vo1:
pmu_bus_idle_req(pmu_bus_id_vo1, state);
break;
case pmu_pd_vo0:
pmu_bus_idle_req(pmu_bus_id_vo0, state);
break;
case pmu_pd_usb:
pmu_bus_idle_req(pmu_bus_id_usb, state);
break;
case pmu_pd_vi:
pmu_bus_idle_req(pmu_bus_id_vi, state);
break;
case pmu_pd_vepu0:
pmu_bus_idle_req(pmu_bus_id_vepu0, state);
break;
case pmu_pd_vepu1:
pmu_bus_idle_req(pmu_bus_id_vepu1, state);
break;
case pmu_pd_vdec:
pmu_bus_idle_req(pmu_bus_id_vdec, state);
break;
case pmu_pd_vpu:
pmu_bus_idle_req(pmu_bus_id_vpu, state);
break;
case pmu_pd_nputop:
pmu_bus_idle_req(pmu_bus_id_nputop, state);
break;
case pmu_pd_npu0:
pmu_bus_idle_req(pmu_bus_id_npu0, state);
break;
case pmu_pd_npu1:
pmu_bus_idle_req(pmu_bus_id_npu1, state);
break;
case pmu_pd_gpu:
pmu_bus_idle_req(pmu_bus_id_gpu, state);
break;
default:
break;
}
if (pd_state == pmu_pd_off)
pmu_power_domain_ctr(pd_id, pd_state);
out:
return 0;
}
static void pmu_power_domains_suspend(void)
{
ddr_data.pmu_pd_st = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST);
ddr_data.bus_idle_st = mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST);
ddr_data.pmu2_pwrgt_sft_con0 = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_SFTCON(0));
qos_save();
pd_usb2phy_save();
if ((ddr_data.pmu_pd_st & BIT(pmu_pd_php)) == 0)
pd_php_save();
}
static void pmu_power_domains_resume(void)
{
int i;
for (i = 0; i < pmu_pd_id_max; i++) {
/* vop smart/clst pd is not controlled by pmu */
if (i == pmu_pd_vop_smart || i == pmu_pd_vop_clst)
continue;
pmu_set_power_domain(i, !!(ddr_data.pmu_pd_st & BIT(i)));
}
/* restore vop smart/clst pd of pmu2_pwrgt_sft_con0 */
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_SFTCON(0),
0x30000000 | ddr_data.pmu2_pwrgt_sft_con0);
for (i = pmu_bus_id_max - 1; i >= 0; i--)
pmu_bus_idle_req(i, !!(ddr_data.bus_idle_st & BIT(i)));
if ((ddr_data.pmu_pd_st & BIT(pmu_pd_php)) == 0)
pd_php_restore();
pd_usb2phy_restore();
qos_restore();
}
static void ddr_sleep_config(void)
{
ddr_data.ddrgrf_cha_con2 =
mmio_read_32(DDR_GRF_BASE + DDRGRF_CHA_CON(2));
ddr_data.ddrgrf_chb_con2 =
mmio_read_32(DDR_GRF_BASE + DDRGRF_CHB_CON(2));
mmio_write_32(DDR_GRF_BASE + DDRGRF_CHA_CON(2), 0x0a000a00);
mmio_write_32(DDR_GRF_BASE + DDRGRF_CHB_CON(2), 0x0a000a00);
}
static void ddr_sleep_config_restore(void)
{
mmio_write_32(DDR_GRF_BASE + DDRGRF_CHA_CON(2),
WITH_16BITS_WMSK(ddr_data.ddrgrf_cha_con2));
mmio_write_32(DDR_GRF_BASE + DDRGRF_CHB_CON(2),
WITH_16BITS_WMSK(ddr_data.ddrgrf_chb_con2));
}
static void sleep_pin_config(void)
{
/* pwr0 sleep: gpio0_a3 */
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(1),
BITS_WITH_WMASK(0x7, 0xf, 0));
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(0),
BITS_WITH_WMASK(0, 0x1, 7));
mmio_write_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_L,
BITS_WITH_WMASK(9, 0xfu, 12));
}
static void pmu_sleep_config(void)
{
uint32_t pmu1_wkup_int_con;
uint32_t pmu1_pwr_con, pmu1_ddr_pwr_con, pmu1cru_pwr_con, pmu1_pll_pd_con;
uint32_t pmu2_bus_idle_con[2], pmu2_pwr_gt_con[2];
uint32_t key_upd_msk = ddr_data.pmu_pd_st & BIT(pmu_pd_vop) ? 0x3 : 0x7;
uint32_t fw_lkp_upd_msk = ddr_data.pmu_pd_st & BIT(pmu_pd_npu) ? 0x3 : 0x7;
uint32_t fw_ddr_upd_msk = key_upd_msk;
uint32_t pmu_pd_st = mmio_read_32(PMU_BASE + PMU2_PWR_GATE_ST);
uint32_t bus_idle_st = mmio_read_32(PMU_BASE + PMU2_BUS_IDLE_ST);
ddr_data.pmu2_bisr_glb_con = mmio_read_32(PMU_BASE + PMU2_BISR_GLB_CON);
ddr_data.pmu2_fast_pwr_con =
mmio_read_32(PMU_BASE + PMU2_FAST_POWER_CON);
ddr_data.pmu2_c0_ack_sel_con0 =
mmio_read_32(PMU_BASE + PMU2_C0_PWRACK_BYPASS_CON(0));
ddr_data.pmu2_c1_ack_sel_con0 =
mmio_read_32(PMU_BASE + PMU2_C1_PWRACK_BYPASS_CON(0));
ddr_data.pmu2_c2_ack_sel_con0 =
mmio_read_32(PMU_BASE + PMU2_C2_PWRACK_BYPASS_CON(0));
ddr_data.pmu0grf_soc_con5 =
mmio_read_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(5));
/* set tsadc_shut_m0 pin iomux to gpio */
mmio_write_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_L,
BITS_WITH_WMASK(0, 0xf, 4));
pmu1_wkup_int_con =
BIT(pmu_wkup_cpu0_int) |
BIT(pmu_wkup_gpio0_int);
pmu1_pwr_con =
BIT(pmu_powermode_en) |
/* BIT(pmu_scu0_byp) | */
/* BIT(pmu_scu1_byp) | */
/* BIT(pmu_cci_byp) | */
/* BIT(pmu_bus_byp) | */
/* BIT(pmu_ddr_byp) | */
/* BIT(pmu_pwrgt_byp) | */
/* BIT(pmu_cru_byp) | */
BIT(pmu_qch_byp) |
/* BIT(pmu_wfi_byp) | */
BIT(pmu_slp_cnt_en);
pmu1_ddr_pwr_con = 0;
pmu1_pll_pd_con =
BIT(pmu_bpll_pd_en) |
BIT(pmu_lpll_pd_en) |
BIT(pmu_spll_pd_en) |
BIT(pmu_gpll_pd_en) |
BIT(pmu_cpll_pd_en) |
BIT(pmu_ppll_pd_en) |
BIT(pmu_aupll_pd_en) |
BIT(pmu_vpll_pd_en);
pmu1cru_pwr_con =
BIT(pmu_alive_osc_mode_en) |
BIT(pmu_io_sleep_en) |
BIT(pmu_power_off_en);
pmu2_bus_idle_con[0] = 0xffff & ~(bus_idle_st & 0xffff);
pmu2_bus_idle_con[1] = 0x3fff & ~(bus_idle_st >> 16);
pmu2_pwr_gt_con[0] = 0xffff & ~(pmu_pd_st & 0xffff);
pmu2_pwr_gt_con[1] = 0x03ff & ~(pmu_pd_st >> 16);
pmu2_pwr_gt_con[0] &=
~(BIT(pmu_pd_secure) |
BIT(pmu_pd_bus) |
BIT(pmu_pd_center) |
BIT(pmu_pd_ddr));
mmio_write_32(PMU_BASE + PMU2_BUS_IDLEACK_BYPASS_CON, 0x00030003);
mmio_write_32(SYS_SGRF_FW_BASE + FW_SGRF_KEYUPD_CON0, 0x03ff0000);
/* disable repair */
mmio_write_32(PMU_BASE + PMU2_BISR_GLB_CON, 0x00010000);
/* disable ddr_hash_key update.
* enable disable ddr_hash_key auto update.
* wait ddr_hash_key auto update.
*/
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(19),
BITS_WITH_WMASK(key_upd_msk, 0x7, 8));
mmio_write_32(SYS_SGRF_FW_BASE + FW_SGRF_KEYUPD_CON1,
BITS_WITH_WMASK(fw_lkp_upd_msk, 0x7, 10));
mmio_write_32(SYS_SGRF_FW_BASE + FW_SGRF_KEYUPD_CON1,
BITS_WITH_WMASK(fw_ddr_upd_msk, 0x7u, 13));
mmio_write_32(PMU_BASE + PMU0_PMIC_STABLE_CNT_THRES, 24000 * 5);
mmio_write_32(PMU_BASE + PMU0_OSC_STABLE_CNT_THRES, 24000 * 5);
mmio_write_32(PMU_BASE + PMU1_OSC_STABLE_CNT_THRESH, 24000 * 5);
mmio_write_32(PMU_BASE + PMU1_STABLE_CNT_THRESH, 24000 * 5);
mmio_write_32(PMU_BASE + PMU1_SLEEP_CNT_THRESH, 24000 * 15);
/* Pmu's clk has switched to 24M back When pmu FSM counts
* the follow counters, so we should use 24M to calculate
* these counters.
*/
mmio_write_32(PMU_BASE + PMU0_WAKEUP_RST_CLR_CNT_THRES, 12000);
mmio_write_32(PMU_BASE + PMU1_WAKEUP_RST_CLR_CNT_THRESH, 12000);
mmio_write_32(PMU_BASE + PMU1_PLL_LOCK_CNT_THRESH, 12000);
mmio_write_32(PMU_BASE + PMU1_PWM_SWITCH_CNT_THRESH,
24000 * 2);
mmio_write_32(PMU_BASE + PMU2_SCU0_PWRUP_CNT_THRESH, 0);
mmio_write_32(PMU_BASE + PMU2_SCU0_PWRDN_CNT_THRESH, 0);
mmio_write_32(PMU_BASE + PMU2_SCU0_STABLE_CNT_THRESH, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRUP_CNT_THRESH_0, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRDN_CNT_THRESH_0, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRUP_CNT_THRESH_1, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRDN_CNT_THRESH_1, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRUP_CNT_THRESH_2, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_PWRDN_CNT_THRESH_2, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_POWER_CON, 0xffff0007);
/* pmu_clst_idle_con */
mmio_write_32(PMU_BASE + PMU2_CLUSTER0_IDLE_CON, 0xffff0007);
mmio_write_32(PMU_BASE + PMU2_CLUSTER1_IDLE_CON, 0xffff0007);
/* pmu_scu_pwr_con */
/* L2's flush and idle by hardware, so need to enable wfil2 bypass */
mmio_write_32(PMU_BASE + PMU2_SCU0_PWR_CON, 0xffff020f);
mmio_write_32(PMU_BASE + PMU2_SCU1_PWR_CON, 0xffff020f);
mmio_write_32(PMU_BASE + PMU2_SCU0_AUTO_PWR_CON, 0x00070000);
mmio_write_32(PMU_BASE + PMU2_SCU1_AUTO_PWR_CON, 0x00070000);
mmio_write_32(PMU_BASE + PMU2_CCI_PWR_CON, 0xffff0009);
/* pmu_int_msk_con */
/* mmio_write_32(PMU_BASE + PMU1_INT_MASK_CON, BITS_WITH_WMASK(1, 0x1, 0)); */
/* pmu_pwr_con */
mmio_write_32(PMU_BASE + PMU1_PWR_CON, WITH_16BITS_WMSK(pmu1_pwr_con));
/* pmu_cru_pwr_conx */
mmio_write_32(PMU_BASE + PMU1_CRU_PWR_CON(0), WITH_16BITS_WMSK(pmu1cru_pwr_con));
/* pmu_ddr_pwr_con */
mmio_write_32(PMU_BASE + PMU0_DDR_RET_CON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_DDR_PWR_CON(0), WITH_16BITS_WMSK(pmu1_ddr_pwr_con));
mmio_write_32(PMU_BASE + PMU1_DDR_PWR_CON(1), WITH_16BITS_WMSK(pmu1_ddr_pwr_con));
mmio_write_32(PMU_BASE + PMU1_DDR_AXIPWR_CON(0), 0x03ff03ff);
mmio_write_32(PMU_BASE + PMU1_DDR_AXIPWR_CON(1), 0x03ff03ff);
/* pll_pd */
mmio_write_32(PMU_BASE + PMU1_PLLPD_CON(0), WITH_16BITS_WMSK(pmu1_pll_pd_con));
/* bus idle */
mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(0), WITH_16BITS_WMSK(pmu2_bus_idle_con[0]));
mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(1), WITH_16BITS_WMSK(pmu2_bus_idle_con[1]));
/* power gate */
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(0), WITH_16BITS_WMSK(pmu2_pwr_gt_con[0]));
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(1), WITH_16BITS_WMSK(pmu2_pwr_gt_con[1]));
/* vol gate */
mmio_write_32(PMU_BASE + PMU2_VOL_GATE_SFTCON(0), 0xffff0031);
mmio_write_32(PMU_BASE + PMU2_VOL_GATE_SFTCON(1), 0xffff0200);
/* wakeup source */
mmio_write_32(PMU_BASE + PMU1_WAKEUP_INT_CON, pmu1_wkup_int_con);
/* ppll clamp */
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(5), 0x00400040);
/* usbphy clamp */
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(5),
BITS_WITH_WMASK(0x9, 0x9, 2));
/* big core pwr ack bypass */
mmio_write_32(PMU_BASE + PMU2_C0_PWRACK_BYPASS_CON(0), 0x01000100);
mmio_write_32(PMU_BASE + PMU2_C1_PWRACK_BYPASS_CON(0), 0x01000100);
mmio_write_32(PMU_BASE + PMU2_C2_PWRACK_BYPASS_CON(0), 0x01000100);
}
static void pmu_sleep_restore(void)
{
mmio_write_32(PMU_BASE + PMU0_INFO_TX_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_DEBUG_INFO_SEL, 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_CLUSTER0_IDLE_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_SCU0_PWR_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_CCI_PWR_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_INT_MASK_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_PWR_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_CRU_PWR_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_DDR_PWR_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_DDR_PWR_CON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_DDR_AXIPWR_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_DDR_AXIPWR_CON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_PLLPD_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_BUS_IDLE_CON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_PWR_GATE_CON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_VOL_GATE_SFTCON(0), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_VOL_GATE_SFTCON(1), 0xffff0000);
mmio_write_32(PMU_BASE + PMU2_BUS_IDLEACK_BYPASS_CON, 0xffff0000);
mmio_write_32(PMU_BASE + PMU1_WAKEUP_INT_CON, 0);
mmio_write_32(PMU_BASE + PMU2_FAST_POWER_CON,
WITH_16BITS_WMSK(ddr_data.pmu2_fast_pwr_con));
mmio_write_32(PMU_BASE + PMU2_BISR_GLB_CON,
WITH_16BITS_WMSK(ddr_data.pmu2_bisr_glb_con));
mmio_write_32(PMU_BASE + PMU2_C0_PWRACK_BYPASS_CON(0),
WITH_16BITS_WMSK(ddr_data.pmu2_c0_ack_sel_con0));
mmio_write_32(PMU_BASE + PMU2_C1_PWRACK_BYPASS_CON(0),
WITH_16BITS_WMSK(ddr_data.pmu2_c1_ack_sel_con0));
mmio_write_32(PMU_BASE + PMU2_C2_PWRACK_BYPASS_CON(0),
WITH_16BITS_WMSK(ddr_data.pmu2_c2_ack_sel_con0));
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(5),
WITH_16BITS_WMSK(ddr_data.pmu0grf_soc_con5));
}
static void secure_watchdog_disable(void)
{
ddr_data.sys_sgrf_soc_con0 =
mmio_read_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(0));
/* pause wdt_s */
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(0),
BITS_WITH_WMASK(1, 0x1, 14));
}
static void secure_watchdog_restore(void)
{
mmio_write_32(SYS_SGRF_BASE + SYSSGRF_SOC_CON(0),
ddr_data.sys_sgrf_soc_con0 |
BITS_WMSK(0x1, 14));
if (mmio_read_32(WDT_S_BASE + WDT_CR) & WDT_EN)
mmio_write_32(WDT_S_BASE + WDT_CRR, 0x76);
}
static void soc_sleep_config(void)
{
ddr_data.pmu0grf_soc_con0 =
mmio_read_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(0));
ddr_data.pmu0grf_soc_con1 =
mmio_read_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(1));
ddr_data.gpio0a_iomux_l =
mmio_read_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_L);
ddr_data.gpio0a_iomux_h =
mmio_read_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_H);
ddr_data.gpio0b_iomux_l =
mmio_read_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0B_IOMUX_SEL_L);
sleep_pin_config();
pmu_sleep_config();
ddr_sleep_config();
secure_watchdog_disable();
}
static void soc_sleep_restore(void)
{
secure_watchdog_restore();
ddr_sleep_config_restore();
pmu_sleep_restore();
mmio_write_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_L,
WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_l));
mmio_write_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0A_IOMUX_SEL_H,
WITH_16BITS_WMSK(ddr_data.gpio0a_iomux_h));
mmio_write_32(PMU0_IOC_BASE + PMUIO0_IOC_GPIO0B_IOMUX_SEL_L,
WITH_16BITS_WMSK(ddr_data.gpio0b_iomux_l));
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(1),
WITH_16BITS_WMSK(ddr_data.pmu0grf_soc_con1));
mmio_write_32(PMU0_GRF_BASE + PMU0GRF_SOC_CON(0),
WITH_16BITS_WMSK(ddr_data.pmu0grf_soc_con0));
}
static void pm_pll_suspend(void)
{
ddr_data.cru_mode_con = mmio_read_32(CRU_BASE + 0x280);
ddr_data.secure_cru_mode = mmio_read_32(SECURE_CRU_BASE + 0x4280);
/* bpll gpll vpll aupll cpll spll switch to slow mode */
mmio_write_32(CRU_BASE + 0x280, 0x03ff0000);
mmio_write_32(SECURE_CRU_BASE + 0x4280, 0x00030000);
/* hclk_pmu_cm0_root_i_sel to 24M */
mmio_write_32(PMU1_CRU_BASE + PMU1CRU_CLKSEL_CON(4),
BITS_WITH_WMASK(0x3, 0x3, 2));
}
static void pm_pll_restore(void)
{
mmio_write_32(CRU_BASE + 0x280, WITH_16BITS_WMSK(ddr_data.cru_mode_con));
mmio_write_32(SECURE_CRU_BASE + 0x4280,
WITH_16BITS_WMSK(ddr_data.secure_cru_mode));
}
int rockchip_soc_sys_pwr_dm_suspend(void)
{
psram_sleep_cfg->pm_flag &= ~PM_WARM_BOOT_BIT;
clk_gate_con_save();
clk_gate_con_disable();
dmc_save();
pmu_power_domains_suspend();
soc_sleep_config();
pm_pll_suspend();
pd_core_save();
return 0;
}
int rockchip_soc_sys_pwr_dm_resume(void)
{
pd_core_restore();
pm_pll_restore();
soc_sleep_restore();
pmu_power_domains_resume();
plat_rockchip_gic_cpuif_enable();
dmc_restore();
clk_gate_con_restore();
psram_sleep_cfg->pm_flag |= PM_WARM_BOOT_BIT;
return 0;
}
void __dead2 rockchip_soc_cores_pd_pwr_dn_wfi(const
psci_power_state_t *target_state)
{
psci_power_down_wfi();
/* should never reach here */
panic();
}
void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void)
{
psci_power_down_wfi();
/* should never reach here */
panic();
}
static int rockchip_reboot_is_rbrom(void)
{
return mmio_read_32(PMU0_GRF_BASE + PMU0GRF_OS_REG(16)) ==
BOOT_BROM_DOWNLOAD;
}
static void rockchip_soc_soft_reset_check_rstout(void)
{
/*
* Maskrom enter maskrom-usb mode according to os_reg0 which
* will be reset by NPOR. So disable tsadc_shut_m0 if we want
* to maskrom-usb mode.
*/
if (rockchip_reboot_is_rbrom() != 0) {
/* write BOOT_BROM_DOWNLOAD to os_reg0 */
mmio_write_32(PMU1_GRF_BASE + PMU1GRF_OS_REG(0), BOOT_BROM_DOWNLOAD);
/* disable first/tsadc/wdt reset output */
mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(0), 0x00070000);
/* clear reset hold */
mmio_write_32(PMU0SGRF_BASE + PMU0SGRF_SOC_CON(1), 0xffff0000);
mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(16), 0xffff0000);
mmio_write_32(PMU1SGRF_BASE + PMU1SGRF_SOC_CON(17), 0xffff0000);
}
}
void __dead2 rockchip_soc_soft_reset(void)
{
rockchip_soc_soft_reset_check_rstout();
/* pll slow mode */
mmio_write_32(CRU_BASE + CRU_MODE_CON, 0x003f0000);
dsb();
isb();
INFO("system reset......\n");
mmio_write_32(CRU_BASE + CRU_GLB_SRST_FST, GLB_SRST_FST_CFG_VAL);
/*
* Maybe the HW needs some times to reset the system,
* so we do not hope the core to execute valid codes.
*/
psci_power_down_wfi();
/* should never reach here */
panic();
}
void __dead2 rockchip_soc_system_off(void)
{
INFO("system poweroff......\n");
/* gpio0_a3 config output */
mmio_write_32(GPIO0_BASE + GPIO_SWPORT_DDR_L,
BITS_WITH_WMASK(1, 0x1, 3));
/* gpio0_a3 config output high level */
mmio_write_32(GPIO0_BASE + GPIO_SWPORT_DR_L,
BITS_WITH_WMASK(1, 0x1, 3));
dsb();
/*
* Maybe the HW needs some times to reset the system,
* so we do not hope the core to execute valid codes.
*/
psci_power_down_wfi();
/* should never reach here */
panic();
}
static void rockchip_pmu_pd_repair_init(void)
{
INFO("enable memory repair\n");
/* Enable gpu and npu repair */
mmio_write_32(PMU_BASE + PMU2_BISR_PDGEN_CON(1),
BITS_WITH_WMASK(0xf, 0xf, 6));
}
void plat_rockchip_pmu_init(void)
{
int cpu;
for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++)
cpuson_flags[cpu] = 0;
psram_sleep_cfg->sp = PSRAM_SP_TOP;
psram_sleep_cfg->ddr_func = (uint64_t)ddr_resume;
psram_sleep_cfg->ddr_data = 0;
psram_sleep_cfg->ddr_flag = 0;
psram_sleep_cfg->boot_mpidr = read_mpidr_el1() & 0xffff;
psram_sleep_cfg->pm_flag = PM_WARM_BOOT_BIT;
nonboot_cpus_off();
/*
* When perform idle operation, corresponding clock can be
* opened or gated automatically.
*/
mmio_write_32(PMU_BASE + PMU2_NOC_AUTO_CON(0), 0xffffffff);
mmio_write_32(PMU_BASE + PMU2_NOC_AUTO_CON(1), 0xffffffff);
/* remap pmusram to 0x00000000 */
mmio_write_32(PMU0SGRF_BASE + PMU0SGRF_SOC_CON(2), BITS_WITH_WMASK(1, 0x3, 0));
/* enable power off VD_NPU by hrdware */
mmio_write_32(PMU_BASE + PMU2_VOL_GATE_SFTCON(0),
BITS_WITH_WMASK(0x1, 0x1, 0));
rockchip_pmu_pd_repair_init();
pm_reg_rgns_init();
}