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