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