1a58c4d70SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause
2a58c4d70SEtienne Carriere /*
3a58c4d70SEtienne Carriere * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
44a382700SEtienne 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
channel_to_smt_hdr(struct scmi_msg_channel * channel)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 */
scmi_entry_smt(unsigned int channel_id,uint32_t * payload_buf)684a382700SEtienne 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) {
100*3f7122d9SEtienne Carriere DMSG("SCMI payload too big %zu", 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 */
scmi_smt_init_agent_channel(struct scmi_msg_channel * channel)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
scmi_smt_set_shared_buffer(struct scmi_msg_channel * channel,void * base)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