1 /*
2  * Copyright (c) 2022 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 static enum mhu_v2_x_error_t
signal_and_wait_for_clear(struct mhu_v2_x_dev_t * dev)48 signal_and_wait_for_clear(struct mhu_v2_x_dev_t *dev)
49 {
50     enum mhu_v2_x_error_t err;
51     uint32_t val;
52     /* Using the last channel for notifications */
53     uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
54 
55     /* FIXME: Avoid wasting a whole channel for notifying */
56     err = mhu_v2_x_channel_send(dev, channel_notify, MHU_NOTIFY_VALUE);
57     if (err != MHU_V_2_X_ERR_NONE) {
58         return err;
59     }
60 
61     do {
62         err = mhu_v2_x_channel_poll(dev, channel_notify, &val);
63         if (err != MHU_V_2_X_ERR_NONE) {
64             break;
65         }
66     } while (val != 0);
67 
68     return err;
69 }
70 
71 static enum mhu_v2_x_error_t
clear_and_wait_for_signal(struct mhu_v2_x_dev_t * dev)72 clear_and_wait_for_signal(struct mhu_v2_x_dev_t *dev)
73 {
74     enum mhu_v2_x_error_t err;
75     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
76     uint32_t val, i;
77 
78     /* Clear all channels */
79     for (i = 0; i < num_channels; ++i) {
80         err = mhu_v2_x_channel_clear(dev, i);
81         if (err != MHU_V_2_X_ERR_NONE) {
82             return err;
83         }
84     }
85 
86     do {
87         /* Using the last channel for notifications */
88         err = mhu_v2_x_channel_receive(dev, num_channels - 1, &val);
89         if (err != MHU_V_2_X_ERR_NONE) {
90             break;
91         }
92     } while (val != MHU_NOTIFY_VALUE);
93 
94     return err;
95 }
96 
mhu_init_sender(void * mhu_sender_dev)97 enum mhu_error_t mhu_init_sender(void *mhu_sender_dev)
98 {
99     enum mhu_v2_x_error_t err;
100     struct mhu_v2_x_dev_t *dev = mhu_sender_dev;
101 
102     if (dev == NULL) {
103         return MHU_ERR_INVALID_ARG;
104     }
105 
106     err = mhu_v2_x_driver_init(dev, MHU_REV_READ_FROM_HW);
107     if (err != MHU_V_2_X_ERR_NONE) {
108         return error_mapping_to_mhu_error_t(err);
109     }
110 
111     /* This wrapper requires at least two channels to be implemented */
112     if (mhu_v2_x_get_num_channel_implemented(dev) < 2) {
113         return MHU_ERR_UNSUPPORTED;
114     }
115 
116     return MHU_ERR_NONE;
117 }
118 
mhu_init_receiver(void * mhu_receiver_dev)119 enum mhu_error_t mhu_init_receiver(void *mhu_receiver_dev)
120 {
121     enum mhu_v2_x_error_t err;
122     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
123     uint32_t num_channels, i;
124 
125     if (dev == NULL) {
126         return MHU_ERR_INVALID_ARG;
127     }
128 
129     err = mhu_v2_x_driver_init(dev, MHU_REV_READ_FROM_HW);
130     if (err != MHU_V_2_X_ERR_NONE) {
131         return error_mapping_to_mhu_error_t(err);
132     }
133 
134     num_channels = mhu_v2_x_get_num_channel_implemented(dev);
135 
136     /* This wrapper requires at least two channels to be implemented */
137     if (num_channels < 2) {
138         return MHU_ERR_UNSUPPORTED;
139     }
140 
141     /* Mask all channels except the notifying channel */
142     for (i = 0; i < (num_channels - 1); ++i) {
143         err = mhu_v2_x_channel_mask_set(dev, i, UINT32_MAX);
144         if (err != MHU_V_2_X_ERR_NONE) {
145             return error_mapping_to_mhu_error_t(err);
146         }
147     }
148 
149     /* The last channel is used for notifications */
150     err = mhu_v2_x_channel_mask_clear(dev, (num_channels - 1), UINT32_MAX);
151     if (err != MHU_V_2_X_ERR_NONE) {
152         return error_mapping_to_mhu_error_t(err);
153     }
154 
155     err = mhu_v2_x_interrupt_enable(dev, MHU_2_1_INTR_CHCOMB_MASK);
156     if (err != MHU_V_2_X_ERR_NONE) {
157         return error_mapping_to_mhu_error_t(err);
158     }
159 
160     return MHU_ERR_NONE;
161 }
162 
mhu_send_data(void * mhu_sender_dev,const uint8_t * send_buffer,size_t size)163 enum mhu_error_t mhu_send_data(void *mhu_sender_dev,
164                                const uint8_t *send_buffer,
165                                size_t size)
166 {
167     enum mhu_v2_x_error_t err;
168     struct mhu_v2_x_dev_t *dev = mhu_sender_dev;
169     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
170     uint32_t chan = 0;
171     uint32_t i;
172     uint32_t *p;
173 
174     if (dev == NULL || send_buffer == NULL) {
175         return MHU_ERR_INVALID_ARG;
176     } else if (size == 0) {
177         return MHU_ERR_NONE;
178     }
179 
180     /* For simplicity, require the send_buffer to be 4-byte aligned. */
181     if ((uintptr_t)send_buffer & 0x3u) {
182         return MHU_ERR_INVALID_ARG;
183     }
184 
185     err = mhu_v2_x_initiate_transfer(dev);
186     if (err != MHU_V_2_X_ERR_NONE) {
187         return error_mapping_to_mhu_error_t(err);
188     }
189 
190     /* First send over the size of the actual message. */
191     err = mhu_v2_x_channel_send(dev, chan, (uint32_t)size);
192     if (err != MHU_V_2_X_ERR_NONE) {
193         return error_mapping_to_mhu_error_t(err);
194     }
195     chan++;
196 
197     p = (uint32_t *)send_buffer;
198     for (i = 0; i < size; i += 4) {
199         err = mhu_v2_x_channel_send(dev, chan, *p++);
200         if (err != MHU_V_2_X_ERR_NONE) {
201             return error_mapping_to_mhu_error_t(err);
202         }
203         if (++chan == (num_channels - 1)) {
204             err = signal_and_wait_for_clear(dev);
205             if (err != MHU_V_2_X_ERR_NONE) {
206                 return error_mapping_to_mhu_error_t(err);
207             }
208             chan = 0;
209         }
210     }
211 
212     /* Signal the end of transfer.
213      *   It's not required to send a signal when the message was
214      *   perfectly-aligned ((num_channels - 1) channels were used in the last
215      *   round) preventing it from signaling twice at the end of transfer.
216      */
217     if (chan != 0) {
218         err = signal_and_wait_for_clear(dev);
219         if (err != MHU_V_2_X_ERR_NONE) {
220             return error_mapping_to_mhu_error_t(err);
221         }
222     }
223 
224     err = mhu_v2_x_close_transfer(dev);
225     return error_mapping_to_mhu_error_t(err);
226 }
227 
mhu_receive_data(void * mhu_receiver_dev,uint8_t * receive_buffer,size_t * size)228 enum mhu_error_t mhu_receive_data(void *mhu_receiver_dev,
229                                   uint8_t *receive_buffer,
230                                   size_t *size)
231 {
232     enum mhu_v2_x_error_t err;
233     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
234     uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
235     uint32_t chan = 0;
236     uint32_t message_len;
237     uint32_t i;
238     uint32_t *p;
239 
240     if (dev == NULL || receive_buffer == NULL) {
241         return MHU_ERR_INVALID_ARG;
242     }
243 
244     /* For simplicity, require:
245      * - the receive_buffer to be 4-byte aligned,
246      * - the buffer size to be a multiple of 4.
247      */
248     if (((uintptr_t)receive_buffer & 0x3u) || (*size & 0x3u)) {
249         return MHU_ERR_INVALID_ARG;
250     }
251 
252     /* The first word is the length of the actual message. */
253     err = mhu_v2_x_channel_receive(dev, chan, &message_len);
254     if (err != MHU_V_2_X_ERR_NONE) {
255         return error_mapping_to_mhu_error_t(err);
256     }
257     chan++;
258 
259     if (message_len > *size) {
260         /* Message buffer too small */
261         *size = message_len;
262         return MHU_ERR_BUFFER_TOO_SMALL;
263     }
264 
265     p = (uint32_t *)receive_buffer;
266     for (i = 0; i < message_len; i += 4) {
267         err = mhu_v2_x_channel_receive(dev, chan, p++);
268         if (err != MHU_V_2_X_ERR_NONE) {
269             return error_mapping_to_mhu_error_t(err);
270         }
271 
272         /* Only wait for next transfer if there is still missing data. */
273         if (++chan == (num_channels - 1) && (message_len - i) > 4) {
274             /* Busy wait for next transfer */
275             err = clear_and_wait_for_signal(dev);
276             if (err != MHU_V_2_X_ERR_NONE) {
277                 return error_mapping_to_mhu_error_t(err);
278             }
279             chan = 0;
280         }
281     }
282 
283     /* Clear all channels */
284     for (i = 0; i < num_channels; ++i) {
285         err = mhu_v2_x_channel_clear(dev, i);
286         if (err != MHU_V_2_X_ERR_NONE) {
287             return error_mapping_to_mhu_error_t(err);
288         }
289     }
290 
291     *size = message_len;
292 
293     return MHU_ERR_NONE;
294 }
295