1 /* 2 * Copyright (c) 2017-2018, 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 int token = 0, ret; 94 scmi_channel_t *ch = (scmi_channel_t *)p; 95 96 validate_scmi_channel(ch); 97 98 scmi_get_channel(ch); 99 100 mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); 101 mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG, 102 token); 103 mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN; 104 mbx_mem->flags = SCMI_FLAG_RESP_POLL; 105 106 scmi_send_sync_command(ch); 107 108 /* Get the return values */ 109 SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version); 110 assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN); 111 assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); 112 113 scmi_put_channel(ch); 114 115 return ret; 116 } 117 118 /* 119 * API to query the protocol message attributes for a SCMI protocol. 120 */ 121 int scmi_proto_msg_attr(void *p, uint32_t proto_id, 122 uint32_t command_id, uint32_t *attr) 123 { 124 mailbox_mem_t *mbx_mem; 125 int token = 0, ret; 126 scmi_channel_t *ch = (scmi_channel_t *)p; 127 128 validate_scmi_channel(ch); 129 130 scmi_get_channel(ch); 131 132 mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); 133 mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, 134 SCMI_PROTO_MSG_ATTR_MSG, token); 135 mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN; 136 mbx_mem->flags = SCMI_FLAG_RESP_POLL; 137 SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id); 138 139 scmi_send_sync_command(ch); 140 141 /* Get the return values */ 142 SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr); 143 assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN); 144 assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); 145 146 scmi_put_channel(ch); 147 148 return ret; 149 } 150 151 /* 152 * SCMI Driver initialization API. Returns initialized channel on success 153 * or NULL on error. The return type is an opaque void pointer. 154 */ 155 void *scmi_init(scmi_channel_t *ch) 156 { 157 uint32_t version; 158 int ret; 159 160 assert(ch && ch->info); 161 assert(ch->info->db_reg_addr); 162 assert(ch->info->db_modify_mask); 163 assert(ch->info->db_preserve_mask); 164 assert(ch->info->ring_doorbell != NULL); 165 166 assert(ch->lock); 167 168 scmi_lock_init(ch->lock); 169 170 ch->is_initialized = 1; 171 172 ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version); 173 if (ret != SCMI_E_SUCCESS) { 174 WARN("SCMI power domain protocol version message failed"); 175 goto error; 176 } 177 178 if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) { 179 WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x", 180 version, SCMI_PWR_DMN_PROTO_VER); 181 goto error; 182 } 183 184 VERBOSE("SCMI power domain protocol version 0x%x detected\n", version); 185 186 ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version); 187 if ((ret != SCMI_E_SUCCESS)) { 188 WARN("SCMI system power protocol version message failed"); 189 goto error; 190 } 191 192 if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) { 193 WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x", 194 version, SCMI_SYS_PWR_PROTO_VER); 195 goto error; 196 } 197 198 VERBOSE("SCMI system power management protocol version 0x%x detected\n", 199 version); 200 201 INFO("SCMI driver initialized\n"); 202 203 return (void *)ch; 204 205 error: 206 ch->is_initialized = 0; 207 return NULL; 208 } 209