114928b88SAntonio Nino Diaz /* 2*af1ac2d7SPranav Madhu * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. 314928b88SAntonio Nino Diaz * 414928b88SAntonio Nino Diaz * SPDX-License-Identifier: BSD-3-Clause 514928b88SAntonio Nino Diaz */ 614928b88SAntonio Nino Diaz 714928b88SAntonio Nino Diaz #include <assert.h> 814928b88SAntonio Nino Diaz 914928b88SAntonio Nino Diaz #include <arch_helpers.h> 1014928b88SAntonio Nino Diaz #include <common/debug.h> 1114928b88SAntonio Nino Diaz #include <drivers/arm/css/scmi.h> 12*af1ac2d7SPranav Madhu #include <drivers/delay_timer.h> 1314928b88SAntonio Nino Diaz 1414928b88SAntonio Nino Diaz #include "scmi_private.h" 1514928b88SAntonio Nino Diaz 1614928b88SAntonio Nino Diaz #if HW_ASSISTED_COHERENCY 1714928b88SAntonio Nino Diaz #define scmi_lock_init(lock) 1814928b88SAntonio Nino Diaz #define scmi_lock_get(lock) spin_lock(lock) 1914928b88SAntonio Nino Diaz #define scmi_lock_release(lock) spin_unlock(lock) 2014928b88SAntonio Nino Diaz #else 2114928b88SAntonio Nino Diaz #define scmi_lock_init(lock) bakery_lock_init(lock) 2214928b88SAntonio Nino Diaz #define scmi_lock_get(lock) bakery_lock_get(lock) 2314928b88SAntonio Nino Diaz #define scmi_lock_release(lock) bakery_lock_release(lock) 2414928b88SAntonio Nino Diaz #endif 2514928b88SAntonio Nino Diaz 2614928b88SAntonio Nino Diaz 2714928b88SAntonio Nino Diaz /* 2814928b88SAntonio Nino Diaz * Private helper function to get exclusive access to SCMI channel. 2914928b88SAntonio Nino Diaz */ 3014928b88SAntonio Nino Diaz void scmi_get_channel(scmi_channel_t *ch) 3114928b88SAntonio Nino Diaz { 3214928b88SAntonio Nino Diaz assert(ch->lock); 3314928b88SAntonio Nino Diaz scmi_lock_get(ch->lock); 3414928b88SAntonio Nino Diaz 3514928b88SAntonio Nino Diaz /* Make sure any previous command has finished */ 3614928b88SAntonio Nino Diaz assert(SCMI_IS_CHANNEL_FREE( 3714928b88SAntonio Nino Diaz ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status)); 3814928b88SAntonio Nino Diaz } 3914928b88SAntonio Nino Diaz 4014928b88SAntonio Nino Diaz /* 4114928b88SAntonio Nino Diaz * Private helper function to transfer ownership of channel from AP to SCP. 4214928b88SAntonio Nino Diaz */ 4314928b88SAntonio Nino Diaz void scmi_send_sync_command(scmi_channel_t *ch) 4414928b88SAntonio Nino Diaz { 4514928b88SAntonio Nino Diaz mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); 4614928b88SAntonio Nino Diaz 4714928b88SAntonio Nino Diaz SCMI_MARK_CHANNEL_BUSY(mbx_mem->status); 4814928b88SAntonio Nino Diaz 4914928b88SAntonio Nino Diaz /* 5014928b88SAntonio Nino Diaz * Ensure that any write to the SCMI payload area is seen by SCP before 5114928b88SAntonio Nino Diaz * we write to the doorbell register. If these 2 writes were reordered 5214928b88SAntonio Nino Diaz * by the CPU then SCP would read stale payload data 5314928b88SAntonio Nino Diaz */ 5414928b88SAntonio Nino Diaz dmbst(); 5514928b88SAntonio Nino Diaz 5614928b88SAntonio Nino Diaz ch->info->ring_doorbell(ch->info); 5714928b88SAntonio Nino Diaz /* 5814928b88SAntonio Nino Diaz * Ensure that the write to the doorbell register is ordered prior to 5914928b88SAntonio Nino Diaz * checking whether the channel is free. 6014928b88SAntonio Nino Diaz */ 6114928b88SAntonio Nino Diaz dmbsy(); 6214928b88SAntonio Nino Diaz 6314928b88SAntonio Nino Diaz /* Wait for channel to be free */ 64*af1ac2d7SPranav Madhu while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status)) { 65*af1ac2d7SPranav Madhu if (ch->info->delay != 0) 66*af1ac2d7SPranav Madhu udelay(ch->info->delay); 67*af1ac2d7SPranav Madhu } 6814928b88SAntonio Nino Diaz 6914928b88SAntonio Nino Diaz /* 7014928b88SAntonio Nino Diaz * Ensure that any read to the SCMI payload area is done after reading 7114928b88SAntonio Nino Diaz * mailbox status. If these 2 reads were reordered then the CPU would 7214928b88SAntonio Nino Diaz * read invalid payload data 7314928b88SAntonio Nino Diaz */ 7414928b88SAntonio Nino Diaz dmbld(); 7514928b88SAntonio Nino Diaz } 7614928b88SAntonio Nino Diaz 7714928b88SAntonio Nino Diaz /* 7814928b88SAntonio Nino Diaz * Private helper function to release exclusive access to SCMI channel. 7914928b88SAntonio Nino Diaz */ 8014928b88SAntonio Nino Diaz void scmi_put_channel(scmi_channel_t *ch) 8114928b88SAntonio Nino Diaz { 8214928b88SAntonio Nino Diaz /* Make sure any previous command has finished */ 8314928b88SAntonio Nino Diaz assert(SCMI_IS_CHANNEL_FREE( 8414928b88SAntonio Nino Diaz ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status)); 8514928b88SAntonio Nino Diaz 8614928b88SAntonio Nino Diaz assert(ch->lock); 8714928b88SAntonio Nino Diaz scmi_lock_release(ch->lock); 8814928b88SAntonio Nino Diaz } 8914928b88SAntonio Nino Diaz 9014928b88SAntonio Nino Diaz /* 9114928b88SAntonio Nino Diaz * API to query the SCMI protocol version. 9214928b88SAntonio Nino Diaz */ 9314928b88SAntonio Nino Diaz int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version) 9414928b88SAntonio Nino Diaz { 9514928b88SAntonio Nino Diaz mailbox_mem_t *mbx_mem; 96bde2836fSAmbroise Vincent unsigned int token = 0; 97bde2836fSAmbroise Vincent int ret; 9814928b88SAntonio Nino Diaz scmi_channel_t *ch = (scmi_channel_t *)p; 9914928b88SAntonio Nino Diaz 10014928b88SAntonio Nino Diaz validate_scmi_channel(ch); 10114928b88SAntonio Nino Diaz 10214928b88SAntonio Nino Diaz scmi_get_channel(ch); 10314928b88SAntonio Nino Diaz 10414928b88SAntonio Nino Diaz mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); 10514928b88SAntonio Nino Diaz mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG, 10614928b88SAntonio Nino Diaz token); 10714928b88SAntonio Nino Diaz mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN; 10814928b88SAntonio Nino Diaz mbx_mem->flags = SCMI_FLAG_RESP_POLL; 10914928b88SAntonio Nino Diaz 11014928b88SAntonio Nino Diaz scmi_send_sync_command(ch); 11114928b88SAntonio Nino Diaz 11214928b88SAntonio Nino Diaz /* Get the return values */ 11314928b88SAntonio Nino Diaz SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version); 11414928b88SAntonio Nino Diaz assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN); 11514928b88SAntonio Nino Diaz assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); 11614928b88SAntonio Nino Diaz 11714928b88SAntonio Nino Diaz scmi_put_channel(ch); 11814928b88SAntonio Nino Diaz 11914928b88SAntonio Nino Diaz return ret; 12014928b88SAntonio Nino Diaz } 12114928b88SAntonio Nino Diaz 12214928b88SAntonio Nino Diaz /* 12314928b88SAntonio Nino Diaz * API to query the protocol message attributes for a SCMI protocol. 12414928b88SAntonio Nino Diaz */ 12514928b88SAntonio Nino Diaz int scmi_proto_msg_attr(void *p, uint32_t proto_id, 12614928b88SAntonio Nino Diaz uint32_t command_id, uint32_t *attr) 12714928b88SAntonio Nino Diaz { 12814928b88SAntonio Nino Diaz mailbox_mem_t *mbx_mem; 129bde2836fSAmbroise Vincent unsigned int token = 0; 130bde2836fSAmbroise Vincent int ret; 13114928b88SAntonio Nino Diaz scmi_channel_t *ch = (scmi_channel_t *)p; 13214928b88SAntonio Nino Diaz 13314928b88SAntonio Nino Diaz validate_scmi_channel(ch); 13414928b88SAntonio Nino Diaz 13514928b88SAntonio Nino Diaz scmi_get_channel(ch); 13614928b88SAntonio Nino Diaz 13714928b88SAntonio Nino Diaz mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); 13814928b88SAntonio Nino Diaz mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, 13914928b88SAntonio Nino Diaz SCMI_PROTO_MSG_ATTR_MSG, token); 14014928b88SAntonio Nino Diaz mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN; 14114928b88SAntonio Nino Diaz mbx_mem->flags = SCMI_FLAG_RESP_POLL; 14214928b88SAntonio Nino Diaz SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id); 14314928b88SAntonio Nino Diaz 14414928b88SAntonio Nino Diaz scmi_send_sync_command(ch); 14514928b88SAntonio Nino Diaz 14614928b88SAntonio Nino Diaz /* Get the return values */ 14714928b88SAntonio Nino Diaz SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr); 14814928b88SAntonio Nino Diaz assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN); 14914928b88SAntonio Nino Diaz assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); 15014928b88SAntonio Nino Diaz 15114928b88SAntonio Nino Diaz scmi_put_channel(ch); 15214928b88SAntonio Nino Diaz 15314928b88SAntonio Nino Diaz return ret; 15414928b88SAntonio Nino Diaz } 15514928b88SAntonio Nino Diaz 15614928b88SAntonio Nino Diaz /* 15714928b88SAntonio Nino Diaz * SCMI Driver initialization API. Returns initialized channel on success 15814928b88SAntonio Nino Diaz * or NULL on error. The return type is an opaque void pointer. 15914928b88SAntonio Nino Diaz */ 16014928b88SAntonio Nino Diaz void *scmi_init(scmi_channel_t *ch) 16114928b88SAntonio Nino Diaz { 16214928b88SAntonio Nino Diaz uint32_t version; 16314928b88SAntonio Nino Diaz int ret; 16414928b88SAntonio Nino Diaz 16514928b88SAntonio Nino Diaz assert(ch && ch->info); 16614928b88SAntonio Nino Diaz assert(ch->info->db_reg_addr); 16714928b88SAntonio Nino Diaz assert(ch->info->db_modify_mask); 16814928b88SAntonio Nino Diaz assert(ch->info->db_preserve_mask); 16914928b88SAntonio Nino Diaz assert(ch->info->ring_doorbell != NULL); 17014928b88SAntonio Nino Diaz 17114928b88SAntonio Nino Diaz assert(ch->lock); 17214928b88SAntonio Nino Diaz 17314928b88SAntonio Nino Diaz scmi_lock_init(ch->lock); 17414928b88SAntonio Nino Diaz 17514928b88SAntonio Nino Diaz ch->is_initialized = 1; 17614928b88SAntonio Nino Diaz 17714928b88SAntonio Nino Diaz ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version); 17814928b88SAntonio Nino Diaz if (ret != SCMI_E_SUCCESS) { 17905579dafSHeyi Guo WARN("SCMI power domain protocol version message failed\n"); 18014928b88SAntonio Nino Diaz goto error; 18114928b88SAntonio Nino Diaz } 18214928b88SAntonio Nino Diaz 18314928b88SAntonio Nino Diaz if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) { 18405579dafSHeyi Guo WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x\n", 18514928b88SAntonio Nino Diaz version, SCMI_PWR_DMN_PROTO_VER); 18614928b88SAntonio Nino Diaz goto error; 18714928b88SAntonio Nino Diaz } 18814928b88SAntonio Nino Diaz 18914928b88SAntonio Nino Diaz VERBOSE("SCMI power domain protocol version 0x%x detected\n", version); 19014928b88SAntonio Nino Diaz 19114928b88SAntonio Nino Diaz ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version); 19214928b88SAntonio Nino Diaz if ((ret != SCMI_E_SUCCESS)) { 19305579dafSHeyi Guo WARN("SCMI system power protocol version message failed\n"); 19414928b88SAntonio Nino Diaz goto error; 19514928b88SAntonio Nino Diaz } 19614928b88SAntonio Nino Diaz 19714928b88SAntonio Nino Diaz if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) { 19805579dafSHeyi Guo WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x\n", 19914928b88SAntonio Nino Diaz version, SCMI_SYS_PWR_PROTO_VER); 20014928b88SAntonio Nino Diaz goto error; 20114928b88SAntonio Nino Diaz } 20214928b88SAntonio Nino Diaz 20314928b88SAntonio Nino Diaz VERBOSE("SCMI system power management protocol version 0x%x detected\n", 20414928b88SAntonio Nino Diaz version); 20514928b88SAntonio Nino Diaz 20614928b88SAntonio Nino Diaz INFO("SCMI driver initialized\n"); 20714928b88SAntonio Nino Diaz 20814928b88SAntonio Nino Diaz return (void *)ch; 20914928b88SAntonio Nino Diaz 21014928b88SAntonio Nino Diaz error: 21114928b88SAntonio Nino Diaz ch->is_initialized = 0; 21214928b88SAntonio Nino Diaz return NULL; 21314928b88SAntonio Nino Diaz } 214