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