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