1a58c4d70SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause 2a58c4d70SEtienne Carriere /* 3a58c4d70SEtienne Carriere * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. 4*4a382700SEtienne Carriere * Copyright (c) 2019-2022, Linaro Limited 5a58c4d70SEtienne Carriere */ 6a58c4d70SEtienne Carriere #include <assert.h> 7a58c4d70SEtienne Carriere #include <drivers/scmi-msg.h> 8a58c4d70SEtienne Carriere #include <drivers/scmi.h> 9a58c4d70SEtienne Carriere #include <io.h> 10a58c4d70SEtienne Carriere #include <stdbool.h> 11a58c4d70SEtienne Carriere #include <stdint.h> 12a58c4d70SEtienne Carriere #include <trace.h> 13a58c4d70SEtienne Carriere #include <util.h> 14a58c4d70SEtienne Carriere 15a58c4d70SEtienne Carriere #include "common.h" 16a58c4d70SEtienne Carriere 17a58c4d70SEtienne Carriere /** 18a58c4d70SEtienne Carriere * struct smt_header - SMT formatted header for SMT base shared memory transfer 19a58c4d70SEtienne Carriere * 20a58c4d70SEtienne Carriere * @status: Bit flags, see SMT_STATUS_* 21a58c4d70SEtienne Carriere * @flags: Bit flags, see SMT_FLAG_* 22a58c4d70SEtienne Carriere * @length: Byte size of message payload (variable) + ::message_header (32bit) 23a58c4d70SEtienne Carriere * payload: SCMI message payload data 24a58c4d70SEtienne Carriere */ 25a58c4d70SEtienne Carriere struct smt_header { 26a58c4d70SEtienne Carriere uint32_t reserved0; 27a58c4d70SEtienne Carriere uint32_t status; 28a58c4d70SEtienne Carriere uint64_t reserved1; 29a58c4d70SEtienne Carriere uint32_t flags; 30a58c4d70SEtienne Carriere uint32_t length; /* message_header + payload */ 31a58c4d70SEtienne Carriere uint32_t message_header; 32a58c4d70SEtienne Carriere uint32_t payload[]; 33a58c4d70SEtienne Carriere }; 34a58c4d70SEtienne Carriere 35a58c4d70SEtienne Carriere /* Flag set in smt_header::status when SMT does not contain pending message */ 36a58c4d70SEtienne Carriere #define SMT_STATUS_FREE BIT(0) 37a58c4d70SEtienne Carriere /* Flag set in smt_header::status when SMT reports an error */ 38a58c4d70SEtienne Carriere #define SMT_STATUS_ERROR BIT(1) 39a58c4d70SEtienne Carriere 40a58c4d70SEtienne Carriere /* Flag set in smt_header::flags when SMT uses interrupts */ 41a58c4d70SEtienne Carriere #define SMT_FLAG_INTR_ENABLED BIT(1) 42a58c4d70SEtienne Carriere 43a58c4d70SEtienne Carriere /* Bit fields packed in smt_header::message_header */ 44a58c4d70SEtienne Carriere #define SMT_MSG_ID_MASK GENMASK_32(7, 0) 45a58c4d70SEtienne Carriere #define SMT_HDR_MSG_ID(_hdr) ((_hdr) & SMT_MSG_ID_MASK) 46a58c4d70SEtienne Carriere 47a58c4d70SEtienne Carriere #define SMT_MSG_TYPE_MASK GENMASK_32(9, 8) 48a58c4d70SEtienne Carriere #define SMT_HDR_TYPE_ID(_hdr) (((_hdr) & SMT_MSG_TYPE_MASK) >> 8) 49a58c4d70SEtienne Carriere 50a58c4d70SEtienne Carriere #define SMT_MSG_PROT_ID_MASK GENMASK_32(17, 10) 51a58c4d70SEtienne Carriere #define SMT_HDR_PROT_ID(_hdr) (((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10) 52a58c4d70SEtienne Carriere 53598c63d3SEtienne Carriere static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *channel) 54a58c4d70SEtienne Carriere { 55598c63d3SEtienne Carriere if (!channel) 56ae07e7efSEtienne Carriere return NULL; 57ae07e7efSEtienne Carriere 58598c63d3SEtienne Carriere return (struct smt_header *)io_pa_or_va(&channel->shm_addr, 59c2e4eb43SAnton Rybakov sizeof(struct smt_header)); 60a58c4d70SEtienne Carriere } 61a58c4d70SEtienne Carriere 62a58c4d70SEtienne Carriere /* 63a58c4d70SEtienne Carriere * Creates a SCMI message instance in secure memory and push it in the SCMI 64a58c4d70SEtienne Carriere * message drivers. Message structure contains SCMI protocol meta-data and 65a58c4d70SEtienne Carriere * references to input payload in secure memory and output message buffer 66a58c4d70SEtienne Carriere * in shared memory. 67a58c4d70SEtienne Carriere */ 68*4a382700SEtienne Carriere void scmi_entry_smt(unsigned int channel_id, uint32_t *payload_buf) 69a58c4d70SEtienne Carriere { 70598c63d3SEtienne Carriere struct scmi_msg_channel *channel = NULL; 71a58c4d70SEtienne Carriere struct smt_header *smt_hdr = NULL; 72a58c4d70SEtienne Carriere size_t in_payload_size = 0; 73a58c4d70SEtienne Carriere uint32_t smt_status = 0; 74a58c4d70SEtienne Carriere struct scmi_msg msg = { }; 75a58c4d70SEtienne Carriere bool error = true; 76a58c4d70SEtienne Carriere 77598c63d3SEtienne Carriere channel = plat_scmi_get_channel(channel_id); 78598c63d3SEtienne Carriere if (!channel) { 79ae07e7efSEtienne Carriere DMSG("Invalid channel ID %u", channel_id); 80a58c4d70SEtienne Carriere return; 81ae07e7efSEtienne Carriere } 82a58c4d70SEtienne Carriere 83598c63d3SEtienne Carriere smt_hdr = channel_to_smt_hdr(channel); 84ae07e7efSEtienne Carriere if (!smt_hdr) { 85ae07e7efSEtienne Carriere DMSG("No shared buffer for channel ID %u", channel_id); 86ae07e7efSEtienne Carriere return; 87ae07e7efSEtienne Carriere } 88a58c4d70SEtienne Carriere 89598c63d3SEtienne Carriere if (!scmi_msg_claim_channel(channel)) { 90659a1f88SEtienne Carriere DMSG("SCMI channel %u busy", channel_id); 91a58c4d70SEtienne Carriere goto out; 92a58c4d70SEtienne Carriere } 93a58c4d70SEtienne Carriere 94ae07e7efSEtienne Carriere smt_status = READ_ONCE(smt_hdr->status); 95ae07e7efSEtienne Carriere 96a58c4d70SEtienne Carriere in_payload_size = READ_ONCE(smt_hdr->length) - 97a58c4d70SEtienne Carriere sizeof(smt_hdr->message_header); 98a58c4d70SEtienne Carriere 99d9b0a06dSEtienne Carriere if (in_payload_size > SCMI_SEC_PAYLOAD_SIZE) { 100a58c4d70SEtienne Carriere DMSG("SCMI payload too big %u", in_payload_size); 101a58c4d70SEtienne Carriere goto out; 102a58c4d70SEtienne Carriere } 103a58c4d70SEtienne Carriere 104a58c4d70SEtienne Carriere if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) { 105a58c4d70SEtienne Carriere DMSG("SCMI channel bad status 0x%x", 106a58c4d70SEtienne Carriere smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)); 107a58c4d70SEtienne Carriere goto out; 108a58c4d70SEtienne Carriere } 109a58c4d70SEtienne Carriere 110a58c4d70SEtienne Carriere /* Fill message */ 111a58c4d70SEtienne Carriere msg.in = (char *)payload_buf; 112a58c4d70SEtienne Carriere msg.in_size = in_payload_size; 113a58c4d70SEtienne Carriere msg.out = (char *)smt_hdr->payload; 114598c63d3SEtienne Carriere msg.out_size = channel->shm_size - sizeof(*smt_hdr); 115a58c4d70SEtienne Carriere 116a58c4d70SEtienne Carriere assert(msg.out && msg.out_size >= sizeof(int32_t)); 117a58c4d70SEtienne Carriere 118a58c4d70SEtienne Carriere /* Here the payload is copied in secure memory */ 119a58c4d70SEtienne Carriere memcpy(msg.in, smt_hdr->payload, in_payload_size); 120a58c4d70SEtienne Carriere 121a58c4d70SEtienne Carriere msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header); 122a58c4d70SEtienne Carriere msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header); 123659a1f88SEtienne Carriere msg.channel_id = channel_id; 124a58c4d70SEtienne Carriere 125a58c4d70SEtienne Carriere scmi_process_message(&msg); 126a58c4d70SEtienne Carriere 127a58c4d70SEtienne Carriere /* Update message length with the length of the response message */ 128a58c4d70SEtienne Carriere smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header); 129a58c4d70SEtienne Carriere 130598c63d3SEtienne Carriere scmi_msg_release_channel(channel); 131a58c4d70SEtienne Carriere error = false; 132a58c4d70SEtienne Carriere 133a58c4d70SEtienne Carriere out: 134a58c4d70SEtienne Carriere if (error) { 135a58c4d70SEtienne Carriere DMSG("SCMI error"); 136a58c4d70SEtienne Carriere smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE; 137a58c4d70SEtienne Carriere } else { 138a58c4d70SEtienne Carriere smt_hdr->status |= SMT_STATUS_FREE; 139a58c4d70SEtienne Carriere } 140a58c4d70SEtienne Carriere } 141a58c4d70SEtienne Carriere 142a58c4d70SEtienne Carriere /* Init a SMT header for a shared memory buffer: state it a free/no-error */ 143598c63d3SEtienne Carriere void scmi_smt_init_agent_channel(struct scmi_msg_channel *channel) 144a58c4d70SEtienne Carriere { 145598c63d3SEtienne Carriere struct smt_header *smt_header = channel_to_smt_hdr(channel); 146ae07e7efSEtienne Carriere 147d9b0a06dSEtienne Carriere static_assert(SCMI_SEC_PAYLOAD_SIZE + sizeof(struct smt_header) <= 148d9b0a06dSEtienne Carriere SMT_BUF_SLOT_SIZE && 149d9b0a06dSEtienne Carriere IS_ALIGNED(SCMI_SEC_PAYLOAD_SIZE, sizeof(uint32_t))); 150ae07e7efSEtienne Carriere assert(smt_header); 151a58c4d70SEtienne Carriere 152a58c4d70SEtienne Carriere memset(smt_header, 0, sizeof(*smt_header)); 153a58c4d70SEtienne Carriere smt_header->status = SMT_STATUS_FREE; 154a58c4d70SEtienne Carriere } 15548f04743SEtienne Carriere 15648f04743SEtienne Carriere void scmi_smt_set_shared_buffer(struct scmi_msg_channel *channel, void *base) 15748f04743SEtienne Carriere { 15848f04743SEtienne Carriere paddr_t p_base = 0; 15948f04743SEtienne Carriere 16048f04743SEtienne Carriere if (base) { 16148f04743SEtienne Carriere assert(!channel->shm_addr.va && !channel->shm_addr.pa); 16248f04743SEtienne Carriere p_base = virt_to_phys(base); 16348f04743SEtienne Carriere assert(p_base); 16448f04743SEtienne Carriere } 16548f04743SEtienne Carriere 16648f04743SEtienne Carriere channel->shm_addr.va = (vaddr_t)base; 16748f04743SEtienne Carriere channel->shm_addr.pa = p_base; 16848f04743SEtienne Carriere } 169