1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *      Message Handling Unit (MHU) Device Driver.
9  */
10 
11 #include <internal/mhu.h>
12 
13 #include <mod_mhu.h>
14 #include <mod_transport.h>
15 
16 #include <fwk_id.h>
17 #include <fwk_interrupt.h>
18 #include <fwk_log.h>
19 #include <fwk_mm.h>
20 #include <fwk_module.h>
21 #include <fwk_module_idx.h>
22 #include <fwk_status.h>
23 
24 #include <stddef.h>
25 #include <stdint.h>
26 
27 /*
28  * Maximum number of slots per MHU device. The maximum number of slots is 31 and
29  * not 32 because bit [31] in the MHU STAT register is reserved in secure MHUs
30  * for indicating that a non-secure access attempt occurred. This reservation
31  * also applies to non-secure MHUs for consistency, though the bit is unused.
32  */
33 #define MHU_SLOT_COUNT_MAX 31
34 
35 struct mhu_transport_channel {
36     fwk_id_t id;
37     struct mod_transport_driver_input_api *api;
38 };
39 
40 /* MHU device context */
41 struct mhu_device_ctx {
42     /* Pointer to the device configuration */
43     const struct mod_mhu_device_config *config;
44 
45     /* Number of slots (represented by sub-elements) */
46     unsigned int slot_count;
47 
48     /* Mask of slots that are bound to a TRANSPORT/SMT channel */
49     uint32_t bound_slots;
50 
51     /* Table of TRANSPORT channels bound to the device */
52     struct mhu_transport_channel *transport_channel_table;
53 };
54 
55 /* MHU context */
56 struct mod_mhu_ctx {
57     /* Table of device contexts */
58     struct mhu_device_ctx *device_ctx_table;
59 
60     /* Number of devices in the device context table*/
61     unsigned int device_count;
62 };
63 
64 static struct mod_mhu_ctx mhu_ctx;
65 
mhu_isr(void)66 static void mhu_isr(void)
67 {
68     int status;
69     unsigned int interrupt;
70     unsigned int device_idx;
71     struct mhu_device_ctx *device_ctx;
72     struct mhu_reg *reg;
73     unsigned int slot;
74     struct mhu_transport_channel *transport_channel;
75 
76     status = fwk_interrupt_get_current(&interrupt);
77     if (status != FWK_SUCCESS) {
78         return;
79     }
80 
81     for (device_idx = 0; device_idx < mhu_ctx.device_count; device_idx++) {
82         device_ctx = &mhu_ctx.device_ctx_table[device_idx];
83         if (device_ctx->config->irq == interrupt) {
84             break;
85         }
86     }
87 
88     if (device_idx >= mhu_ctx.device_count) {
89         return;
90     }
91 
92     reg = (struct mhu_reg *)device_ctx->config->in;
93 
94     /* Loop over all the slots */
95     while (reg->STAT != 0) {
96         slot = (unsigned int)__builtin_ctz(reg->STAT);
97 
98         /*
99          * If the slot is bound to an TRANSPORT channel, signal the message
100          * to the TRANSPORT channel.
101          */
102         if ((device_ctx->bound_slots & (uint32_t)(1U << slot)) != (uint32_t)0) {
103             transport_channel = &device_ctx->transport_channel_table[slot];
104             status =
105                 transport_channel->api->signal_message(transport_channel->id);
106             if (status != FWK_SUCCESS) {
107                 FWK_LOG_DEBUG("[MHU] %s @%d", __func__, __LINE__);
108             }
109         }
110 
111         /* Acknowledge the interrupt */
112         reg->CLEAR = 1U << slot;
113     }
114 }
115 
116 /*
117  * TRANSPORT module driver API
118  */
119 
raise_interrupt(fwk_id_t slot_id)120 static int raise_interrupt(fwk_id_t slot_id)
121 {
122     struct mhu_device_ctx *device_ctx;
123     unsigned int slot;
124     struct mhu_reg *reg;
125 
126 
127     device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(slot_id)];
128     slot = fwk_id_get_sub_element_idx(slot_id);
129     reg = (struct mhu_reg *)device_ctx->config->out;
130 
131     reg->SET |= (1U << slot);
132 
133     return FWK_SUCCESS;
134 }
135 
136 const struct mod_transport_driver_api mhu_mod_transport_driver_api = {
137     .trigger_event = raise_interrupt,
138 };
139 
140 /*
141  * Framework handlers
142  */
143 
mhu_init(fwk_id_t module_id,unsigned int device_count,const void * unused)144 static int mhu_init(fwk_id_t module_id, unsigned int device_count,
145                     const void *unused)
146 {
147     if (device_count == 0) {
148         return FWK_E_PARAM;
149     }
150 
151     mhu_ctx.device_ctx_table = fwk_mm_calloc(device_count,
152         sizeof(mhu_ctx.device_ctx_table[0]));
153 
154     mhu_ctx.device_count = device_count;
155 
156     return FWK_SUCCESS;
157 }
158 
mhu_device_init(fwk_id_t device_id,unsigned int slot_count,const void * data)159 static int mhu_device_init(fwk_id_t device_id, unsigned int slot_count,
160                            const void *data)
161 {
162     struct mod_mhu_device_config *config = (struct mod_mhu_device_config *)data;
163     struct mhu_device_ctx *device_ctx;
164 
165     if ((config->in == 0) || (config->out == 0)) {
166         return FWK_E_PARAM;
167     }
168 
169     device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(device_id)];
170 
171     device_ctx->transport_channel_table = fwk_mm_calloc(
172         slot_count, sizeof(device_ctx->transport_channel_table[0]));
173 
174     device_ctx->config = config;
175     device_ctx->slot_count = slot_count;
176 
177     return FWK_SUCCESS;
178 }
179 
mhu_bind(fwk_id_t id,unsigned int round)180 static int mhu_bind(fwk_id_t id, unsigned int round)
181 {
182     int status;
183     struct mhu_device_ctx *device_ctx;
184     unsigned int slot;
185     struct mhu_transport_channel *transport_channel;
186 
187     if ((round == 1U) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
188         device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(id)];
189 
190         for (slot = 0; slot < MHU_SLOT_COUNT_MAX; slot++) {
191             if ((device_ctx->bound_slots & (uint32_t)(1U << slot)) ==
192                 (uint32_t)0) {
193                 continue;
194             }
195 
196             transport_channel = &device_ctx->transport_channel_table[slot];
197 
198             status = fwk_module_bind(
199                 transport_channel->id,
200                 FWK_ID_API(
201                     FWK_MODULE_IDX_TRANSPORT,
202                     MOD_TRANSPORT_API_IDX_DRIVER_INPUT),
203                 &transport_channel->api);
204             if (status != FWK_SUCCESS) {
205                 return status;
206             }
207         }
208     }
209 
210     return FWK_SUCCESS;
211 }
212 
mhu_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)213 static int mhu_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
214                                     fwk_id_t api_id, const void **api)
215 {
216     struct mhu_device_ctx *device_ctx;
217     unsigned int slot;
218 
219     if (!fwk_id_is_type(target_id, FWK_ID_TYPE_SUB_ELEMENT)) {
220         return FWK_E_ACCESS;
221     }
222 
223     device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(target_id)];
224     slot = fwk_id_get_sub_element_idx(target_id);
225 
226     if (device_ctx->bound_slots & (1U << slot)) {
227         return FWK_E_ACCESS;
228     }
229 
230     device_ctx->transport_channel_table[slot].id = source_id;
231     device_ctx->bound_slots |= 1U << slot;
232 
233     *api = &mhu_mod_transport_driver_api;
234 
235     return FWK_SUCCESS;
236 }
237 
mhu_start(fwk_id_t id)238 static int mhu_start(fwk_id_t id)
239 {
240     int status;
241     struct mhu_device_ctx *device_ctx;
242 
243     if (fwk_id_get_type(id) == FWK_ID_TYPE_MODULE) {
244         return FWK_SUCCESS;
245     }
246 
247     device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(id)];
248 
249     if (device_ctx->bound_slots != 0) {
250         status = fwk_interrupt_set_isr(device_ctx->config->irq, &mhu_isr);
251         if (status != FWK_SUCCESS) {
252             return status;
253         }
254         status = fwk_interrupt_enable(device_ctx->config->irq);
255         if (status != FWK_SUCCESS) {
256             return status;
257         }
258     }
259 
260     return FWK_SUCCESS;
261 }
262 
263 /* MHU module definition */
264 const struct fwk_module module_mhu = {
265     .type = FWK_MODULE_TYPE_DRIVER,
266     .api_count = 1,
267     .init = mhu_init,
268     .element_init = mhu_device_init,
269     .bind = mhu_bind,
270     .start = mhu_start,
271     .process_bind_request = mhu_process_bind_request,
272 };
273