1/* 2 * Copyright (c) 2014-2025, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7#include <arch.h> 8#include <asm_macros.S> 9#include <assert_macros.S> 10#include <common/bl_common.h> 11#include <common/debug.h> 12#include <cpu_macros.S> 13#include <lib/cpus/cpu_ops.h> 14#include <lib/cpus/errata.h> 15#include <lib/el3_runtime/cpu_data.h> 16 17#ifdef IMAGE_BL31 /* The power down core and cluster is needed only in BL31 */ 18 /* 19 * void prepare_cpu_pwr_dwn(unsigned int power_level) 20 * 21 * Prepare CPU power down function for all platforms. The function takes 22 * a domain level to be powered down as its parameter. After the cpu_ops 23 * pointer is retrieved from cpu_data, the handler for requested power 24 * level is called. 25 */ 26 .globl prepare_cpu_pwr_dwn 27func prepare_cpu_pwr_dwn 28 /* 29 * If the given power level exceeds CPU_MAX_PWR_DWN_OPS, we call the 30 * power down handler for the last power level 31 */ 32 mov_imm x2, (CPU_MAX_PWR_DWN_OPS - 1) 33 cmp x0, x2 34 csel x2, x2, x0, hi 35 36 mrs x1, tpidr_el3 37 ldr x0, [x1, #CPU_DATA_CPU_OPS_PTR] 38#if ENABLE_ASSERTIONS 39 cmp x0, #0 40 ASM_ASSERT(ne) 41#endif 42 43 /* Get the appropriate power down handler */ 44 mov x1, #CPU_PWR_DWN_OPS 45 add x1, x1, x2, lsl #3 46 ldr x1, [x0, x1] 47#if ENABLE_ASSERTIONS 48 cmp x1, #0 49 ASM_ASSERT(ne) 50#endif 51 br x1 52endfunc prepare_cpu_pwr_dwn 53 54 55 /* 56 * Initializes the cpu_ops_ptr if not already initialized 57 * in cpu_data. This can be called without a runtime stack, but may 58 * only be called after the MMU is enabled. 59 * clobbers: x0 - x6, x10 60 */ 61 .globl init_cpu_ops 62func init_cpu_ops 63 mrs x6, tpidr_el3 64 ldr x0, [x6, #CPU_DATA_CPU_OPS_PTR] 65 cbnz x0, 1f 66 mov x10, x30 67 bl get_cpu_ops_ptr 68 str x0, [x6, #CPU_DATA_CPU_OPS_PTR]! 69 mov x30, x10 701: 71 ret 72endfunc init_cpu_ops 73#endif /* IMAGE_BL31 */ 74 75#if defined(IMAGE_BL31) && CRASH_REPORTING 76 /* 77 * The cpu specific registers which need to be reported in a crash 78 * are reported via cpu_ops cpu_reg_dump function. After a matching 79 * cpu_ops structure entry is found, the correponding cpu_reg_dump 80 * in the cpu_ops is invoked. 81 */ 82 .globl do_cpu_reg_dump 83func do_cpu_reg_dump 84 mov x16, x30 85 86 /* Get the matching cpu_ops pointer */ 87 bl get_cpu_ops_ptr 88 cbz x0, 1f 89 90 /* Get the cpu_ops cpu_reg_dump */ 91 ldr x2, [x0, #CPU_REG_DUMP] 92 cbz x2, 1f 93 blr x2 941: 95 mov x30, x16 96 ret 97endfunc do_cpu_reg_dump 98#endif 99 100 /* 101 * The below function returns the cpu_ops structure matching the 102 * midr of the core. It reads the MIDR_EL1 and finds the matching 103 * entry in cpu_ops entries. Only the implementation and part number 104 * are used to match the entries. 105 * 106 * If cpu_ops for the MIDR_EL1 cannot be found and 107 * SUPPORT_UNKNOWN_MPID is enabled, it will try to look for a 108 * default cpu_ops with an MIDR value of 0. 109 * (Implementation number 0x0 should be reserved for software use 110 * and therefore no clashes should happen with that default value). 111 * 112 * Return : 113 * x0 - The matching cpu_ops pointer on Success 114 * x0 - 0 on failure. 115 * Clobbers : x0 - x5 116 */ 117 .globl get_cpu_ops_ptr 118func get_cpu_ops_ptr 119 /* Read the MIDR_EL1 */ 120 mrs x2, midr_el1 121 mov_imm x3, CPU_IMPL_PN_MASK 122 123 /* Retain only the implementation and part number using mask */ 124 and w2, w2, w3 125 126 /* Get the cpu_ops end location */ 127 adr_l x5, (__CPU_OPS_END__ + CPU_MIDR) 128 129 /* Initialize the return parameter */ 130 mov x0, #0 1311: 132 /* Get the cpu_ops start location */ 133 adr_l x4, (__CPU_OPS_START__ + CPU_MIDR) 134 1352: 136 /* Check if we have reached end of list */ 137 cmp x4, x5 138 b.eq search_def_ptr 139 140 /* load the midr from the cpu_ops */ 141 ldr x1, [x4], #CPU_OPS_SIZE 142 and w1, w1, w3 143 144 /* Check if midr matches to midr of this core */ 145 cmp w1, w2 146 b.ne 2b 147 148 /* Subtract the increment and offset to get the cpu-ops pointer */ 149 sub x0, x4, #(CPU_OPS_SIZE + CPU_MIDR) 150#if ENABLE_ASSERTIONS 151 cmp x0, #0 152 ASM_ASSERT(ne) 153#endif 154#ifdef SUPPORT_UNKNOWN_MPID 155 cbnz x2, exit_mpid_found 156 /* Mark the unsupported MPID flag */ 157 adrp x1, unsupported_mpid_flag 158 add x1, x1, :lo12:unsupported_mpid_flag 159 str w2, [x1] 160exit_mpid_found: 161#endif 162 ret 163 164 /* 165 * Search again for a default pointer (MIDR = 0x0) 166 * or return error if already searched. 167 */ 168search_def_ptr: 169#ifdef SUPPORT_UNKNOWN_MPID 170 cbz x2, error_exit 171 mov x2, #0 172 b 1b 173error_exit: 174#endif 175#if ENABLE_ASSERTIONS 176 /* 177 * Assert if invalid cpu_ops obtained. If this is not valid, it may 178 * suggest that the proper CPU file hasn't been included. 179 */ 180 cmp x0, #0 181 ASM_ASSERT(ne) 182#endif 183 ret 184endfunc get_cpu_ops_ptr 185 186 .globl cpu_get_rev_var 187func cpu_get_rev_var 188 get_rev_var x0, x1 189 ret 190endfunc cpu_get_rev_var 191