1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Message Handling Unit (MHU) v2 Device Driver.
9 */
10
11 #include <mhu2.h>
12
13 #include <mod_mhu2.h>
14 #include <mod_transport.h>
15
16 #include <fwk_assert.h>
17 #include <fwk_id.h>
18 #include <fwk_interrupt.h>
19 #include <fwk_log.h>
20 #include <fwk_mm.h>
21 #include <fwk_module.h>
22 #include <fwk_module_idx.h>
23 #include <fwk_status.h>
24
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28
29 #define MHU_SLOT_COUNT_MAX 32
30
31 #if defined(BUILD_HAS_INBAND_MSG_SUPPORT)
32 /* MHUv2 channel status register size in bytes */
33 # define MHU_CHANNEL_STATUS_REGISTER_WIDTH 4
34
35 /*
36 * For, transferring in-band messages, we require at least 9 MHUv2 channels
37 * excluding the payload. Out of 9 channels, one is used for doorbell and 8
38 * channels are used for transferring the in-band message in the same layout
39 * as a shared memory which includes implementation defined reserved field,
40 * channel status, channel flags, length, etc.
41 *
42 */
43 const uint8_t min_channels_required = 9;
44 #endif
45
46 struct mhu2_bound_channel {
47 fwk_id_t id;
48 const struct mod_transport_driver_input_api *driver_input_api;
49 };
50
51 /* MHU channel context */
52 struct mhu2_channel_ctx {
53 /* Pointer to the channel configuration */
54 const struct mod_mhu2_channel_config *config;
55
56 /* Pointer to send register set */
57 struct mhu2_send_reg *send;
58
59 /* Pointers to channel-specific register sets */
60 struct mhu2_send_channel_reg *send_channel;
61 struct mhu2_recv_channel_reg *recv_channel;
62
63 /* Number of slots (represented by sub-elements) */
64 unsigned int slot_count;
65
66 /* Mask of slots that are bound to an TRANSPORT channel */
67 uint32_t bound_slots;
68
69 /* Table of channels bound to the channel */
70 struct mhu2_bound_channel *bound_channels_table;
71 };
72
73 /* MHU v2 context */
74 static struct mhu2_ctx {
75 /* Table of channel contexts */
76 struct mhu2_channel_ctx *channel_ctx_table;
77
78 /* Number of channels in the channel context table*/
79 unsigned int channel_count;
80 } ctx;
81
mhu2_isr(uintptr_t ctx_param)82 static void mhu2_isr(uintptr_t ctx_param)
83 {
84 struct mhu2_channel_ctx *channel_ctx = (struct mhu2_channel_ctx *)ctx_param;
85 struct mhu2_bound_channel *bound_channel;
86 unsigned int slot;
87
88 fwk_assert(channel_ctx != NULL);
89
90 while (channel_ctx->recv_channel->STAT != 0) {
91 slot = __builtin_ctz(channel_ctx->recv_channel->STAT);
92 /*
93 * If the slot is bound to a transport channel,
94 * signal the message to the corresponding module
95 */
96 if (channel_ctx->bound_slots & (1 << slot)) {
97 bound_channel = &channel_ctx->bound_channels_table[slot];
98
99 bound_channel->driver_input_api->signal_message(bound_channel->id);
100 }
101 /* Acknowledge the interrupt */
102 channel_ctx->recv_channel->STAT_CLEAR = 1 << slot;
103 }
104 }
105
106 /*
107 * TRANSPORT module driver API
108 */
109
raise_interrupt(fwk_id_t slot_id)110 static int raise_interrupt(fwk_id_t slot_id)
111 {
112 unsigned int slot;
113 struct mhu2_channel_ctx *channel_ctx;
114 struct mhu2_send_reg *send;
115
116 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(slot_id)];
117 slot = fwk_id_get_sub_element_idx(slot_id);
118 send = channel_ctx->send;
119
120 /* Turn on receiver */
121 send->ACCESS_REQUEST = 1;
122 while (send->ACCESS_READY != 1)
123 continue;
124
125 channel_ctx->send_channel->STAT_SET |= (1 << slot);
126
127 /* Signal that the receiver is no longer needed */
128 send->ACCESS_REQUEST = 0;
129
130 return FWK_SUCCESS;
131 }
132
133 #ifdef BUILD_HAS_INBAND_MSG_SUPPORT
134 /*
135 * transport module driver API
136 */
mhu2_send_message(struct mod_transport_buffer * message,fwk_id_t slot_id)137 static int mhu2_send_message(
138 struct mod_transport_buffer *message,
139 fwk_id_t slot_id)
140 {
141 struct mhu2_channel_ctx *channel_ctx;
142 struct mhu2_send_reg *send;
143 uint8_t channel_count;
144 size_t payload_size;
145 uint8_t db_ch = 0, ch = 0;
146 uint8_t channels_used_for_payload = 0;
147 uint32_t *msg_ptr;
148
149 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(slot_id)];
150
151 /* Get pointer to the MHUv2 send register */
152 send = channel_ctx->send;
153
154 /* Get pointer to the message to be sent */
155 msg_ptr = (uint32_t *)message;
156
157 /* Calculate the size of the payload */
158 payload_size = message->length - sizeof(message->message_header);
159
160 /* Calculate the number of channels required for payload */
161 if (payload_size) {
162 channels_used_for_payload =
163 payload_size / MHU_CHANNEL_STATUS_REGISTER_WIDTH;
164 }
165
166 /* Read the number of channels implemented */
167 channel_count = send->MSG_NO_CAP;
168
169 /* Check if minimum number of MHUv2 channels are available */
170 if (channel_count < (min_channels_required + channels_used_for_payload)) {
171 FWK_LOG_INFO(
172 "[MHUv2] ERROR! Message length exceeds the number of MHUv2"
173 "channels available\n");
174 return FWK_E_SUPPORT;
175 }
176
177 /* Turn on receiver */
178 send->ACCESS_REQUEST = 1;
179 while (send->ACCESS_READY != 1)
180 continue;
181
182 /* Get the channel used for doorbell */
183 db_ch = channel_ctx->config->channel;
184
185 /* Copy the in-band message to the message status registers */
186 for (uint8_t idx = 0; ch < (min_channels_required - 1); ch++) {
187 /* Skip the doorbell channel */
188 if (ch == db_ch)
189 continue;
190
191 send->channel[ch].STAT_SET = msg_ptr[idx];
192 idx++;
193 }
194
195 /*
196 * If we have payload to be sent, copy the payload to message status
197 * registers.
198 */
199 if (payload_size != 0) {
200 for (uint8_t payload_idx = 0;
201 payload_idx < (payload_size / MHU_CHANNEL_STATUS_REGISTER_WIDTH);
202 payload_idx++) {
203 /* Skip the doorbell channel */
204 if ((ch + payload_idx) == db_ch)
205 continue;
206
207 send->channel[payload_idx + ch].STAT_SET =
208 message->payload[payload_idx];
209 }
210 }
211
212 /* Receiver no longer required */
213 send->ACCESS_REQUEST = 0;
214
215 return FWK_SUCCESS;
216 }
217
mhu2_get_message(struct mod_transport_buffer * message,fwk_id_t slot_id)218 static int mhu2_get_message(
219 struct mod_transport_buffer *message,
220 fwk_id_t slot_id)
221 {
222 struct mhu2_channel_ctx *channel_ctx;
223 struct mhu2_recv_reg *recv;
224 size_t payload_size;
225 uint8_t db_ch = 0, ch = 0;
226 uint32_t *msg_ptr;
227
228 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(slot_id)];
229
230 /* Get the channel used for doorbell */
231 db_ch = channel_ctx->config->channel;
232
233 /* Get a pointer to buffer */
234 msg_ptr = (uint32_t *)message;
235
236 /* Get a pointer to MHUv2 receive register */
237 recv = (struct mhu2_recv_reg *)channel_ctx->config->recv;
238
239 /* Copy the in-band message from message status registers */
240 for (uint8_t idx = 0; ch < (min_channels_required - 1); ch++) {
241 /* Skip doorbell channel */
242 if (ch == db_ch)
243 continue;
244
245 msg_ptr[idx++] = recv->channel[ch].STAT;
246
247 /* Clear the message status register */
248 recv->channel[ch].STAT_CLEAR = 0xffffffff;
249 }
250
251 /* Calculate size of the received payload */
252 payload_size = message->length - sizeof(message->message_header);
253
254 /* If payload is present, copy it from the message status registers */
255 if (payload_size != 0) {
256 for (uint8_t payload_idx = 0;
257 payload_idx < (payload_size / MHU_CHANNEL_STATUS_REGISTER_WIDTH);
258 payload_idx++) {
259 /* Skip doorbell channel */
260 if ((ch + payload_idx) == db_ch)
261 continue;
262
263 message->payload[payload_idx] =
264 recv->channel[payload_idx + ch].STAT;
265
266 /* Clear the message status register */
267 recv->channel[payload_idx + ch].STAT_CLEAR = 0xffffffff;
268 }
269 }
270
271 return FWK_SUCCESS;
272 }
273 #endif
274
275 struct mod_transport_driver_api mhu2_mod_transport_driver_api = {
276 # ifdef BUILD_HAS_INBAND_MSG_SUPPORT
277 .send_message = mhu2_send_message,
278 .get_message = mhu2_get_message,
279 # endif
280 .trigger_event = raise_interrupt,
281 };
282
283 /*
284 * Framework handlers
285 */
286
mhu2_init(fwk_id_t module_id,unsigned int channel_count,const void * unused)287 static int mhu2_init(fwk_id_t module_id,
288 unsigned int channel_count,
289 const void *unused)
290 {
291 if (channel_count == 0) {
292 /* There must be at least 1 mhu channel */
293 fwk_unexpected();
294 return FWK_E_PARAM;
295 }
296
297 ctx.channel_ctx_table = fwk_mm_calloc(channel_count,
298 sizeof(ctx.channel_ctx_table[0]));
299
300 ctx.channel_count = channel_count;
301
302 return FWK_SUCCESS;
303 }
304
mhu2_channel_init(fwk_id_t channel_id,unsigned int slot_count,const void * data)305 static int mhu2_channel_init(fwk_id_t channel_id,
306 unsigned int slot_count,
307 const void *data)
308 {
309 const struct mod_mhu2_channel_config *config = data;
310 struct mhu2_channel_ctx *channel_ctx;
311 struct mhu2_recv_reg *recv_reg;
312
313 if ((config == NULL) || (config->recv == 0) || (config->send == 0)) {
314 fwk_unexpected();
315 return FWK_E_DATA;
316 }
317
318 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
319 channel_ctx->send = (struct mhu2_send_reg *)config->send;
320
321 if (config->channel >= channel_ctx->send->MSG_NO_CAP) {
322 fwk_unexpected();
323 return FWK_E_DATA;
324 }
325
326 channel_ctx->config = config;
327 channel_ctx->slot_count = slot_count;
328 channel_ctx->send_channel = &channel_ctx->send->channel[config->channel];
329 recv_reg = (struct mhu2_recv_reg *)config->recv;
330
331 #if defined(BUILD_HAS_INBAND_MSG_SUPPORT)
332 /*
333 * Mask the channels used for transferring in-band messages.
334 * Only the doorbell channel should be used to raise interrupt.
335 */
336 for (uint8_t ch = 0; ch < recv_reg->MSG_NO_CAP; ch++) {
337 /* Skip the channel used for doorbell */
338 if (ch == config->channel)
339 continue;
340
341 /*
342 * Set channel mask.
343 *
344 * Masked channels don't generate interrupt when data is written
345 * to them.
346 */
347 recv_reg->channel[ch].MASK_SET = 0xffffffff;
348 }
349 #endif
350 channel_ctx->recv_channel = &recv_reg->channel[config->channel];
351
352 channel_ctx->bound_channels_table =
353 fwk_mm_calloc(slot_count, sizeof(channel_ctx->bound_channels_table[0]));
354
355 return FWK_SUCCESS;
356 }
357
mhu2_bind(fwk_id_t id,unsigned int round)358 static int mhu2_bind(fwk_id_t id, unsigned int round)
359 {
360 int status;
361 struct mhu2_channel_ctx *channel_ctx;
362 unsigned int slot;
363
364 if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
365 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
366
367 for (slot = 0; slot < MHU_SLOT_COUNT_MAX; slot++) {
368 struct mhu2_bound_channel *bound_channel;
369 if (!(channel_ctx->bound_slots & (UINT32_C(1) << slot)))
370 continue;
371
372 bound_channel = &channel_ctx->bound_channels_table[slot];
373
374 status = fwk_module_bind(
375 bound_channel->id,
376 FWK_ID_API(
377 FWK_MODULE_IDX_TRANSPORT,
378 MOD_TRANSPORT_API_IDX_DRIVER_INPUT),
379 &bound_channel->driver_input_api);
380
381 if (status != FWK_SUCCESS) {
382 /* Unable to bind back to TRANSPORT channel */
383 fwk_unexpected();
384 return status;
385 }
386 }
387 }
388
389 return FWK_SUCCESS;
390 }
391
mhu2_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)392 static int mhu2_process_bind_request(fwk_id_t source_id,
393 fwk_id_t target_id,
394 fwk_id_t api_id,
395 const void **api)
396 {
397 struct mhu2_channel_ctx *channel_ctx;
398 unsigned int slot;
399 enum mod_mhu2_api_idx api_id_type;
400 api_id_type = (enum mod_mhu2_api_idx)fwk_id_get_api_idx(api_id);
401
402 if (!fwk_id_is_type(target_id, FWK_ID_TYPE_SUB_ELEMENT)) {
403 /*
404 * Something tried to bind to the module or an element. Only binding to
405 * a slot (sub-element) is allowed.
406 */
407 fwk_unexpected();
408 return FWK_E_ACCESS;
409 }
410
411 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(target_id)];
412 slot = fwk_id_get_sub_element_idx(target_id);
413 if (channel_ctx->bound_slots & (1 << slot)) {
414 /* Something tried to bind to a slot that has already been bound to */
415 fwk_unexpected();
416 return FWK_E_ACCESS;
417 }
418
419 channel_ctx->bound_channels_table[slot].id = source_id;
420 channel_ctx->bound_slots |= 1 << slot;
421
422 if (api_id_type != MOD_MHU2_API_IDX_TRANSPORT_DRIVER) {
423 /* Invalid config */
424 fwk_unexpected();
425 return FWK_E_PARAM;
426 }
427
428 *api = &mhu2_mod_transport_driver_api;
429
430 return FWK_SUCCESS;
431 }
432
mhu2_start(fwk_id_t id)433 static int mhu2_start(fwk_id_t id)
434 {
435 int status;
436 struct mhu2_channel_ctx *channel_ctx;
437
438 if (fwk_id_get_type(id) == FWK_ID_TYPE_MODULE)
439 return FWK_SUCCESS;
440
441 channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
442
443 if (channel_ctx->bound_slots != 0) {
444 status = fwk_interrupt_set_isr_param(channel_ctx->config->irq,
445 &mhu2_isr,
446 (uintptr_t)channel_ctx);
447 if (status != FWK_SUCCESS) {
448 /* Failed to set isr */
449 fwk_unexpected();
450 return status;
451 }
452 status = fwk_interrupt_enable(channel_ctx->config->irq);
453 if (status != FWK_SUCCESS) {
454 /* Failed to enable isr */
455 fwk_unexpected();
456 return status;
457 }
458 }
459
460 return FWK_SUCCESS;
461 }
462
463 /* MHU v2 module definition */
464 const struct fwk_module module_mhu2 = {
465 .type = FWK_MODULE_TYPE_DRIVER,
466 .api_count = MOD_MHU2_API_IDX_COUNT,
467 .init = mhu2_init,
468 .element_init = mhu2_channel_init,
469 .bind = mhu2_bind,
470 .start = mhu2_start,
471 .process_bind_request = mhu2_process_bind_request,
472 };
473