1 /* 2 * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. 3 * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. 4 * Copyright (c) 2022-2025, Advanced Micro Devices, Inc. All rights reserved. 5 * 6 * SPDX-License-Identifier: BSD-3-Clause 7 */ 8 9 10 #include <arch_helpers.h> 11 #include <common/debug.h> 12 #include <lib/bakery_lock.h> 13 #include <lib/mmio.h> 14 #include <lib/spinlock.h> 15 #include <plat/common/platform.h> 16 17 #include <ipi.h> 18 #include <plat_ipi.h> 19 #include <plat_private.h> 20 #include "pm_defs.h" 21 #include "pm_ipi.h" 22 23 #define ERROR_CODE_MASK (0xFFFFU) 24 #define PM_OFFSET (0U) 25 26 /* 27 * ARM v8.2, the cache will turn off automatically when cpu 28 * power down. Therefore, there is no doubt to use the spin_lock here. 29 */ 30 #if !HW_ASSISTED_COHERENCY 31 static DEFINE_BAKERY_LOCK(pm_secure_lock); 32 static inline void pm_ipi_lock_get(void) 33 { 34 bakery_lock_get(&pm_secure_lock); 35 } 36 37 static inline void pm_ipi_lock_release(void) 38 { 39 bakery_lock_release(&pm_secure_lock); 40 } 41 #else 42 spinlock_t pm_secure_lock; 43 static inline void pm_ipi_lock_get(void) 44 { 45 spin_lock(&pm_secure_lock); 46 } 47 48 static inline void pm_ipi_lock_release(void) 49 { 50 spin_unlock(&pm_secure_lock); 51 } 52 #endif 53 54 /** 55 * pm_ipi_init() - Initialize IPI peripheral for communication with 56 * remote processor. 57 * @proc: Pointer to the processor who is initiating request. 58 * 59 * Return: On success, the initialization function must return 0. 60 * Any other return value will cause the framework to ignore 61 * the service. 62 * 63 * Called from pm_setup initialization function. 64 */ 65 void pm_ipi_init(const struct pm_proc *proc) 66 { 67 ipi_mb_open(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); 68 } 69 70 /** 71 * pm_ipi_send_common() - Sends IPI request to the remote processor. 72 * @proc: Pointer to the processor who is initiating request. 73 * @payload: API id and call arguments to be written in IPI buffer. 74 * @is_blocking: if to trigger the notification in blocking mode or not. 75 * 76 * Send an IPI request to the power controller. Caller needs to hold 77 * the 'pm_secure_lock' lock. 78 * 79 * Return: Returns status, either success or error+reason. 80 * 81 */ 82 static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc, 83 uint32_t payload[PAYLOAD_ARG_CNT], 84 uint32_t is_blocking) 85 { 86 uint32_t offset = PM_OFFSET; 87 uintptr_t buffer_base = proc->ipi->buffer_base + 88 IPI_BUFFER_TARGET_REMOTE_OFFSET + 89 IPI_BUFFER_REQ_OFFSET; 90 #if IPI_CRC_CHECK 91 payload[PAYLOAD_CRC_POS] = calculate_crc(payload, IPI_W0_TO_W6_SIZE); 92 #endif 93 94 /* Write payload into IPI buffer */ 95 for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) { 96 mmio_write_32(buffer_base + offset, payload[i]); 97 offset += PAYLOAD_ARG_SIZE; 98 } 99 100 /* Generate IPI to remote processor */ 101 ipi_mb_notify(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id, 102 is_blocking); 103 104 return PM_RET_SUCCESS; 105 } 106 107 /** 108 * pm_ipi_send_non_blocking() - Sends IPI request to the remote processor 109 * without blocking notification. 110 * @proc: Pointer to the processor who is initiating request. 111 * @payload: API id and call arguments to be written in IPI buffer. 112 * 113 * Send an IPI request to the power controller. 114 * 115 * Return: Returns status, either success or error+reason. 116 * 117 */ 118 enum pm_ret_status pm_ipi_send_non_blocking(const struct pm_proc *proc, 119 uint32_t payload[PAYLOAD_ARG_CNT]) 120 { 121 enum pm_ret_status ret; 122 123 pm_ipi_lock_get(); 124 125 ret = pm_ipi_send_common(proc, payload, IPI_NON_BLOCKING); 126 127 pm_ipi_lock_release(); 128 129 return ret; 130 } 131 132 /** 133 * pm_ipi_send() - Sends IPI request to the remote processor. 134 * @proc: Pointer to the processor who is initiating request. 135 * @payload: API id and call arguments to be written in IPI buffer. 136 * 137 * Send an IPI request to the power controller. 138 * 139 * Return: Returns status, either success or error+reason. 140 * 141 */ 142 enum pm_ret_status pm_ipi_send(const struct pm_proc *proc, 143 uint32_t payload[PAYLOAD_ARG_CNT]) 144 { 145 enum pm_ret_status ret; 146 147 pm_ipi_lock_get(); 148 149 ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); 150 151 pm_ipi_lock_release(); 152 153 return ret; 154 } 155 156 157 /** 158 * pm_ipi_buff_read() - Reads IPI response after remote processor has handled 159 * interrupt. 160 * @proc: Pointer to the processor who is waiting and reading response. 161 * @value: Used to return value from IPI buffer element (optional). 162 * @count: Number of values to return in @value. 163 * 164 * Return: Returns status, either success or error+reason. 165 * 166 */ 167 static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, 168 uint32_t *value, size_t count) 169 { 170 size_t i; 171 enum pm_ret_status ret; 172 #if IPI_CRC_CHECK 173 uint32_t crc; 174 #endif 175 uintptr_t buffer_base = proc->ipi->buffer_base + 176 IPI_BUFFER_TARGET_REMOTE_OFFSET + 177 IPI_BUFFER_RESP_OFFSET; 178 179 /* 180 * Read response from IPI buffer 181 * buf-0: success or error+reason 182 * buf-1: value 183 * buf-2: unused 184 * buf-3: unused 185 */ 186 for (i = 0U; i < count; i++) { 187 value[i] = mmio_read_32(buffer_base + ((i + 1U) * PAYLOAD_ARG_SIZE)); 188 } 189 190 /* 191 * Here mmio_read_32() reads return status stored in IPI payload that 192 * is received from firmware and it's value will be one the values 193 * listed in enum pm_ret_status. 194 */ 195 ret = (enum pm_ret_status)mmio_read_32(buffer_base); 196 #if IPI_CRC_CHECK 197 crc = mmio_read_32(buffer_base + (PAYLOAD_CRC_POS * PAYLOAD_ARG_SIZE)); 198 if (crc != calculate_crc((uint32_t *)buffer_base, IPI_W0_TO_W6_SIZE)) { 199 NOTICE("ERROR in CRC response payload value:0x%x\n", crc); 200 ret = PM_RET_ERROR_INVALID_CRC; 201 /* Payload data is invalid as CRC validation failed 202 * Clear the payload to avoid leakage of data to upper layers 203 */ 204 memset(value, 0, count); 205 } 206 #endif 207 208 return ret; 209 } 210 211 /** 212 * pm_ipi_buff_read_callb() - Callback function that reads value from 213 * ipi response buffer. 214 * @value: Used to return value from IPI buffer element. 215 * @count: Number of values to return in @value. 216 * 217 * This callback function fills requested data in @value from ipi response 218 * buffer. 219 * 220 * Return: Returns status, either success or error. 221 * 222 */ 223 enum pm_ret_status pm_ipi_buff_read_callb(uint32_t *value, size_t count) 224 { 225 size_t i; 226 #if IPI_CRC_CHECK 227 size_t local_count = count; 228 uint32_t crc; 229 #endif 230 uintptr_t buffer_base = IPI_BUFFER_REMOTE_BASE + 231 IPI_BUFFER_TARGET_LOCAL_OFFSET + 232 IPI_BUFFER_REQ_OFFSET; 233 enum pm_ret_status ret = PM_RET_SUCCESS; 234 235 for (i = 0; i < count; i++) { 236 value[i] = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); 237 } 238 #if IPI_CRC_CHECK 239 if (local_count > (uint32_t)IPI_BUFFER_MAX_WORDS) { 240 local_count = IPI_BUFFER_MAX_WORDS; 241 } 242 243 crc = mmio_read_32(buffer_base + (PAYLOAD_CRC_POS * PAYLOAD_ARG_SIZE)); 244 if (crc != calculate_crc((uint32_t *)buffer_base, IPI_W0_TO_W6_SIZE)) { 245 NOTICE("ERROR in CRC response payload value:0x%x\n", crc); 246 ret = PM_RET_ERROR_INVALID_CRC; 247 /* Payload data is invalid as CRC validation failed 248 * Clear the payload to avoid leakage of data to upper layers 249 */ 250 memset(value, 0, local_count); 251 } 252 #endif 253 return ret; 254 } 255 256 /** 257 * pm_ipi_send_sync() - Sends IPI request to the remote processor. 258 * @proc: Pointer to the processor who is initiating request. 259 * @payload: API id and call arguments to be written in IPI buffer. 260 * @value: Used to return value from IPI buffer element (optional). 261 * @count: Number of values to return in @value. 262 * 263 * Send an IPI request to the power controller and wait for it to be handled. 264 * 265 * Return: Returns status, either success or error+reason and, optionally, 266 * @value. 267 * 268 */ 269 enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc, 270 uint32_t payload[PAYLOAD_ARG_CNT], 271 uint32_t *value, size_t count) 272 { 273 enum pm_ret_status ret; 274 275 pm_ipi_lock_get(); 276 277 ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); 278 if (ret != PM_RET_SUCCESS) { 279 goto unlock; 280 } 281 282 ret = (enum pm_ret_status)(ERROR_CODE_MASK & 283 (uint32_t)(pm_ipi_buff_read(proc, value, count))); 284 285 unlock: 286 pm_ipi_lock_release(); 287 288 return ret; 289 } 290 291 void pm_ipi_irq_enable(const struct pm_proc *proc) 292 { 293 ipi_mb_enable_irq(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); 294 } 295 296 void pm_ipi_irq_clear(const struct pm_proc *proc) 297 { 298 ipi_mb_ack(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); 299 } 300 301 uint32_t pm_ipi_irq_status(const struct pm_proc *proc) 302 { 303 uint32_t ret; 304 uint32_t result = (uint32_t)PM_RET_SUCCESS; 305 306 ret = ipi_mb_enquire_status(proc->ipi->local_ipi_id, 307 proc->ipi->remote_ipi_id); 308 if ((ret & IPI_MB_STATUS_RECV_PENDING) != 0U) { 309 result = IPI_MB_STATUS_RECV_PENDING; 310 } 311 312 return result; 313 } 314 315 #if IPI_CRC_CHECK 316 uint32_t calculate_crc(uint32_t payload[PAYLOAD_ARG_CNT], uint32_t buffersize) 317 { 318 uint32_t crcinit = CRC_INIT_VALUE; 319 uint32_t order = CRC_ORDER; 320 uint32_t polynom = CRC_POLYNOM; 321 uint32_t i, j, c, bit, datain, crcmask, crchighbit; 322 uint32_t crc = crcinit; 323 324 crcmask = ((((uint32_t)1U << (order - 1U)) - 1U) << 1U) | 1U; 325 crchighbit = ((uint32_t)1U << (order - 1U)); 326 327 for (i = 0U; i < buffersize; i++) { 328 datain = mmio_read_8((unsigned long)payload + i); 329 c = datain; 330 j = 0x80U; 331 while (j != 0U) { 332 bit = crc & crchighbit; 333 crc <<= 1U; 334 if (0U != (c & j)) { 335 bit ^= crchighbit; 336 } 337 if (bit != 0U) { 338 crc ^= polynom; 339 } 340 j >>= 1U; 341 } 342 crc &= crcmask; 343 } 344 return crc; 345 } 346 #endif 347