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