1 /*
2  * Copyright (c) 2022-2023 Arm Limited. All rights reserved.
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 
19 #include <stddef.h>
20 #include <stdint.h>
21 
22 #include "mhu_v2_x.h"
23 
24 #define MHU_NOTIFY_VALUE    (1234u)
25 
26 static enum mhu_error_t
error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err)27 error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err)
28 {
29     switch (err) {
30     case MHU_V_2_X_ERR_NONE:
31         return MHU_ERR_NONE;
32     case MHU_V_2_X_ERR_NOT_INIT:
33         return MHU_ERR_NOT_INIT;
34     case MHU_V_2_X_ERR_ALREADY_INIT:
35         return MHU_ERR_ALREADY_INIT;
36     case MHU_V_2_X_ERR_UNSUPPORTED_VERSION:
37         return MHU_ERR_UNSUPPORTED_VERSION;
38     case MHU_V_2_X_ERR_INVALID_ARG:
39         return MHU_ERR_INVALID_ARG;
40     case MHU_V_2_X_ERR_GENERAL:
41         return MHU_ERR_GENERAL;
42     default:
43         return MHU_ERR_GENERAL;
44     }
45 }
46 
47 enum mhu_error_t
signal_and_wait_for_clear(void * mhu_sender_dev,uint32_t value)48 signal_and_wait_for_clear(void *mhu_sender_dev, uint32_t value)
49 {
50     enum mhu_v2_x_error_t err;
51     struct mhu_v2_x_dev_t *dev;
52     uint32_t channel_notify;
53     uint32_t wait_val;
54 
55     if (mhu_sender_dev == NULL) {
56         return MHU_ERR_INVALID_ARG;
57     }
58 
59     dev = (struct mhu_v2_x_dev_t *)mhu_sender_dev;
60 
61     /* Use the last channel for notifications */
62     channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
63 
64     /* FIXME: Avoid wasting a whole channel for notifying */
65     err = mhu_v2_x_channel_send(dev, channel_notify, value);
66     if (err != MHU_V_2_X_ERR_NONE) {
67         return error_mapping_to_mhu_error_t(err);
68     }
69 
70     do {
71         err = mhu_v2_x_channel_poll(dev, channel_notify, &wait_val);
72         if (err != MHU_V_2_X_ERR_NONE) {
73             break;
74         }
75     } while (wait_val != 0);
76 
77     return error_mapping_to_mhu_error_t(err);
78 }
79 
80 enum mhu_error_t
wait_for_signal_and_clear(void * mhu_receiver_dev,uint32_t value)81 wait_for_signal_and_clear(void *mhu_receiver_dev, uint32_t value)
82 {
83     enum mhu_v2_x_error_t err;
84     struct mhu_v2_x_dev_t *dev;
85     uint32_t channel_notify;
86     uint32_t wait_val;
87 
88     if (mhu_receiver_dev == NULL) {
89         return MHU_ERR_INVALID_ARG;
90     }
91 
92     dev = (struct mhu_v2_x_dev_t *)mhu_receiver_dev;
93 
94     /* Use the last channel for notifications */
95     channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
96 
97     do {
98         /* Using the last channel for notifications */
99         err = mhu_v2_x_channel_receive(dev, channel_notify, &wait_val);
100         if (err != MHU_V_2_X_ERR_NONE) {
101             return error_mapping_to_mhu_error_t(err);
102         }
103     } while (wait_val != value);
104 
105     /* Clear the last channel */
106     err = mhu_v2_x_channel_clear(dev, channel_notify);
107 
108     return error_mapping_to_mhu_error_t(err);
109 }
110 
111 static enum mhu_v2_x_error_t
clear_and_wait_for_signal(struct mhu_v2_x_dev_t * dev)112 clear_and_wait_for_signal(struct mhu_v2_x_dev_t *dev)
113 {
114     enum mhu_v2_x_error_t err;
115     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
116     uint32_t val, i;
117 
118     /* Clear all channels */
119     for (i = 0; i < num_channels; ++i) {
120         err = mhu_v2_x_channel_clear(dev, i);
121         if (err != MHU_V_2_X_ERR_NONE) {
122             return err;
123         }
124     }
125 
126     do {
127         /* Using the last channel for notifications */
128         err = mhu_v2_x_channel_receive(dev, num_channels - 1, &val);
129         if (err != MHU_V_2_X_ERR_NONE) {
130             break;
131         }
132     } while (val != MHU_NOTIFY_VALUE);
133 
134     return err;
135 }
136 
mhu_init_sender(void * mhu_sender_dev)137 enum mhu_error_t mhu_init_sender(void *mhu_sender_dev)
138 {
139     enum mhu_v2_x_error_t err;
140     struct mhu_v2_x_dev_t *dev = mhu_sender_dev;
141 
142     if (dev == NULL) {
143         return MHU_ERR_INVALID_ARG;
144     }
145 
146     err = mhu_v2_x_driver_init(dev, MHU_REV_READ_FROM_HW);
147     if (err != MHU_V_2_X_ERR_NONE) {
148         return error_mapping_to_mhu_error_t(err);
149     }
150 
151     /* This wrapper requires at least two channels to be implemented */
152     if (mhu_v2_x_get_num_channel_implemented(dev) < 2) {
153         return MHU_ERR_UNSUPPORTED;
154     }
155 
156     return MHU_ERR_NONE;
157 }
158 
mhu_init_receiver(void * mhu_receiver_dev)159 enum mhu_error_t mhu_init_receiver(void *mhu_receiver_dev)
160 {
161     enum mhu_v2_x_error_t err;
162     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
163     uint32_t num_channels, i;
164 
165     if (dev == NULL) {
166         return MHU_ERR_INVALID_ARG;
167     }
168 
169     err = mhu_v2_x_driver_init(dev, MHU_REV_READ_FROM_HW);
170     if (err != MHU_V_2_X_ERR_NONE) {
171         return error_mapping_to_mhu_error_t(err);
172     }
173 
174     num_channels = mhu_v2_x_get_num_channel_implemented(dev);
175 
176     /* This wrapper requires at least two channels to be implemented */
177     if (num_channels < 2) {
178         return MHU_ERR_UNSUPPORTED;
179     }
180 
181     /* Mask all channels except the notifying channel */
182     for (i = 0; i < (num_channels - 1); ++i) {
183         err = mhu_v2_x_channel_mask_set(dev, i, UINT32_MAX);
184         if (err != MHU_V_2_X_ERR_NONE) {
185             return error_mapping_to_mhu_error_t(err);
186         }
187     }
188 
189     /* The last channel is used for notifications */
190     err = mhu_v2_x_channel_mask_clear(dev, (num_channels - 1), UINT32_MAX);
191     if (err != MHU_V_2_X_ERR_NONE) {
192         return error_mapping_to_mhu_error_t(err);
193     }
194 
195     err = mhu_v2_x_interrupt_enable(dev, MHU_2_1_INTR_CHCOMB_MASK);
196     if (err != MHU_V_2_X_ERR_NONE) {
197         return error_mapping_to_mhu_error_t(err);
198     }
199 
200     return MHU_ERR_NONE;
201 }
202 
mhu_send_data(void * mhu_sender_dev,const uint8_t * send_buffer,size_t size)203 enum mhu_error_t mhu_send_data(void *mhu_sender_dev,
204                                const uint8_t *send_buffer,
205                                size_t size)
206 {
207     enum mhu_v2_x_error_t err;
208     enum mhu_error_t mhu_err;
209     struct mhu_v2_x_dev_t *dev = mhu_sender_dev;
210     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
211     uint32_t chan = 0;
212     uint32_t i;
213     uint32_t *p;
214 
215     if (dev == NULL || send_buffer == NULL) {
216         return MHU_ERR_INVALID_ARG;
217     } else if (size == 0) {
218         return MHU_ERR_NONE;
219     }
220 
221     /* For simplicity, require the send_buffer to be 4-byte aligned. */
222     if ((uintptr_t)send_buffer & 0x3u) {
223         return MHU_ERR_INVALID_ARG;
224     }
225 
226     err = mhu_v2_x_initiate_transfer(dev);
227     if (err != MHU_V_2_X_ERR_NONE) {
228         return error_mapping_to_mhu_error_t(err);
229     }
230 
231     /* First send over the size of the actual message. */
232     err = mhu_v2_x_channel_send(dev, chan, (uint32_t)size);
233     if (err != MHU_V_2_X_ERR_NONE) {
234         return error_mapping_to_mhu_error_t(err);
235     }
236     chan++;
237 
238     p = (uint32_t *)send_buffer;
239     for (i = 0; i < size; i += 4) {
240         err = mhu_v2_x_channel_send(dev, chan, *p++);
241         if (err != MHU_V_2_X_ERR_NONE) {
242             return error_mapping_to_mhu_error_t(err);
243         }
244         if (++chan == (num_channels - 1)) {
245             mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE);
246             if (mhu_err != MHU_ERR_NONE) {
247                 return mhu_err;
248             }
249             chan = 0;
250         }
251     }
252 
253     /* Signal the end of transfer.
254      *   It's not required to send a signal when the message was
255      *   perfectly-aligned ((num_channels - 1) channels were used in the last
256      *   round) preventing it from signaling twice at the end of transfer.
257      */
258     if (chan != 0) {
259         mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE);
260         if (mhu_err != MHU_ERR_NONE) {
261             return mhu_err;
262         }
263     }
264 
265     err = mhu_v2_x_close_transfer(dev);
266     return error_mapping_to_mhu_error_t(err);
267 }
268 
mhu_wait_data(void * mhu_receiver_dev)269 enum mhu_error_t mhu_wait_data(void *mhu_receiver_dev)
270 {
271     enum mhu_v2_x_error_t err;
272     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
273     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
274     uint32_t val;
275 
276     do {
277         /* Using the last channel for notifications */
278         err = mhu_v2_x_channel_receive(dev, num_channels - 1, &val);
279         if (err != MHU_V_2_X_ERR_NONE) {
280             break;
281         }
282     } while (val != MHU_NOTIFY_VALUE);
283 
284     return error_mapping_to_mhu_error_t(err);
285 }
286 
mhu_receive_data(void * mhu_receiver_dev,uint8_t * receive_buffer,size_t * size)287 enum mhu_error_t mhu_receive_data(void *mhu_receiver_dev,
288                                   uint8_t *receive_buffer,
289                                   size_t *size)
290 {
291     enum mhu_v2_x_error_t err;
292     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
293     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
294     uint32_t chan = 0;
295     uint32_t message_len;
296     uint32_t i;
297     uint32_t *p;
298 
299     if (dev == NULL || receive_buffer == NULL) {
300         return MHU_ERR_INVALID_ARG;
301     }
302 
303     /* For simplicity, require:
304      * - the receive_buffer to be 4-byte aligned,
305      * - the buffer size to be a multiple of 4.
306      */
307     if (((uintptr_t)receive_buffer & 0x3u) || (*size & 0x3u)) {
308         return MHU_ERR_INVALID_ARG;
309     }
310 
311     /* The first word is the length of the actual message. */
312     err = mhu_v2_x_channel_receive(dev, chan, &message_len);
313     if (err != MHU_V_2_X_ERR_NONE) {
314         return error_mapping_to_mhu_error_t(err);
315     }
316     chan++;
317 
318     if (message_len > *size) {
319         /* Message buffer too small */
320         *size = message_len;
321         return MHU_ERR_BUFFER_TOO_SMALL;
322     }
323 
324     p = (uint32_t *)receive_buffer;
325     for (i = 0; i < message_len; i += 4) {
326         err = mhu_v2_x_channel_receive(dev, chan, p++);
327         if (err != MHU_V_2_X_ERR_NONE) {
328             return error_mapping_to_mhu_error_t(err);
329         }
330 
331         /* Only wait for next transfer if there is still missing data. */
332         if (++chan == (num_channels - 1) && (message_len - i) > 4) {
333             /* Busy wait for next transfer */
334             err = clear_and_wait_for_signal(dev);
335             if (err != MHU_V_2_X_ERR_NONE) {
336                 return error_mapping_to_mhu_error_t(err);
337             }
338             chan = 0;
339         }
340     }
341 
342     /* Clear all channels */
343     for (i = 0; i < num_channels; ++i) {
344         err = mhu_v2_x_channel_clear(dev, i);
345         if (err != MHU_V_2_X_ERR_NONE) {
346             return error_mapping_to_mhu_error_t(err);
347         }
348     }
349 
350     *size = message_len;
351 
352     return MHU_ERR_NONE;
353 }
354