1 /*
2 * Copyright (c) 2023 Arm Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "mhu.h"
18 #include "mhu_v3_x.h"
19
20 #include <stdint.h>
21 #include <string.h>
22
23 #define MHU_NOTIFY_VALUE (1234u)
24
25 #ifndef ALIGN_UP
26 #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
27 #endif
28
29 /*
30 * MHUv3 Wrapper utility macros
31 */
32 #define IS_ALIGNED(val, align) (val == ALIGN_UP(val, align))
33
34 /* MHUv3 driver error to MHUv3 wrapper error mapping */
error_mapping_to_mhu_error_t(enum mhu_v3_x_error_t err)35 static enum mhu_error_t error_mapping_to_mhu_error_t(
36 enum mhu_v3_x_error_t err)
37 {
38 switch (err) {
39 case MHU_V_3_X_ERR_NONE:
40 return MHU_ERR_NONE;
41
42 case MHU_V_3_X_ERR_NOT_INIT:
43 return MHU_ERR_NOT_INIT;
44
45 case MHU_V_3_X_ERR_UNSUPPORTED_VERSION:
46 return MHU_ERR_UNSUPPORTED_VERSION;
47
48 case MHU_V_3_X_ERR_UNSUPPORTED:
49 return MHU_ERR_UNSUPPORTED;
50
51 case MHU_V_3_X_ERR_INVALID_PARAM:
52 return MHU_ERR_INVALID_ARG;
53
54 default:
55 return MHU_ERR_GENERAL;
56 }
57 }
58
signal_and_wait_for_clear(void * mhu_sender_dev,uint32_t value)59 enum mhu_error_t signal_and_wait_for_clear(
60 void* mhu_sender_dev, uint32_t value)
61 {
62 enum mhu_v3_x_error_t err;
63 struct mhu_v3_x_dev_t *dev;
64 uint8_t num_channels;
65 uint32_t read_val;
66
67 dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev;
68
69 if (dev == NULL || dev->base == 0) {
70 return MHU_ERR_INVALID_ARG;
71 }
72
73 err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
74 &num_channels);
75 if (err != MHU_V_3_X_ERR_NONE) {
76 return error_mapping_to_mhu_error_t(err);
77 }
78
79 /* Wait for any pending acknowledgement from transmitter side */
80 do {
81 err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
82 if (err != MHU_V_3_X_ERR_NONE) {
83 return error_mapping_to_mhu_error_t(err);
84 }
85 } while ((read_val & value) == value);
86
87 /* Use the last channel to nofity that a transfer is ready */
88 err = mhu_v3_x_doorbell_write(dev, num_channels - 1, value);
89 if (err != MHU_V_3_X_ERR_NONE) {
90 return error_mapping_to_mhu_error_t(err);
91 }
92
93 /* Wait until receiver side acknowledges the transfer */
94 do {
95 err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
96 if (err != MHU_V_3_X_ERR_NONE) {
97 return error_mapping_to_mhu_error_t(err);
98 }
99 } while ((read_val & value) == value);
100
101 return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE);
102 }
103
wait_for_signal_and_clear(void * mhu_receiver_dev,uint32_t value)104 enum mhu_error_t wait_for_signal_and_clear (
105 void* mhu_receiver_dev, uint32_t value)
106 {
107 enum mhu_v3_x_error_t err;
108 struct mhu_v3_x_dev_t *dev;
109 uint8_t num_channels;
110 uint32_t read_val;
111
112 dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
113
114 if (dev == NULL || dev->base == 0) {
115 return MHU_ERR_INVALID_ARG;
116 }
117
118 err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
119 &num_channels);
120 if (err != MHU_V_3_X_ERR_NONE) {
121 return error_mapping_to_mhu_error_t(err);
122 }
123
124 /* Wait on status register for transmitter side to send data */
125 do {
126 err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
127 if (err != MHU_V_3_X_ERR_NONE) {
128 return error_mapping_to_mhu_error_t(err);
129 }
130 } while ((read_val & value) != value);
131
132 /* Acknowledge the transfer and clear the doorbell register */
133 err = mhu_v3_x_doorbell_clear(dev, num_channels - 1, value);
134 if (err != MHU_V_3_X_ERR_NONE) {
135 return error_mapping_to_mhu_error_t(err);
136 }
137
138 return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE);
139 }
140
clear_and_wait_for_signal(void * mhu_receiver_dev,uint32_t value)141 enum mhu_error_t clear_and_wait_for_signal (
142 void* mhu_receiver_dev, uint32_t value)
143 {
144 enum mhu_v3_x_error_t err;
145 struct mhu_v3_x_dev_t *dev;
146 uint8_t num_channels;
147 uint32_t read_val;
148
149 dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
150
151 if (dev == NULL || dev->base == 0) {
152 return MHU_ERR_INVALID_ARG;
153 }
154
155 err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
156 &num_channels);
157 if (err != MHU_V_3_X_ERR_NONE) {
158 return error_mapping_to_mhu_error_t(err);
159 }
160
161 /* Clear all channels */
162 for (int i = 0; i < num_channels; ++i) {
163 err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX);
164 if (err != MHU_V_3_X_ERR_NONE) {
165 return error_mapping_to_mhu_error_t(err);
166 }
167 }
168
169 /* Wait for transmitter side to send data */
170 do {
171 err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
172 if (err != MHU_V_3_X_ERR_NONE) {
173 return error_mapping_to_mhu_error_t(err);
174 }
175 } while (read_val != value);
176
177 return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE);
178 }
179
validate_buffer_params(uintptr_t buf_addr,size_t buf_size)180 static enum mhu_error_t validate_buffer_params(uintptr_t buf_addr,
181 size_t buf_size)
182 {
183 if ((buf_addr == 0) || (!IS_ALIGNED(buf_addr, 4))) {
184 return MHU_ERR_INVALID_ARG;
185 }
186
187 return MHU_ERR_NONE;
188 }
189
mhu_init_sender(void * mhu_sender_dev)190 enum mhu_error_t mhu_init_sender(void *mhu_sender_dev)
191 {
192 enum mhu_v3_x_error_t err;
193 struct mhu_v3_x_dev_t *dev;
194 uint8_t num_ch;
195 uint32_t ch;
196
197 dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev;
198
199 if (dev == NULL || dev->base == 0) {
200 return MHU_ERR_INVALID_ARG;
201 }
202
203 /* Initialize MHUv3 */
204 err = mhu_v3_x_driver_init(dev);
205 if (err != MHU_V_3_X_ERR_NONE) {
206 return error_mapping_to_mhu_error_t(err);
207 }
208
209 /* Read the number of doorbell channels implemented in the MHU */
210 err = mhu_v3_x_get_num_channel_implemented(
211 dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch);
212 if (err != MHU_V_3_X_ERR_NONE) {
213 return error_mapping_to_mhu_error_t(err);
214 } else if (num_ch < 2) {
215 /* This wrapper requires at least two channels to be implemented */
216 return MHU_ERR_UNSUPPORTED;
217 }
218
219 /*
220 * The sender polls the postbox doorbell channel window status register to
221 * get notified about successful transfer. So, disable the doorbell
222 * channel's contribution to postbox combined interrupt.
223 *
224 * Also, clear and disable the postbox doorbell channel transfer acknowledge
225 * interrupt.
226 */
227 for (ch = 0; ch < num_ch; ++ch) {
228 err = mhu_v3_x_channel_interrupt_disable(
229 dev, ch, MHU_V3_X_CHANNEL_TYPE_DBCH);
230 if (err != MHU_V_3_X_ERR_NONE) {
231 return error_mapping_to_mhu_error_t(err);
232 }
233 }
234
235 return MHU_ERR_NONE;
236 }
237
mhu_init_receiver(void * mhu_receiver_dev)238 enum mhu_error_t mhu_init_receiver(void *mhu_receiver_dev)
239 {
240 enum mhu_v3_x_error_t err;
241 struct mhu_v3_x_dev_t *dev;
242 uint32_t ch;
243 uint8_t num_ch;
244
245 dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
246
247 if (dev == NULL || dev->base == 0) {
248 return MHU_ERR_INVALID_ARG;
249 }
250
251 /* Initialize MHUv3 */
252 err = mhu_v3_x_driver_init(dev);
253 if (err != MHU_V_3_X_ERR_NONE) {
254 return error_mapping_to_mhu_error_t(err);
255 }
256
257 /* Read the number of doorbell channels implemented in the MHU */
258 err = mhu_v3_x_get_num_channel_implemented(
259 dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch);
260 if (err != MHU_V_3_X_ERR_NONE) {
261 return error_mapping_to_mhu_error_t(err);
262 } else if (num_ch < 2) {
263 /* This wrapper requires at least two channels to be implemented */
264 return MHU_ERR_UNSUPPORTED;
265 }
266
267 /* Mask all channels except the notifying channel */
268 for (ch = 0; ch < (num_ch - 1); ++ch) {
269 err = mhu_v3_x_doorbell_mask_set(dev, ch, UINT32_MAX);
270 if (err != MHU_V_3_X_ERR_NONE) {
271 return error_mapping_to_mhu_error_t(err);
272 }
273 }
274
275 /* Unmask doorbell notification channel interrupt */
276 err = mhu_v3_x_doorbell_mask_clear(dev, (num_ch - 1), UINT32_MAX);
277 if (err != MHU_V_3_X_ERR_NONE) {
278 return error_mapping_to_mhu_error_t(err);
279 }
280
281 /*
282 * Enable the doorbell channel's contribution to mailbox combined
283 * interrupt.
284 */
285 err = mhu_v3_x_channel_interrupt_enable(dev, (num_ch - 1),
286 MHU_V3_X_CHANNEL_TYPE_DBCH);
287 if (err != MHU_V_3_X_ERR_NONE) {
288 return error_mapping_to_mhu_error_t(err);
289 }
290
291 return MHU_ERR_NONE;
292 }
293
mhu_send_data(void * mhu_sender_dev,const uint8_t * send_buffer,size_t size)294 enum mhu_error_t mhu_send_data(void *mhu_sender_dev, const uint8_t *send_buffer,
295 size_t size)
296 {
297 enum mhu_error_t mhu_err;
298 enum mhu_v3_x_error_t mhu_v3_err;
299 uint8_t num_channels;
300 uint8_t chan;
301 uint32_t *buffer;
302 struct mhu_v3_x_dev_t *dev;
303
304 if (size == 0) {
305 return MHU_ERR_NONE;
306 }
307
308 dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev;
309 chan = 0;
310
311 if (dev == NULL || dev->base == 0) {
312 return MHU_ERR_INVALID_ARG;
313 }
314
315 mhu_err = validate_buffer_params((uintptr_t)send_buffer, size);
316 if (mhu_err != MHU_ERR_NONE) {
317 return mhu_err;
318 }
319
320 mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
321 &num_channels);
322 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
323 return error_mapping_to_mhu_error_t(mhu_v3_err);
324 }
325
326 /* First send over the size of the actual message. */
327 mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, (uint32_t)size);
328 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
329 return error_mapping_to_mhu_error_t(mhu_v3_err);
330 }
331 chan++;
332
333 buffer = (uint32_t *)send_buffer;
334 for (size_t i = 0; i < size; i += 4) {
335 mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, *buffer++);
336 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
337 return error_mapping_to_mhu_error_t(mhu_v3_err);
338 }
339 if (++chan == (num_channels - 1)) {
340 /* Using the last channel for notifications */
341 mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE);
342 if (mhu_err != MHU_ERR_NONE) {
343 return mhu_err;
344 }
345 chan = 0;
346 }
347 }
348
349 if (chan != 0) {
350 /* Using the last channel for notifications */
351 mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE);
352 if (mhu_err != MHU_ERR_NONE) {
353 return mhu_err;
354 }
355 }
356
357 return MHU_ERR_NONE;
358 }
359
mhu_wait_data(void * mhu_receiver_dev)360 enum mhu_error_t mhu_wait_data(void *mhu_receiver_dev)
361 {
362 struct mhu_v3_x_dev_t *dev = mhu_receiver_dev;
363 enum mhu_v3_x_error_t mhu_v3_err;
364 uint8_t num_channels;
365 uint32_t read_val;
366
367 mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
368 &num_channels);
369 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
370 return error_mapping_to_mhu_error_t(mhu_v3_err);
371 }
372
373 /* Wait for transmitter side to send data */
374 do {
375 mhu_v3_err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
376 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
377 return error_mapping_to_mhu_error_t(mhu_v3_err);
378 }
379 } while (read_val != MHU_NOTIFY_VALUE);
380
381 return error_mapping_to_mhu_error_t(mhu_v3_err);
382 }
383
384
mhu_receive_data(void * mhu_receiver_dev,uint8_t * receive_buffer,size_t * size)385 enum mhu_error_t mhu_receive_data(void *mhu_receiver_dev,
386 uint8_t *receive_buffer, size_t *size)
387 {
388 enum mhu_error_t mhu_err;
389 enum mhu_v3_x_error_t mhu_v3_err;
390 uint32_t msg_len;
391 uint8_t num_channels;
392 uint8_t chan;
393 uint32_t *buffer;
394 struct mhu_v3_x_dev_t *dev;
395
396 dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
397 chan = 0;
398
399 mhu_err = validate_buffer_params((uintptr_t)receive_buffer, *size);
400 if (mhu_err != MHU_ERR_NONE) {
401 return mhu_err;
402 }
403
404 mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, MHU_V3_X_CHANNEL_TYPE_DBCH,
405 &num_channels);
406 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
407 return error_mapping_to_mhu_error_t(mhu_v3_err);
408 }
409
410 /* The first word is the length of the actual message. */
411 mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, &msg_len);
412 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
413 return error_mapping_to_mhu_error_t(mhu_v3_err);
414 }
415 chan++;
416
417 if (*size < msg_len) {
418 /* Message buffer too small */
419 *size = msg_len;
420 return MHU_ERR_BUFFER_TOO_SMALL;
421 }
422
423 buffer = (uint32_t *)receive_buffer;
424 for (size_t i = 0; i < msg_len; i += 4) {
425 mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, buffer++);
426 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
427 return error_mapping_to_mhu_error_t(mhu_v3_err);
428 }
429
430 /* Only wait for next transfer if there is still missing data. */
431 if (++chan == (num_channels - 1) && (msg_len - i) > 4) {
432 /* Busy wait for next transfer */
433 mhu_err = clear_and_wait_for_signal(dev, MHU_NOTIFY_VALUE);
434 if (mhu_err != MHU_ERR_NONE) {
435 return mhu_err;
436 }
437 chan = 0;
438 }
439 }
440
441 /* Clear all channels */
442 for (int i = 0; i < num_channels; ++i) {
443 mhu_v3_err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX);
444 if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
445 return error_mapping_to_mhu_error_t(mhu_v3_err);
446 }
447 }
448
449 *size = msg_len;
450
451 return MHU_ERR_NONE;
452 }
453