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