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