Angel Pons | 1ddb894 | 2020-04-04 18:51:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 2 | |
Julius Werner | 80af442 | 2014-10-20 13:18:56 -0700 | [diff] [blame] | 3 | #include <arch/cpu.h> |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 4 | #include <device/mmio.h> |
Julius Werner | 80af442 | 2014-10-20 13:18:56 -0700 | [diff] [blame] | 5 | #include <soc/cpu.h> |
| 6 | #include <soc/power.h> |
Julius Werner | fa938c7 | 2013-08-29 14:17:36 -0700 | [diff] [blame] | 7 | #include <string.h> |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 8 | #include <types.h> |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 9 | |
| 10 | /* ACTLR, L2CTLR L2ACTLR constants used in SMP core power up. */ |
| 11 | |
| 12 | #define ACTLR_SMP (1 << 6) |
| 13 | |
| 14 | #define L2CTLR_ECC_PARITY (1 << 21) |
| 15 | #define L2CTLR_DATA_RAM_LATENCY_MASK (7 << 0) |
| 16 | #define L2CTLR_TAG_RAM_LATENCY_MASK (7 << 6) |
| 17 | #define L2CTLR_DATA_RAM_LATENCY_CYCLES_3 (2 << 0) |
| 18 | #define L2CTLR_TAG_RAM_LATENCY_CYCLES_3 (2 << 6) |
| 19 | |
| 20 | #define L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL (1 << 3) |
| 21 | #define L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT (1 << 7) |
| 22 | #define L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE (1 << 27) |
| 23 | |
| 24 | /* Part number in CPU ID (MPIDR). */ |
| 25 | #define PART_NUMBER_CORTEX_A15 (0xc0f) |
| 26 | |
| 27 | /* State of CPU cores in Exynos 5420. */ |
| 28 | #define CORE_STATE_RESET (1 << 0) |
| 29 | #define CORE_STATE_SECONDARY_RESET (1 << 1) |
| 30 | #define CORE_STATE_SWITCH_CLUSTER (1 << 4) |
| 31 | |
| 32 | /* The default address to re-power on a code. */ |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 33 | #define CORE_RESET_INIT_ADDRESS ((void *)0x00000000) |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 34 | |
| 35 | /* Vectors in BL1 (0x02020000 = base of iRAM). */ |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 36 | #define VECTOR_CORE_SEV_HANDLER ((void *)(intptr_t)0x02020004) |
| 37 | #define VECTOR_LOW_POWER_FLAG ((void *)(intptr_t)0x02020028) |
| 38 | #define VECTOR_LOW_POWER_ADDRESS ((void *)(intptr_t)0x0202002C) |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 39 | |
| 40 | /* The data structure for the "CPU state" memory page (shared with kernel) |
| 41 | * controlling cores in active cluster. Kernel will put starting address for one |
| 42 | * core in "hotplug_address" before power on. Note the address is hard-coded in |
| 43 | * kernel (EXYNOS5420_PA_SYSRAM_NS = 0x02073000). */ |
| 44 | volatile struct exynos5420_cpu_states |
| 45 | { |
| 46 | uint32_t _reserved[2]; /* RESV, +0x00 */ |
| 47 | uint32_t resume_address; /* REG0, +0x08 */ |
| 48 | uint32_t resume_flag; /* REG1, +0x0C */ |
| 49 | uint32_t _reg2; /* REG2, +0x10 */ |
| 50 | uint32_t _reg3; /* REG3, +0x14 */ |
| 51 | uint32_t switch_address; /* REG4, +0x18, cluster switching */ |
| 52 | uint32_t hotplug_address; /* REG5, +0x1C, core hotplug */ |
| 53 | uint32_t _reg6; /* REG6, +0x20 */ |
| 54 | uint32_t c2_address; /* REG7, +0x24, C2 state change */ |
| 55 | |
| 56 | /* Managed per core status for active cluster, offset: +0x28~0x38 */ |
| 57 | uint32_t cpu_states[4]; |
| 58 | |
| 59 | /* Managed per core GIC status for active cluster, offset: 0x38~0x48 */ |
| 60 | uint32_t cpu_gic_states[4]; |
| 61 | } *exynos_cpu_states = (volatile struct exynos5420_cpu_states*)0x02073000; |
| 62 | |
| 63 | /* When leaving core handlers and jump to hot-plug address (or cluster |
| 64 | * switching), we are not sure if the destination is Thumb or ARM mode. |
| 65 | * So a BX command is required. |
| 66 | */ |
| 67 | inline static void jump_bx(void *address) |
| 68 | { |
| 69 | asm volatile ("bx %0" : : "r"(address)); |
| 70 | /* never returns. */ |
| 71 | } |
| 72 | |
| 73 | /* Extracts arbitrary bits from a 32-bit unsigned int. */ |
| 74 | inline static uint32_t get_bits(uint32_t value, uint32_t start, uint32_t len) |
| 75 | { |
| 76 | return ((value << (sizeof(value) * 8 - len - start)) >> |
| 77 | (sizeof(value) * 8 - len)); |
| 78 | } |
| 79 | |
| 80 | /* Waits the referenced address to be ready (non-zero) and then jump into it. */ |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 81 | static void wait_and_jump(volatile uint32_t *reference) |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 82 | { |
| 83 | while (!*reference) { |
| 84 | wfe(); |
| 85 | } |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 86 | jump_bx((void *)*reference); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | /* Configures L2 Control Register to use 3 cycles for DATA/TAG RAM latency. */ |
| 90 | static void configure_l2ctlr(void) |
| 91 | { |
| 92 | uint32_t val; |
| 93 | |
| 94 | val = read_l2ctlr(); |
| 95 | val &= ~(L2CTLR_DATA_RAM_LATENCY_MASK | L2CTLR_TAG_RAM_LATENCY_MASK); |
| 96 | val |= (L2CTLR_DATA_RAM_LATENCY_CYCLES_3 | L2CTLR_TAG_RAM_LATENCY_CYCLES_3 | |
| 97 | L2CTLR_ECC_PARITY); |
| 98 | write_l2ctlr(val); |
| 99 | } |
| 100 | |
| 101 | /* Configures L2 Auxiliary Control Register for Cortex A15. */ |
| 102 | static void configure_l2actlr(void) |
| 103 | { |
| 104 | uint32_t val; |
| 105 | |
| 106 | val = read_l2actlr(); |
| 107 | val |= (L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL | |
| 108 | L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT | |
| 109 | L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE); |
| 110 | write_l2actlr(val); |
| 111 | } |
| 112 | |
| 113 | /* Initializes the CPU states to reset state. */ |
| 114 | static void init_exynos_cpu_states(void) { |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 115 | memset((void *)exynos_cpu_states, 0, sizeof(*exynos_cpu_states)); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 116 | exynos_cpu_states->cpu_states[0] = CORE_STATE_RESET; |
| 117 | exynos_cpu_states->cpu_states[1] = CORE_STATE_SECONDARY_RESET; |
| 118 | exynos_cpu_states->cpu_states[2] = CORE_STATE_SECONDARY_RESET; |
| 119 | exynos_cpu_states->cpu_states[3] = CORE_STATE_SECONDARY_RESET; |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | * Ensures that the L2 logic has been used within the previous 256 cycles |
| 124 | * before modifying the ACTLR.SMP bit. This is required during boot before |
| 125 | * MMU has been enabled, or during a specified reset or power down sequence. |
| 126 | */ |
| 127 | static void enable_smp(void) |
| 128 | { |
| 129 | uint32_t actlr, val; |
| 130 | |
| 131 | /* Enable SMP mode */ |
| 132 | actlr = read_actlr(); |
| 133 | actlr |= ACTLR_SMP; |
| 134 | |
| 135 | /* Dummy read to assure L2 access */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 136 | val = read32(&exynos_power->inform0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 137 | val &= 0; |
| 138 | actlr |= val; |
| 139 | |
| 140 | write_actlr(actlr); |
| 141 | dsb(); |
| 142 | isb(); |
| 143 | } |
| 144 | |
| 145 | /* Starts the core and jumps to correct location by its state. */ |
| 146 | static void core_start_execution(void) |
| 147 | { |
| 148 | u32 cpu_id, cpu_state; |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 149 | |
| 150 | enable_smp(); |
Julius Werner | 985ff36 | 2013-09-18 14:39:50 -0700 | [diff] [blame] | 151 | set_system_mode(); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 152 | |
| 153 | cpu_id = read_mpidr() & 0x3; /* up to 4 processors for one cluster. */ |
| 154 | cpu_state = exynos_cpu_states->cpu_states[cpu_id]; |
| 155 | |
| 156 | if (cpu_state & CORE_STATE_SWITCH_CLUSTER) { |
| 157 | wait_and_jump(&exynos_cpu_states->switch_address); |
| 158 | /* never returns. */ |
| 159 | } |
| 160 | |
| 161 | /* Standard Exynos suspend/resume. */ |
Julius Werner | fa938c7 | 2013-08-29 14:17:36 -0700 | [diff] [blame] | 162 | if (exynos_power->inform1) { |
| 163 | exynos_power->inform1 = 0; |
Elyes HAOUAS | 39303d5 | 2018-07-08 12:40:45 +0200 | [diff] [blame] | 164 | jump_bx((void *)exynos_power->inform0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 165 | /* never returns. */ |
| 166 | } |
| 167 | |
| 168 | if (cpu_state & CORE_STATE_RESET) { |
| 169 | /* For Reset, U-Boot jumps to its starting address; |
Martin Roth | e18e642 | 2017-06-03 20:03:18 -0600 | [diff] [blame] | 170 | * on coreboot, seems ok to ignore for now. */ |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 171 | } |
| 172 | wait_and_jump(&exynos_cpu_states->hotplug_address); |
| 173 | /* never returns. */ |
| 174 | } |
| 175 | |
| 176 | /* The entry point for hotplug-in and cluster switching. */ |
| 177 | static void low_power_start(void) |
| 178 | { |
| 179 | uint32_t sctlr, reg_val; |
| 180 | |
| 181 | /* On warm reset, because iRAM is not cleared, all cores will enter |
| 182 | * low_power_start, not the initial address. So we need to check reset |
| 183 | * status again, and jump to 0x0 in that case. */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 184 | reg_val = read32(&exynos_power->spare0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 185 | if (reg_val != RST_FLAG_VAL) { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 186 | write32(VECTOR_LOW_POWER_FLAG, 0x0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 187 | jump_bx(CORE_RESET_INIT_ADDRESS); |
Elyes HAOUAS | 038e724 | 2016-07-29 18:31:16 +0200 | [diff] [blame] | 188 | /* restart CPU execution and never returns. */ |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | /* Workaround for iROM EVT1. A7 core execution may flow into incorrect |
| 192 | * path, bypassing first jump address and makes final jump address 0x0, |
| 193 | * so we try to make any core set again low_power_start address, if that |
| 194 | * becomes zero. */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 195 | reg_val = read32(VECTOR_CORE_SEV_HANDLER); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 196 | if (reg_val != (intptr_t)low_power_start) { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 197 | write32(VECTOR_CORE_SEV_HANDLER, (intptr_t)low_power_start); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 198 | dsb(); |
| 199 | /* ask all cores to power on again. */ |
| 200 | sev(); |
| 201 | } |
| 202 | |
Julius Werner | 985ff36 | 2013-09-18 14:39:50 -0700 | [diff] [blame] | 203 | set_system_mode(); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 204 | |
| 205 | /* Whenever a Cortex A-15 core powers on, iROM resets its L2 cache |
| 206 | * so we need to configure again. */ |
| 207 | if (get_bits(read_midr(), 4, 12) == PART_NUMBER_CORTEX_A15) { |
| 208 | configure_l2ctlr(); |
| 209 | configure_l2actlr(); |
| 210 | } |
| 211 | |
| 212 | /* Invalidate L1 & TLB */ |
| 213 | tlbiall(); |
| 214 | iciallu(); |
| 215 | |
| 216 | /* Disable MMU stuff and caches */ |
| 217 | sctlr = read_sctlr(); |
| 218 | sctlr &= ~(SCTLR_V | SCTLR_M | SCTLR_C); |
| 219 | sctlr |= (SCTLR_I | SCTLR_Z | SCTLR_A); |
| 220 | write_sctlr(sctlr); |
| 221 | |
| 222 | core_start_execution(); |
| 223 | /* The core should not return. But in order to prevent unexpected |
| 224 | * errors, a WFI command will help to put CPU back to idle state. */ |
| 225 | wfi(); |
| 226 | } |
| 227 | |
| 228 | /* Callback to shutdown a core, safe to be set as hot-plug address. */ |
| 229 | static void power_down_core(void) |
| 230 | { |
| 231 | uint32_t mpidr, core_id; |
| 232 | |
| 233 | /* MPIDR: 0~2=ID, 8~11=cluster. On Exynos 5420, cluster will be only 0 |
| 234 | * or 1. */ |
| 235 | mpidr = read_mpidr(); |
| 236 | core_id = get_bits(mpidr, 0, 2) | (get_bits(mpidr, 8, 4) << 2); |
| 237 | |
| 238 | /* Set the status of the core to low. |
| 239 | * S5E5420A User Manual, 8.8.1.202, ARM_CORE0_CONFIGURATION, two bits to |
| 240 | * control power state in each power down level. |
| 241 | */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 242 | write32(&exynos_power->arm_core[core_id].config, 0x0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 243 | |
| 244 | /* S5E5420A User Manual, 8.4.2.5, after ARM_CORE*_CONFIGURATION has been |
| 245 | * set to zero, PMU will detect and wait for WFI then run power-down |
| 246 | * sequence. */ |
| 247 | wfi(); |
| 248 | } |
| 249 | |
Martin Roth | 1fc2ba5 | 2014-12-07 14:59:11 -0700 | [diff] [blame] | 250 | /* Configures the CPU states shared memory page and then shutdown all cores. */ |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 251 | static void configure_secondary_cores(void) |
| 252 | { |
David Hendricks | b774313 | 2013-08-09 18:19:29 -0700 | [diff] [blame] | 253 | if (get_bits(read_midr(), 4, 12) == PART_NUMBER_CORTEX_A15) { |
| 254 | configure_l2ctlr(); |
| 255 | configure_l2actlr(); |
| 256 | } |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 257 | |
| 258 | /* Currently we use power_down_core as callback for each core to |
| 259 | * shutdown itself, but it is also ok to directly set ARM_CORE*_CONFIG |
| 260 | * to zero by CPU0 because every secondary cores should be already in |
| 261 | * WFI state (in bootblock). The power_down_core will be more helpful |
| 262 | * when we want to use SMP inside firmware. */ |
| 263 | |
Elyes HAOUAS | 038e724 | 2016-07-29 18:31:16 +0200 | [diff] [blame] | 264 | /* Clear boot reg (hotplug address) in CPU states */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 265 | write32((void *)&exynos_cpu_states->hotplug_address, 0); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 266 | |
| 267 | /* set low_power flag and address */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 268 | write32(VECTOR_LOW_POWER_ADDRESS, (intptr_t)low_power_start); |
| 269 | write32(VECTOR_LOW_POWER_FLAG, RST_FLAG_VAL); |
| 270 | write32(&exynos_power->spare0, RST_FLAG_VAL); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 271 | |
| 272 | /* On next SEV, shutdown all cores. */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 273 | write32(VECTOR_CORE_SEV_HANDLER, (intptr_t)power_down_core); |
Hung-Te Lin | 0682cfe | 2013-08-06 20:37:55 +0800 | [diff] [blame] | 274 | |
| 275 | /* Ask all cores in WFE mode to shutdown. */ |
| 276 | dsb(); |
| 277 | sev(); |
| 278 | } |
| 279 | |
| 280 | /* Configures the SMP cores on Exynos 5420 SOC (and shutdown all secondary |
| 281 | * cores) */ |
| 282 | void exynos5420_config_smp(void) |
| 283 | { |
| 284 | init_exynos_cpu_states(); |
| 285 | configure_secondary_cores(); |
| 286 | } |