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