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