blob: a3075dfa0d41cfee3a6a96a955d74e36f6d0932c [file] [log] [blame]
Jamie Fox278c0382024-10-01 18:01:46 +01001/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
4 *
5 */
6
7#include "psa/service.h"
8#include "psa_manifest/scmi_comms.h"
9#include "scmi_comms.h"
10#include "scmi_hal.h"
11#include "scmi_protocol.h"
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +000012#include "tfm_log_unpriv.h"
Jamie Fox278c0382024-10-01 18:01:46 +010013
14#include <assert.h>
15#include <stddef.h>
16#include <stdint.h>
17#include <string.h>
18
19#define SCMI_MESSAGE_HEADER_MESSAGE_ID_POS 0
20#define SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK \
21 (UINT32_C(0xFF) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS)
22
23#define SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS 8
24#define SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK \
25 (UINT32_C(0x3) << SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS)
26
27#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS 10
28#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK \
29 (UINT32_C(0xFF) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS)
30
31#define SCMI_MESSAGE_HEADER_TOKEN_POS 18
32#define SCMI_MESSAGE_HEADER_TOKEN_MASK \
33 (UINT32_C(0x3FF) << SCMI_MESSAGE_HEADER_TOKEN_POS)
34
35static uint32_t scmi_message_header(uint8_t message_id, uint8_t message_type,
36 uint8_t protocol_id, uint8_t token)
37{
38 return (((uint32_t)message_id << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS) &
39 SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) |
40 (((uint32_t)message_type << SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS) &
41 SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK) |
42 (((uint32_t)protocol_id << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS) &
43 SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) |
44 (((uint32_t)token << SCMI_MESSAGE_HEADER_TOKEN_POS) &
45 SCMI_MESSAGE_HEADER_TOKEN_MASK);
46}
47
48#define TRANSPORT_BUFFER_STATUS_FREE_POS 0
49#define TRANSPORT_BUFFER_STATUS_FREE_MASK \
50 (UINT32_C(0x1) << TRANSPORT_BUFFER_STATUS_FREE_POS)
51
52#define TRANSPORT_BUFFER_STATUS_ERROR_POS 1
53#define TRANSPORT_BUFFER_STATUS_ERROR_MASK \
54 (UINT32_C(0x1) << TRANSPORT_BUFFER_STATUS_ERROR_POS)
55
56#define TRANSPORT_BUFFER_FLAGS_INTERRUPT_POS 0
57#define TRANSPORT_BUFFER_FLAGS_INTERRUPT_MASK \
58 (UINT32_C(0x1) << TRANSPORT_BUFFER_FLAGS_INTERRUPT_POS)
59
60#define TRANSPORT_BUFFER_MAX_LENGTH \
61 (SCP_SHARED_MEMORY_SIZE - offsetof(struct transport_buffer_t, message_header))
62
63/**
64 * \brief Shared memory area layout used for sending & receiving messages
65 */
66struct transport_buffer_t {
67 uint32_t reserved0; /**< Reserved, must be zero */
68 volatile uint32_t status; /**< Channel status */
69 uint64_t reserved1; /**< Implementation defined field */
70 uint32_t flags; /**< Channel flags */
71 volatile uint32_t length; /**< Length in bytes of the message header and payload */
72 uint32_t message_header; /**< Message header */
73 uint32_t message_payload[]; /**< Message payload */
74};
75
76/**
77 * \brief Structure representing an SCMI message.
78 */
79struct scmi_message_t {
80 uint32_t header;
81 uint32_t payload[(TRANSPORT_BUFFER_MAX_LENGTH - sizeof(uint32_t)) / sizeof(uint32_t)];
82 uint32_t payload_len;
83};
84
85static struct transport_buffer_t *const shared_memory =
86 (struct transport_buffer_t *)SCP_SHARED_MEMORY_BASE;
87
88/**
89 * \brief Initialize the SCMI transport layer.
90 *
91 * \return Error value as defined by scmi_comms_err_t.
92 */
93static scmi_comms_err_t transport_init(void)
94{
95 scmi_comms_err_t err;
96
97 err = scmi_hal_doorbell_init();
98 if (err != SCMI_COMMS_SUCCESS) {
99 return err;
100 }
101
102 err = scmi_hal_shared_memory_init();
103 if (err != SCMI_COMMS_SUCCESS) {
104 return err;
105 }
106
107 shared_memory->flags = 0;
108 shared_memory->length = 0;
109 shared_memory->status = TRANSPORT_BUFFER_STATUS_FREE_MASK;
110
111 return SCMI_COMMS_SUCCESS;
112}
113
114/**
115 * \brief Read a message from the shared memory to the local buffer.
116 *
117 * \param[out] msg SCMI message
118 *
119 * \return Error value as defined by scmi_comms_err_t.
120 */
121static scmi_comms_err_t transport_receive(struct scmi_message_t *msg)
122{
123 scmi_comms_err_t err = scmi_hal_doorbell_clear();
124 if (err != SCMI_COMMS_SUCCESS) {
125 return err;
126 }
127
128 uint32_t length = shared_memory->length;
129
130 if ((length < sizeof(shared_memory->message_header)) ||
131 (length > TRANSPORT_BUFFER_MAX_LENGTH)) {
132 return SCMI_COMMS_INVALID_ARGUMENT;
133 }
134
135 memcpy(msg, &shared_memory->message_header, length);
136 msg->payload_len = length - sizeof(msg->header);
137
138 return SCMI_COMMS_SUCCESS;
139}
140
141/**
142 * \brief Write a response from the local buffer to the shared memory and signal
143 * completion.
144 *
145 * \param[in] msg SCMI message
146 */
147static void transport_respond(const struct scmi_message_t *msg)
148{
149 /* Populate shared memory area */
150 memcpy(shared_memory->message_payload, msg->payload, msg->payload_len);
151 shared_memory->length = msg->payload_len + sizeof(msg->header);
152
153 /* Mark channel as free */
154 shared_memory->status |= TRANSPORT_BUFFER_STATUS_FREE_MASK;
155
156 /* TODO: Issue completion interrupt */
157}
158
159/**
160 * \brief Write a message from the local buffer to the shared memory and wait
161 * for a response.
162 *
163 * \param[in] msg SCMI message
164 *
165 * \return Error value as defined by scmi_comms_err_t.
166 */
167static int32_t transport_send(const struct scmi_message_t *msg)
168{
169 int32_t err;
170 uint32_t length = msg->payload_len + sizeof(msg->header);
171
172 if (length > TRANSPORT_BUFFER_MAX_LENGTH) {
173 return SCMI_COMMS_INVALID_ARGUMENT;
174 }
175
176 /* Wait for channel to be free */
177 /* TODO: Timeout */
178 while (!(shared_memory->status & TRANSPORT_BUFFER_STATUS_FREE_MASK));
179
180 /* Populate shared memory area */
181 memcpy(&shared_memory->message_header, msg, length);
182 shared_memory->length = length;
183
184 /* Mark channel as busy */
185 shared_memory->status &= ~TRANSPORT_BUFFER_STATUS_FREE_MASK;
186
187 /* Ring doorbell */
188 err = scmi_hal_doorbell_ring();
189 if (err != SCMI_COMMS_SUCCESS) {
190 return err;
191 }
192
193 /* Wait until channel is free */
194 /* TODO: Wait for completion interrupt */
195 while (!(shared_memory->status & TRANSPORT_BUFFER_STATUS_FREE_MASK));
196
197 return SCMI_COMMS_SUCCESS;
198}
199
200/**
201 * \brief Create an SCMI response containing only status.
202 *
203 * \param[out] msg SCMI message
204 * \param[in] status SCMI status
205 */
206static void scmi_response_status(struct scmi_message_t *msg, int32_t status)
207{
208 msg->payload[0] = status;
209 msg->payload_len = sizeof(msg->payload[0]);
210}
211
212/**
213 * \brief Create an SCMI system power state notify message.
214 *
215 * \param[out] msg SCMI message
216 */
217static void scmi_message_sys_power_state_notify(struct scmi_message_t *msg)
218{
219 msg->header =
220 scmi_message_header(SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFY,
221 SCMI_MESSAGE_TYPE_COMMAND,
222 SCMI_PROTOCOL_ID_SYS_POWER_STATE,
223 0);
224
225 assert(sizeof(struct scmi_sys_power_state_notify_t) <= sizeof(msg->payload));
226
227 memcpy(msg->payload,
228 &(struct scmi_sys_power_state_notify_t) { .notify_enable = 1 },
229 sizeof(struct scmi_sys_power_state_notify_t));
230
231 msg->payload_len = sizeof(struct scmi_sys_power_state_notify_t);
232}
233
234/**
235 * \brief Handle an SCMI system power state set message.
236 *
237 * \param[in,out] msg SCMI message
238 */
239static void scmi_handle_sys_power_state_set(struct scmi_message_t *msg)
240{
241 if (msg->payload_len != sizeof(struct scmi_sys_power_state_set_t)) {
242 scmi_response_status(msg, SCMI_STATUS_PROTOCOL_ERROR);
243 return;
244 }
245
246 struct scmi_sys_power_state_set_t *pwr_set =
247 (struct scmi_sys_power_state_set_t *)msg->payload;
248
249 int32_t status = scmi_hal_sys_power_state(0, pwr_set->flags, pwr_set->system_state);
250
251 scmi_response_status(msg, status);
252}
253
254/**
255 * \brief Handle an SCMI system power state notification.
256 *
257 * \param[in,out] msg SCMI message
258 */
259static void scmi_handle_sys_power_state_notifier(struct scmi_message_t *msg)
260{
261 if (msg->payload_len != sizeof(struct scmi_sys_power_state_notifier_t)) {
262 /* No return values for notifications */
263 msg->payload_len = 0;
264 return;
265 }
266
267 struct scmi_sys_power_state_notifier_t *pwr_not =
268 (struct scmi_sys_power_state_notifier_t *)msg->payload;
269
270 scmi_hal_sys_power_state(pwr_not->agent_id, pwr_not->flags, pwr_not->system_state);
271
272 /* No return values for notifications */
273 msg->payload_len = 0;
274}
275
276/**
277 * \brief Handle a received SCMI message.
278 *
279 * \param[in,out] msg SCMI message
280 */
281static void scmi_handle_message(struct scmi_message_t *msg)
282{
283 uint8_t message_id = (msg->header & SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK)
284 >> SCMI_MESSAGE_HEADER_MESSAGE_ID_POS;
285 uint8_t message_type = (msg->header & SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK)
286 >> SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS;
287 uint8_t protocol_id = (msg->header & SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK)
288 >> SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS;
289
290 if (protocol_id == SCMI_PROTOCOL_ID_SYS_POWER_STATE) {
291 if (message_type == SCMI_MESSAGE_TYPE_COMMAND &&
292 message_id == SCMI_MESSAGE_ID_SYS_POWER_STATE_SET) {
293 scmi_handle_sys_power_state_set(msg);
294 return;
295 } else if (message_type == SCMI_MESSAGE_TYPE_NOTIFICATION &&
296 message_id == SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFIER) {
297 scmi_handle_sys_power_state_notifier(msg);
298 return;
299 }
300 }
301
302 /* Any command that is sent with an unknown protocol_id or message_id must
303 * be responded to with a return value of NOT_SUPPORTED as the status code.
304 */
305 scmi_response_status(msg, SCMI_STATUS_NOT_SUPPORTED);
306}
307
308/**
309 * \brief Handle a received SCMI response.
310 *
311 * \param[in] msg SCMI message
312 *
313 * \return SCMI status of the response.
314 */
315static int32_t scmi_handle_response(const struct scmi_message_t *msg)
316{
317 /* Only simple status responses are currently supported */
318 if (msg->payload_len != sizeof(int32_t)) {
319 return SCMI_STATUS_PROTOCOL_ERROR;
320 }
321 return (int32_t)msg->payload[0];
322}
323
324/**
325 * \brief Subscribe to system power state notifications.
326 *
327 * \param[in,out] msg SCMI message
328 *
329 * \return Error value as defined by scmi_comms_err_t.
330 */
331static scmi_comms_err_t scmi_comms_notification_subscribe(struct scmi_message_t *msg)
332{
333 scmi_comms_err_t err;
334
335 scmi_message_sys_power_state_notify(msg);
336
337 err = transport_send(msg);
338 if (err != SCMI_COMMS_SUCCESS) {
339 return err;
340 }
341
342 err = transport_receive(msg);
343 if (err != SCMI_COMMS_SUCCESS) {
344 return err;
345 }
346
347 return (scmi_handle_response(msg) == SCMI_STATUS_SUCCESS) ?
348 SCMI_COMMS_SUCCESS : SCMI_COMMS_GENERIC_ERROR;
349}
350
351void scmi_comms_main(void)
352{
353 scmi_comms_err_t err;
354 struct scmi_message_t agent_buf;
355
356 err = transport_init();
357 if (err != SCMI_COMMS_SUCCESS) {
358 psa_panic();
359 }
360
361 psa_irq_enable(SCP_DOORBELL_SIGNAL);
362
363 /* First wait for SCP to signal that it is ready to receive commands. */
364 (void)psa_wait(SCP_DOORBELL_SIGNAL, PSA_BLOCK);
365
366 err = scmi_hal_doorbell_clear();
367 if (err != SCMI_COMMS_SUCCESS) {
368 psa_panic();
369 }
370
371 psa_eoi(SCP_DOORBELL_SIGNAL);
372
373 /* Subscribe to notifications. If it fails, the agent will still listen for
374 * SCMI commands.
375 */
376 err = scmi_comms_notification_subscribe(&agent_buf);
377 if (err == SCMI_COMMS_SUCCESS) {
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +0000378 INFO_UNPRIV_RAW("SCMI Comms subscribed to power state notifications\n");
Jamie Fox278c0382024-10-01 18:01:46 +0100379 } else {
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +0000380 ERROR_UNPRIV_RAW("SCMI Comms failed to subscribe to power state notifications\n");
Jamie Fox278c0382024-10-01 18:01:46 +0100381 }
382
383 while (1) {
384 (void)psa_wait(SCP_DOORBELL_SIGNAL, PSA_BLOCK);
385
386 err = transport_receive(&agent_buf);
387 if (err == SCMI_COMMS_SUCCESS) {
388 scmi_handle_message(&agent_buf);
389 } else {
390 scmi_response_status(&agent_buf, SCMI_STATUS_PROTOCOL_ERROR);
391 }
392 transport_respond(&agent_buf);
393
394 psa_eoi(SCP_DOORBELL_SIGNAL);
395 }
396}