Home | History | Annotate | Download | only in scmi
      1 /*
      2  * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
      3  *
      4  * SPDX-License-Identifier: BSD-3-Clause
      5  */
      6 
      7 #include <arch_helpers.h>
      8 #include <assert.h>
      9 #include <debug.h>
     10 #include "scmi.h"
     11 #include "scmi_private.h"
     12 
     13 /*
     14  * Private helper function to get exclusive access to SCMI channel.
     15  */
     16 void scmi_get_channel(scmi_channel_t *ch)
     17 {
     18 	assert(ch->lock);
     19 	bakery_lock_get(ch->lock);
     20 
     21 	/* Make sure any previous command has finished */
     22 	assert(SCMI_IS_CHANNEL_FREE(
     23 			((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
     24 }
     25 
     26 /*
     27  * Private helper function to transfer ownership of channel from AP to SCP.
     28  */
     29 void scmi_send_sync_command(scmi_channel_t *ch)
     30 {
     31 	mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
     32 
     33 	SCMI_MARK_CHANNEL_BUSY(mbx_mem->status);
     34 
     35 	/*
     36 	 * Ensure that any write to the SCMI payload area is seen by SCP before
     37 	 * we write to the doorbell register. If these 2 writes were reordered
     38 	 * by the CPU then SCP would read stale payload data
     39 	 */
     40 	dmbst();
     41 
     42 	SCMI_RING_DOORBELL(ch->info->db_reg_addr, ch->info->db_modify_mask,
     43 					ch->info->db_preserve_mask);
     44 
     45 	/*
     46 	 * Ensure that the write to the doorbell register is ordered prior to
     47 	 * checking whether the channel is free.
     48 	 */
     49 	dmbsy();
     50 
     51 	/* Wait for channel to be free */
     52 	while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status))
     53 		;
     54 
     55 	/*
     56 	 * Ensure that any read to the SCMI payload area is done after reading
     57 	 * mailbox status. If these 2 reads were reordered then the CPU would
     58 	 * read invalid payload data
     59 	 */
     60 	dmbld();
     61 }
     62 
     63 /*
     64  * Private helper function to release exclusive access to SCMI channel.
     65  */
     66 void scmi_put_channel(scmi_channel_t *ch)
     67 {
     68 	/* Make sure any previous command has finished */
     69 	assert(SCMI_IS_CHANNEL_FREE(
     70 			((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status));
     71 
     72 	assert(ch->lock);
     73 	bakery_lock_release(ch->lock);
     74 }
     75 
     76 /*
     77  * API to query the SCMI protocol version.
     78  */
     79 int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version)
     80 {
     81 	mailbox_mem_t *mbx_mem;
     82 	int token = 0, ret;
     83 	scmi_channel_t *ch = (scmi_channel_t *)p;
     84 
     85 	validate_scmi_channel(ch);
     86 
     87 	scmi_get_channel(ch);
     88 
     89 	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
     90 	mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG,
     91 							token);
     92 	mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN;
     93 	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
     94 
     95 	scmi_send_sync_command(ch);
     96 
     97 	/* Get the return values */
     98 	SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version);
     99 	assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN);
    100 	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
    101 
    102 	scmi_put_channel(ch);
    103 
    104 	return ret;
    105 }
    106 
    107 /*
    108  * API to query the protocol message attributes for a SCMI protocol.
    109  */
    110 int scmi_proto_msg_attr(void *p, uint32_t proto_id,
    111 		uint32_t command_id, uint32_t *attr)
    112 {
    113 	mailbox_mem_t *mbx_mem;
    114 	int token = 0, ret;
    115 	scmi_channel_t *ch = (scmi_channel_t *)p;
    116 
    117 	validate_scmi_channel(ch);
    118 
    119 	scmi_get_channel(ch);
    120 
    121 	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
    122 	mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id,
    123 				SCMI_PROTO_MSG_ATTR_MSG, token);
    124 	mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN;
    125 	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
    126 	SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id);
    127 
    128 	scmi_send_sync_command(ch);
    129 
    130 	/* Get the return values */
    131 	SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr);
    132 	assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN);
    133 	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
    134 
    135 	scmi_put_channel(ch);
    136 
    137 	return ret;
    138 }
    139 
    140 /*
    141  * SCMI Driver initialization API. Returns initialized channel on success
    142  * or NULL on error. The return type is an opaque void pointer.
    143  */
    144 void *scmi_init(scmi_channel_t *ch)
    145 {
    146 	uint32_t version;
    147 	int ret;
    148 
    149 	assert(ch && ch->info);
    150 	assert(ch->info->db_reg_addr);
    151 	assert(ch->info->db_modify_mask);
    152 	assert(ch->info->db_preserve_mask);
    153 
    154 	assert(ch->lock);
    155 
    156 	bakery_lock_init(ch->lock);
    157 
    158 	ch->is_initialized = 1;
    159 
    160 	ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version);
    161 	if (ret != SCMI_E_SUCCESS) {
    162 		WARN("SCMI power domain protocol version message failed");
    163 		goto error;
    164 	}
    165 
    166 	if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) {
    167 		WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x",
    168 			version, SCMI_PWR_DMN_PROTO_VER);
    169 		goto error;
    170 	}
    171 
    172 	VERBOSE("SCMI power domain protocol version 0x%x detected\n", version);
    173 
    174 	ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version);
    175 	if ((ret != SCMI_E_SUCCESS)) {
    176 		WARN("SCMI system power protocol version message failed");
    177 		goto error;
    178 	}
    179 
    180 	if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) {
    181 		WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x",
    182 			version, SCMI_SYS_PWR_PROTO_VER);
    183 		goto error;
    184 	}
    185 
    186 	VERBOSE("SCMI system power management protocol version 0x%x detected\n",
    187 						version);
    188 
    189 	INFO("SCMI driver initialized\n");
    190 
    191 	return (void *)ch;
    192 
    193 error:
    194 	ch->is_initialized = 0;
    195 	return NULL;
    196 }
    197