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