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