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