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