Heiko Stuebner | 87b9a3c | 2019-03-14 22:12:04 +0100 | [diff] [blame] | 1 | /* |
Quentin Schulz | 61a9422 | 2024-10-28 11:46:57 +0100 | [diff] [blame^] | 2 | * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved. |
Heiko Stuebner | 87b9a3c | 2019-03-14 22:12:04 +0100 | [diff] [blame] | 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <assert.h> |
| 8 | #include <errno.h> |
| 9 | |
| 10 | #include <platform_def.h> |
| 11 | |
| 12 | #include <arch_helpers.h> |
| 13 | #include <common/debug.h> |
| 14 | #include <drivers/delay_timer.h> |
| 15 | #include <lib/mmio.h> |
| 16 | #include <plat/common/platform.h> |
| 17 | |
| 18 | #include <plat_private.h> |
| 19 | #include <pmu.h> |
| 20 | #include <pmu_com.h> |
| 21 | #include <rk3288_def.h> |
| 22 | #include <secure.h> |
| 23 | #include <soc.h> |
| 24 | |
| 25 | DEFINE_BAKERY_LOCK(rockchip_pd_lock); |
| 26 | |
| 27 | static uint32_t cpu_warm_boot_addr; |
| 28 | |
| 29 | static uint32_t store_pmu_pwrmode_con; |
| 30 | static uint32_t store_sgrf_soc_con0; |
| 31 | static uint32_t store_sgrf_cpu_con0; |
| 32 | |
| 33 | /* These enum are variants of low power mode */ |
| 34 | enum { |
| 35 | ROCKCHIP_ARM_OFF_LOGIC_NORMAL = 0, |
| 36 | ROCKCHIP_ARM_OFF_LOGIC_DEEP = 1, |
| 37 | }; |
| 38 | |
Heiko Stuebner | 87b9a3c | 2019-03-14 22:12:04 +0100 | [diff] [blame] | 39 | static bool rk3288_sleep_disable_osc(void) |
| 40 | { |
| 41 | static const uint32_t reg_offset[] = { GRF_UOC0_CON0, GRF_UOC1_CON0, |
| 42 | GRF_UOC2_CON0 }; |
| 43 | uint32_t reg, i; |
| 44 | |
| 45 | /* |
| 46 | * if any usb phy is still on(GRF_SIDDQ==0), that means we need the |
| 47 | * function of usb wakeup, so do not switch to 32khz, since the usb phy |
| 48 | * clk does not connect to 32khz osc |
| 49 | */ |
| 50 | for (i = 0; i < ARRAY_SIZE(reg_offset); i++) { |
| 51 | reg = mmio_read_32(GRF_BASE + reg_offset[i]); |
| 52 | if (!(reg & GRF_SIDDQ)) |
| 53 | return false; |
| 54 | } |
| 55 | |
| 56 | return true; |
| 57 | } |
| 58 | |
| 59 | static void pmu_set_sleep_mode(int level) |
| 60 | { |
| 61 | uint32_t mode_set, mode_set1; |
| 62 | bool osc_disable = rk3288_sleep_disable_osc(); |
| 63 | |
| 64 | mode_set = BIT(pmu_mode_glb_int_dis) | BIT(pmu_mode_l2_flush_en) | |
| 65 | BIT(pmu_mode_sref0_enter) | BIT(pmu_mode_sref1_enter) | |
| 66 | BIT(pmu_mode_ddrc0_gt) | BIT(pmu_mode_ddrc1_gt) | |
| 67 | BIT(pmu_mode_en) | BIT(pmu_mode_chip_pd) | |
| 68 | BIT(pmu_mode_scu_pd); |
| 69 | |
| 70 | mode_set1 = BIT(pmu_mode_clr_core) | BIT(pmu_mode_clr_cpup); |
| 71 | |
| 72 | if (level == ROCKCHIP_ARM_OFF_LOGIC_DEEP) { |
| 73 | /* arm off, logic deep sleep */ |
| 74 | mode_set |= BIT(pmu_mode_bus_pd) | BIT(pmu_mode_pmu_use_lf) | |
| 75 | BIT(pmu_mode_ddrio1_ret) | |
| 76 | BIT(pmu_mode_ddrio0_ret) | |
| 77 | BIT(pmu_mode_pmu_alive_use_lf) | |
| 78 | BIT(pmu_mode_pll_pd); |
| 79 | |
| 80 | if (osc_disable) |
| 81 | mode_set |= BIT(pmu_mode_osc_dis); |
| 82 | |
| 83 | mode_set1 |= BIT(pmu_mode_clr_alive) | BIT(pmu_mode_clr_bus) | |
| 84 | BIT(pmu_mode_clr_peri) | BIT(pmu_mode_clr_dma); |
| 85 | |
| 86 | mmio_write_32(PMU_BASE + PMU_WAKEUP_CFG1, |
| 87 | pmu_armint_wakeup_en); |
| 88 | |
| 89 | /* |
| 90 | * In deep suspend we use PMU_PMU_USE_LF to let the rk3288 |
| 91 | * switch its main clock supply to the alternative 32kHz |
| 92 | * source. Therefore set 30ms on a 32kHz clock for pmic |
| 93 | * stabilization. Similar 30ms on 24MHz for the other |
| 94 | * mode below. |
| 95 | */ |
| 96 | mmio_write_32(PMU_BASE + PMU_STABL_CNT, 32 * 30); |
| 97 | |
| 98 | /* only wait for stabilization, if we turned the osc off */ |
| 99 | mmio_write_32(PMU_BASE + PMU_OSC_CNT, |
| 100 | osc_disable ? 32 * 30 : 0); |
| 101 | } else { |
| 102 | /* |
| 103 | * arm off, logic normal |
| 104 | * if pmu_clk_core_src_gate_en is not set, |
| 105 | * wakeup will be error |
| 106 | */ |
| 107 | mode_set |= BIT(pmu_mode_core_src_gt); |
| 108 | |
| 109 | mmio_write_32(PMU_BASE + PMU_WAKEUP_CFG1, |
| 110 | BIT(pmu_armint_wakeup_en) | |
| 111 | BIT(pmu_gpioint_wakeup_en)); |
| 112 | |
| 113 | /* 30ms on a 24MHz clock for pmic stabilization */ |
| 114 | mmio_write_32(PMU_BASE + PMU_STABL_CNT, 24000 * 30); |
| 115 | |
| 116 | /* oscillator is still running, so no need to wait */ |
| 117 | mmio_write_32(PMU_BASE + PMU_OSC_CNT, 0); |
| 118 | } |
| 119 | |
| 120 | mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, mode_set); |
| 121 | mmio_write_32(PMU_BASE + PMU_PWRMODE_CON1, mode_set1); |
| 122 | } |
| 123 | |
| 124 | static int cpus_power_domain_on(uint32_t cpu_id) |
| 125 | { |
| 126 | uint32_t cpu_pd; |
| 127 | |
| 128 | cpu_pd = PD_CPU0 + cpu_id; |
| 129 | |
| 130 | /* if the core has been on, power it off first */ |
| 131 | if (pmu_power_domain_st(cpu_pd) == pmu_pd_on) { |
| 132 | /* put core in reset - some sort of A12/A17 bug */ |
| 133 | mmio_write_32(CRU_BASE + CRU_SOFTRSTS_CON(0), |
| 134 | BIT(cpu_id) | (BIT(cpu_id) << 16)); |
| 135 | |
| 136 | pmu_power_domain_ctr(cpu_pd, pmu_pd_off); |
| 137 | } |
| 138 | |
| 139 | pmu_power_domain_ctr(cpu_pd, pmu_pd_on); |
| 140 | |
| 141 | /* pull core out of reset */ |
| 142 | mmio_write_32(CRU_BASE + CRU_SOFTRSTS_CON(0), BIT(cpu_id) << 16); |
| 143 | |
| 144 | return 0; |
| 145 | } |
| 146 | |
| 147 | static int cpus_power_domain_off(uint32_t cpu_id) |
| 148 | { |
| 149 | uint32_t cpu_pd = PD_CPU0 + cpu_id; |
| 150 | |
| 151 | if (pmu_power_domain_st(cpu_pd) == pmu_pd_off) |
| 152 | return 0; |
| 153 | |
| 154 | if (check_cpu_wfie(cpu_id, CKECK_WFEI_MSK)) |
| 155 | return -EINVAL; |
| 156 | |
| 157 | /* put core in reset - some sort of A12/A17 bug */ |
| 158 | mmio_write_32(CRU_BASE + CRU_SOFTRSTS_CON(0), |
| 159 | BIT(cpu_id) | (BIT(cpu_id) << 16)); |
| 160 | |
| 161 | pmu_power_domain_ctr(cpu_pd, pmu_pd_off); |
| 162 | |
| 163 | return 0; |
| 164 | } |
| 165 | |
| 166 | static void nonboot_cpus_off(void) |
| 167 | { |
| 168 | uint32_t boot_cpu, cpu; |
| 169 | |
| 170 | boot_cpu = plat_my_core_pos(); |
| 171 | boot_cpu = MPIDR_AFFLVL0_VAL(read_mpidr()); |
| 172 | |
| 173 | /* turn off noboot cpus */ |
| 174 | for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) { |
| 175 | if (cpu == boot_cpu) |
| 176 | continue; |
| 177 | |
| 178 | cpus_power_domain_off(cpu); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | void sram_save(void) |
| 183 | { |
| 184 | /* TODO: support the sdram save for rk3288 SoCs*/ |
| 185 | } |
| 186 | |
| 187 | void sram_restore(void) |
| 188 | { |
| 189 | /* TODO: support the sdram restore for rk3288 SoCs */ |
| 190 | } |
| 191 | |
| 192 | int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint) |
| 193 | { |
| 194 | uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); |
| 195 | |
| 196 | assert(cpu_id < PLATFORM_CORE_COUNT); |
| 197 | assert(cpuson_flags[cpu_id] == 0); |
| 198 | cpuson_flags[cpu_id] = PMU_CPU_HOTPLUG; |
| 199 | cpuson_entry_point[cpu_id] = entrypoint; |
| 200 | dsb(); |
| 201 | |
| 202 | cpus_power_domain_on(cpu_id); |
| 203 | |
| 204 | /* |
| 205 | * We communicate with the bootrom to active the cpus other |
| 206 | * than cpu0, after a blob of initialize code, they will |
Elyes Haouas | 2be03c0 | 2023-02-13 09:14:48 +0100 | [diff] [blame] | 207 | * stay at wfe state, once they are activated, they will check |
Heiko Stuebner | 87b9a3c | 2019-03-14 22:12:04 +0100 | [diff] [blame] | 208 | * the mailbox: |
| 209 | * sram_base_addr + 4: 0xdeadbeaf |
| 210 | * sram_base_addr + 8: start address for pc |
| 211 | * The cpu0 need to wait the other cpus other than cpu0 entering |
| 212 | * the wfe state.The wait time is affected by many aspects. |
| 213 | * (e.g: cpu frequency, bootrom frequency, sram frequency, ...) |
| 214 | */ |
| 215 | mdelay(1); /* ensure the cpus other than cpu0 to startup */ |
| 216 | |
| 217 | /* tell the bootrom mailbox where to start from */ |
| 218 | mmio_write_32(SRAM_BASE + 8, cpu_warm_boot_addr); |
| 219 | mmio_write_32(SRAM_BASE + 4, 0xDEADBEAF); |
| 220 | dsb(); |
| 221 | sev(); |
| 222 | |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | int rockchip_soc_cores_pwr_dm_on_finish(void) |
| 227 | { |
| 228 | return 0; |
| 229 | } |
| 230 | |
| 231 | int rockchip_soc_sys_pwr_dm_resume(void) |
| 232 | { |
| 233 | mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, store_pmu_pwrmode_con); |
| 234 | mmio_write_32(SGRF_BASE + SGRF_CPU_CON(0), |
| 235 | store_sgrf_cpu_con0 | SGRF_DAPDEVICE_MSK); |
| 236 | |
| 237 | /* disable fastboot mode */ |
| 238 | mmio_write_32(SGRF_BASE + SGRF_SOC_CON(0), |
| 239 | store_sgrf_soc_con0 | SGRF_FAST_BOOT_DIS); |
| 240 | |
| 241 | secure_watchdog_ungate(); |
| 242 | clk_gate_con_restore(); |
| 243 | clk_sel_con_restore(); |
| 244 | clk_plls_resume(); |
| 245 | |
| 246 | secure_gic_init(); |
| 247 | plat_rockchip_gic_init(); |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | int rockchip_soc_sys_pwr_dm_suspend(void) |
| 253 | { |
| 254 | nonboot_cpus_off(); |
| 255 | |
| 256 | store_sgrf_cpu_con0 = mmio_read_32(SGRF_BASE + SGRF_CPU_CON(0)); |
| 257 | store_sgrf_soc_con0 = mmio_read_32(SGRF_BASE + SGRF_SOC_CON(0)); |
| 258 | store_pmu_pwrmode_con = mmio_read_32(PMU_BASE + PMU_PWRMODE_CON); |
| 259 | |
| 260 | /* save clk-gates and ungate all for suspend */ |
| 261 | clk_gate_con_save(); |
| 262 | clk_gate_con_disable(); |
| 263 | clk_sel_con_save(); |
| 264 | |
| 265 | pmu_set_sleep_mode(ROCKCHIP_ARM_OFF_LOGIC_NORMAL); |
| 266 | |
| 267 | clk_plls_suspend(); |
| 268 | secure_watchdog_gate(); |
| 269 | |
| 270 | /* |
| 271 | * The dapswjdp can not auto reset before resume, that cause it may |
| 272 | * access some illegal address during resume. Let's disable it before |
| 273 | * suspend, and the MASKROM will enable it back. |
| 274 | */ |
| 275 | mmio_write_32(SGRF_BASE + SGRF_CPU_CON(0), SGRF_DAPDEVICE_MSK); |
| 276 | |
| 277 | /* |
| 278 | * SGRF_FAST_BOOT_EN - system to boot from FAST_BOOT_ADDR |
| 279 | */ |
| 280 | mmio_write_32(SGRF_BASE + SGRF_SOC_CON(0), SGRF_FAST_BOOT_ENA); |
| 281 | |
| 282 | /* boot-address of resuming system is from this register value */ |
| 283 | mmio_write_32(SGRF_BASE + SGRF_FAST_BOOT_ADDR, |
| 284 | (uint32_t)&pmu_cpuson_entrypoint); |
| 285 | |
| 286 | /* flush all caches - otherwise we might loose the resume address */ |
| 287 | dcsw_op_all(DC_OP_CISW); |
| 288 | |
| 289 | return 0; |
| 290 | } |
| 291 | |
| 292 | void rockchip_plat_mmu_svc_mon(void) |
| 293 | { |
| 294 | } |
| 295 | |
| 296 | void plat_rockchip_pmu_init(void) |
| 297 | { |
| 298 | uint32_t cpu; |
| 299 | |
| 300 | cpu_warm_boot_addr = (uint32_t)platform_cpu_warmboot; |
| 301 | |
| 302 | /* on boot all power-domains are on */ |
| 303 | for (cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu++) |
| 304 | cpuson_flags[cpu] = pmu_pd_on; |
| 305 | |
| 306 | nonboot_cpus_off(); |
| 307 | } |