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