Marek Vasut | a538522 | 2022-12-22 01:46:43 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * This file implements basic PSCI support for i.MX8M |
| 4 | * |
| 5 | * Copyright (C) 2022 Marek Vasut <marex@denx.de> |
| 6 | */ |
| 7 | #include <asm/arch/imx-regs.h> |
| 8 | #include <asm/cache.h> |
| 9 | #include <asm/gic.h> |
| 10 | #include <asm/io.h> |
| 11 | #include <asm/psci.h> |
| 12 | #include <asm/secure.h> |
Marek Vasut | a538522 | 2022-12-22 01:46:43 +0100 | [diff] [blame] | 13 | #include <cpu_func.h> |
| 14 | #include <debug_uart.h> |
| 15 | #include <fsl_wdog.h> |
| 16 | #include <linux/bitops.h> |
| 17 | |
| 18 | #define SNVS_LPCR 0x38 |
| 19 | #define SNVS_LPCR_TOP BIT(6) |
| 20 | #define SNVS_LPCR_DP_EN BIT(5) |
| 21 | #define SNVS_LPCR_SRTC_ENV BIT(0) |
| 22 | |
| 23 | #define MPIDR_AFF0 GENMASK(7, 0) |
| 24 | |
| 25 | #define GPC_LPCR_A53_AD 0x4 |
| 26 | #define EN_Cn_WFI_PDN(cpu) BIT(((((cpu) & 1) * 2) + (((cpu) & 2) * 8))) |
| 27 | #define GPC_PGC_nCTRL(cpu) (0x800 + ((cpu) * 0x40)) |
| 28 | #define PGC_PCR BIT(0) |
| 29 | #define GPC_CPU_PGC_SW_PUP_REQ (IS_ENABLED(CONFIG_IMX8MP) ? 0xd0 : 0xf0) |
| 30 | #define COREn_A53_SW_PUP_REQ(cpu) BIT(cpu) |
| 31 | |
| 32 | #define SRC_A53RCR1 0x8 |
| 33 | #define A53_COREn_ENABLE(n) BIT(n) |
| 34 | #define SRC_GPR(n) (0x74 + ((n) * 4)) |
| 35 | |
| 36 | /* |
| 37 | * Helper code |
| 38 | */ |
| 39 | static u8 psci_state[CONFIG_ARMV8_PSCI_NR_CPUS] __secure_data = { |
| 40 | PSCI_AFFINITY_LEVEL_ON, |
| 41 | PSCI_AFFINITY_LEVEL_OFF, |
| 42 | PSCI_AFFINITY_LEVEL_OFF, |
| 43 | PSCI_AFFINITY_LEVEL_OFF |
| 44 | }; |
| 45 | |
| 46 | int psci_update_dt(void *fdt) |
| 47 | { |
| 48 | return 0; |
| 49 | } |
| 50 | |
| 51 | __secure static void psci_set_state(int cpu, u8 state) |
| 52 | { |
| 53 | psci_state[cpu] = state; |
| 54 | dsb(); |
| 55 | isb(); |
| 56 | } |
| 57 | |
| 58 | __secure static s32 psci_cpu_on_validate_mpidr(u64 mpidr, u32 *cpu) |
| 59 | { |
| 60 | *cpu = mpidr & MPIDR_AFF0; |
| 61 | |
| 62 | if (mpidr & ~MPIDR_AFF0) |
| 63 | return ARM_PSCI_RET_INVAL; |
| 64 | |
| 65 | if (*cpu >= CONFIG_ARMV8_PSCI_NR_CPUS) |
| 66 | return ARM_PSCI_RET_INVAL; |
| 67 | |
| 68 | if (psci_state[*cpu] == PSCI_AFFINITY_LEVEL_ON) |
| 69 | return ARM_PSCI_RET_ALREADY_ON; |
| 70 | |
| 71 | if (psci_state[*cpu] == PSCI_AFFINITY_LEVEL_ON_PENDING) |
| 72 | return ARM_PSCI_RET_ON_PENDING; |
| 73 | |
| 74 | return ARM_PSCI_RET_SUCCESS; |
| 75 | } |
| 76 | |
| 77 | __secure static void psci_cpu_on_write_entry_point(const u32 cpu, u64 entry_point) |
| 78 | { |
| 79 | const u64 ep = CONFIG_SPL_TEXT_BASE; |
| 80 | |
| 81 | /* Trampoline target */ |
| 82 | writeq(entry_point, CPU_RELEASE_ADDR); |
| 83 | /* RVBAR address HI */ |
| 84 | writel((u32)(ep >> 24) & 0xffff, |
| 85 | (void *)SRC_BASE_ADDR + SRC_GPR(cpu * 2)); |
| 86 | /* RVBAR address LO */ |
| 87 | writel((u32)(ep >> 2) & 0x3fffff, |
| 88 | (void *)SRC_BASE_ADDR + SRC_GPR(cpu * 2 + 1)); |
| 89 | } |
| 90 | |
| 91 | __secure static void psci_cpu_on_power_on(const u32 cpu) |
| 92 | { |
| 93 | int i; |
| 94 | |
| 95 | clrbits_le32((void *)GPC_BASE_ADDR + GPC_LPCR_A53_AD, EN_Cn_WFI_PDN(cpu)); |
| 96 | clrbits_le32((void *)SRC_BASE_ADDR + SRC_A53RCR1, A53_COREn_ENABLE(cpu)); |
| 97 | setbits_le32((void *)GPC_BASE_ADDR + GPC_PGC_nCTRL(cpu), PGC_PCR); |
| 98 | setbits_le32((void *)GPC_BASE_ADDR + GPC_CPU_PGC_SW_PUP_REQ, COREn_A53_SW_PUP_REQ(cpu)); |
| 99 | |
| 100 | /* If we fail here, the core gets power cycled, hang is OK */ |
| 101 | while (readl(GPC_BASE_ADDR + GPC_CPU_PGC_SW_PUP_REQ) & COREn_A53_SW_PUP_REQ(cpu)) |
| 102 | ; |
| 103 | |
| 104 | clrbits_le32((void *)GPC_BASE_ADDR + GPC_PGC_nCTRL(cpu), PGC_PCR); |
| 105 | setbits_le32((void *)SRC_BASE_ADDR + SRC_A53RCR1, A53_COREn_ENABLE(cpu)); |
| 106 | |
| 107 | /* Give the core a bit of time to boot and start executing code */ |
| 108 | for (i = 0; i < 100000; i++) |
| 109 | asm volatile("nop"); |
| 110 | } |
| 111 | |
| 112 | __secure static void psci_cpu_on_power_off(const u32 cpu) |
| 113 | { |
| 114 | setbits_le32((void *)GPC_BASE_ADDR + GPC_LPCR_A53_AD, EN_Cn_WFI_PDN(cpu)); |
| 115 | setbits_le32((void *)GPC_BASE_ADDR + GPC_PGC_nCTRL(cpu), PGC_PCR); |
| 116 | } |
| 117 | |
| 118 | /* |
| 119 | * Common PSCI code |
| 120 | */ |
| 121 | /* Return supported PSCI version */ |
| 122 | __secure u32 psci_version(void) |
| 123 | { |
| 124 | return ARM_PSCI_VER_1_0; |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * 64bit PSCI code |
| 129 | */ |
| 130 | __secure s32 psci_cpu_on_64(u32 __always_unused function_id, u64 mpidr, |
| 131 | u64 entry_point_address, u64 context_id) |
| 132 | { |
| 133 | u32 cpu = 0; |
| 134 | int ret; |
| 135 | |
| 136 | ret = psci_cpu_on_validate_mpidr(mpidr, &cpu); |
| 137 | if (ret != ARM_PSCI_RET_SUCCESS) |
| 138 | return ret; |
| 139 | |
| 140 | psci_cpu_on_write_entry_point(cpu, entry_point_address); |
| 141 | |
| 142 | psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON); |
| 143 | |
| 144 | psci_cpu_on_power_on(cpu); |
| 145 | |
| 146 | smp_kick_all_cpus(); |
| 147 | |
| 148 | return ARM_PSCI_RET_SUCCESS; |
| 149 | } |
| 150 | |
| 151 | __secure s32 psci_affinity_info_64(u32 __always_unused function_id, |
| 152 | u64 target_affinity, u32 lowest_affinity_level) |
| 153 | { |
| 154 | u32 cpu = target_affinity & MPIDR_AFF0; |
| 155 | |
| 156 | if (lowest_affinity_level > 0) |
| 157 | return ARM_PSCI_RET_INVAL; |
| 158 | |
| 159 | if (target_affinity & ~MPIDR_AFF0) |
| 160 | return ARM_PSCI_RET_INVAL; |
| 161 | |
| 162 | if (cpu >= CONFIG_ARMV8_PSCI_NR_CPUS) |
| 163 | return ARM_PSCI_RET_INVAL; |
| 164 | |
| 165 | return psci_state[cpu]; |
| 166 | } |
| 167 | |
| 168 | __secure s32 psci_system_reset2_64(u32 __always_unused function_id, |
| 169 | u32 reset_type, u64 cookie) |
| 170 | { |
| 171 | psci_system_reset(); |
| 172 | return 0; /* Not reached */ |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | * 32bit PSCI code |
| 177 | */ |
| 178 | __secure s32 psci_affinity_info(u32 __always_unused function_id, |
| 179 | u32 target_affinity, u32 lowest_affinity_level) |
| 180 | { |
| 181 | return psci_affinity_info_64(function_id, target_affinity, lowest_affinity_level); |
| 182 | } |
| 183 | |
| 184 | __secure s32 psci_cpu_on(u32 __always_unused function_id, u32 mpidr, |
| 185 | u32 entry_point_address, u32 context_id) |
| 186 | { |
| 187 | return psci_cpu_on_64(function_id, mpidr, entry_point_address, context_id); |
| 188 | } |
| 189 | |
| 190 | __secure s32 psci_cpu_off(void) |
| 191 | { |
| 192 | u32 cpu = psci_get_cpu_id(); |
| 193 | |
| 194 | psci_cpu_on_power_off(cpu); |
| 195 | psci_set_state(cpu, PSCI_AFFINITY_LEVEL_OFF); |
| 196 | |
| 197 | while (1) |
| 198 | wfi(); |
| 199 | } |
| 200 | |
| 201 | __secure u32 psci_migrate_info_type(void) |
| 202 | { |
| 203 | /* Trusted OS is either not present or does not require migration */ |
| 204 | return 2; |
| 205 | } |
| 206 | |
| 207 | __secure void psci_system_reset(void) |
| 208 | { |
| 209 | struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR; |
| 210 | bool ext_reset = true; |
| 211 | |
| 212 | u16 wcr = WCR_WDE; |
| 213 | |
| 214 | if (ext_reset) |
| 215 | wcr |= WCR_SRS; /* do not assert internal reset */ |
| 216 | else |
| 217 | wcr |= WCR_WDA; /* do not assert external reset */ |
| 218 | |
| 219 | /* Write 3 times to ensure it works, due to IMX6Q errata ERR004346 */ |
| 220 | writew(wcr, &wdog->wcr); |
| 221 | writew(wcr, &wdog->wcr); |
| 222 | writew(wcr, &wdog->wcr); |
| 223 | |
| 224 | while (1) |
| 225 | wfi(); |
| 226 | } |
| 227 | |
| 228 | __secure void psci_system_off(void) |
| 229 | { |
| 230 | writel(SNVS_LPCR_TOP | SNVS_LPCR_DP_EN | SNVS_LPCR_SRTC_ENV, |
| 231 | SNVS_BASE_ADDR + SNVS_LPCR); |
| 232 | |
| 233 | while (1) |
| 234 | wfi(); |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | * PSCI jump table |
| 239 | */ |
| 240 | __secure s32 psci_features(u32 __always_unused function_id, u32 psci_fid) |
| 241 | { |
| 242 | switch (psci_fid) { |
| 243 | case ARM_PSCI_0_2_FN_PSCI_VERSION: |
| 244 | case ARM_PSCI_0_2_FN_CPU_OFF: |
| 245 | case ARM_PSCI_0_2_FN_CPU_ON: |
| 246 | case ARM_PSCI_0_2_FN_AFFINITY_INFO: |
| 247 | case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: |
| 248 | case ARM_PSCI_0_2_FN_SYSTEM_OFF: |
| 249 | case ARM_PSCI_0_2_FN_SYSTEM_RESET: |
| 250 | case ARM_PSCI_0_2_FN64_CPU_ON: |
| 251 | case ARM_PSCI_0_2_FN64_AFFINITY_INFO: |
| 252 | |
| 253 | /* PSCI 1.0 interface */ |
| 254 | case ARM_PSCI_1_0_FN_PSCI_FEATURES: |
| 255 | |
| 256 | /* PSCI 1.1 interface */ |
| 257 | case ARM_PSCI_1_1_FN64_SYSTEM_RESET2: |
| 258 | return 0x0; |
| 259 | |
| 260 | /* |
| 261 | * Not implemented: |
| 262 | * ARM_PSCI_0_2_FN_CPU_SUSPEND |
| 263 | * ARM_PSCI_1_0_FN_CPU_FREEZE |
| 264 | * ARM_PSCI_1_0_FN_CPU_DEFAULT_SUSPEND |
| 265 | * ARM_PSCI_1_0_FN_NODE_HW_STATE |
| 266 | * ARM_PSCI_1_0_FN_SYSTEM_SUSPEND |
| 267 | * ARM_PSCI_1_0_FN_SET_SUSPEND_MODE |
| 268 | * ARM_PSCI_1_0_FN_STAT_RESIDENCY |
| 269 | * ARM_PSCI_1_0_FN_STAT_COUNT |
| 270 | * ARM_PSCI_0_2_FN64_CPU_SUSPEND |
| 271 | * ARM_PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND |
| 272 | * ARM_PSCI_1_0_FN64_NODE_HW_STATE |
| 273 | * ARM_PSCI_1_0_FN64_SYSTEM_SUSPEND |
| 274 | * ARM_PSCI_1_0_FN64_STAT_RESIDENCY |
| 275 | * ARM_PSCI_1_0_FN64_STAT_COUNT |
| 276 | */ |
| 277 | |
| 278 | /* Not required, ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE returns 2 */ |
| 279 | case ARM_PSCI_0_2_FN_MIGRATE: |
| 280 | case ARM_PSCI_0_2_FN64_MIGRATE: |
| 281 | /* Not required */ |
| 282 | case ARM_PSCI_0_2_FN_MIGRATE_INFO_UP_CPU: |
| 283 | case ARM_PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU: |
| 284 | default: |
| 285 | return ARM_PSCI_RET_NI; |
| 286 | } |
| 287 | } |