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