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