1af26d7d6STamas Ban /*
2af26d7d6STamas Ban * Copyright (c) 2022, Arm Limited. All rights reserved.
3af26d7d6STamas Ban *
4af26d7d6STamas Ban * SPDX-License-Identifier: BSD-3-Clause
5af26d7d6STamas Ban */
6af26d7d6STamas Ban
7af26d7d6STamas Ban #include <assert.h>
8af26d7d6STamas Ban #include <stddef.h>
9af26d7d6STamas Ban #include <stdint.h>
10af26d7d6STamas Ban #include <string.h>
11af26d7d6STamas Ban
12af26d7d6STamas Ban #include <drivers/arm/mhu.h>
13af26d7d6STamas Ban
14af26d7d6STamas Ban #include "mhu_v2_x.h"
15af26d7d6STamas Ban
16af26d7d6STamas Ban #define MHU_NOTIFY_VALUE (1234u)
17af26d7d6STamas Ban
18af26d7d6STamas Ban /*
19af26d7d6STamas Ban * MHU devices for host:
20af26d7d6STamas Ban * HSE: Host to Secure Enclave (sender device)
21af26d7d6STamas Ban * SEH: Secure Enclave to Host (receiver device)
22af26d7d6STamas Ban */
23af26d7d6STamas Ban struct mhu_v2_x_dev_t MHU1_HSE_DEV = {0, MHU_V2_X_SENDER_FRAME};
24af26d7d6STamas Ban struct mhu_v2_x_dev_t MHU1_SEH_DEV = {0, MHU_V2_X_RECEIVER_FRAME};
25af26d7d6STamas Ban
error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err)26af26d7d6STamas Ban static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err)
27af26d7d6STamas Ban {
28af26d7d6STamas Ban switch (err) {
29af26d7d6STamas Ban case MHU_V_2_X_ERR_NONE:
30af26d7d6STamas Ban return MHU_ERR_NONE;
31af26d7d6STamas Ban case MHU_V_2_X_ERR_NOT_INIT:
32af26d7d6STamas Ban return MHU_ERR_NOT_INIT;
33af26d7d6STamas Ban case MHU_V_2_X_ERR_ALREADY_INIT:
34af26d7d6STamas Ban return MHU_ERR_ALREADY_INIT;
35af26d7d6STamas Ban case MHU_V_2_X_ERR_UNSUPPORTED_VERSION:
36af26d7d6STamas Ban return MHU_ERR_UNSUPPORTED_VERSION;
37af26d7d6STamas Ban case MHU_V_2_X_ERR_INVALID_ARG:
38af26d7d6STamas Ban return MHU_ERR_INVALID_ARG;
39af26d7d6STamas Ban case MHU_V_2_X_ERR_GENERAL:
40af26d7d6STamas Ban return MHU_ERR_GENERAL;
41af26d7d6STamas Ban default:
42af26d7d6STamas Ban return MHU_ERR_GENERAL;
43af26d7d6STamas Ban }
44af26d7d6STamas Ban }
45af26d7d6STamas Ban
signal_and_wait_for_clear(void)46af26d7d6STamas Ban static enum mhu_v2_x_error_t signal_and_wait_for_clear(void)
47af26d7d6STamas Ban {
48af26d7d6STamas Ban enum mhu_v2_x_error_t err;
49af26d7d6STamas Ban struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV;
50af26d7d6STamas Ban uint32_t val = MHU_NOTIFY_VALUE;
51af26d7d6STamas Ban /* Using the last channel for notifications */
52af26d7d6STamas Ban uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
53af26d7d6STamas Ban
54af26d7d6STamas Ban err = mhu_v2_x_channel_send(dev, channel_notify, val);
55af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
56af26d7d6STamas Ban return err;
57af26d7d6STamas Ban }
58af26d7d6STamas Ban
59af26d7d6STamas Ban do {
60af26d7d6STamas Ban err = mhu_v2_x_channel_poll(dev, channel_notify, &val);
61af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
62af26d7d6STamas Ban break;
63af26d7d6STamas Ban }
64af26d7d6STamas Ban } while (val != 0);
65af26d7d6STamas Ban
66af26d7d6STamas Ban return err;
67af26d7d6STamas Ban }
68af26d7d6STamas Ban
wait_for_signal(void)69af26d7d6STamas Ban static enum mhu_v2_x_error_t wait_for_signal(void)
70af26d7d6STamas Ban {
71af26d7d6STamas Ban enum mhu_v2_x_error_t err;
72af26d7d6STamas Ban struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
73af26d7d6STamas Ban uint32_t val = 0;
74af26d7d6STamas Ban /* Using the last channel for notifications */
75af26d7d6STamas Ban uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
76af26d7d6STamas Ban
77af26d7d6STamas Ban do {
78af26d7d6STamas Ban err = mhu_v2_x_channel_receive(dev, channel_notify, &val);
79af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
80af26d7d6STamas Ban break;
81af26d7d6STamas Ban }
82af26d7d6STamas Ban } while (val != MHU_NOTIFY_VALUE);
83af26d7d6STamas Ban
84af26d7d6STamas Ban return err;
85af26d7d6STamas Ban }
86af26d7d6STamas Ban
clear_and_wait_for_next_signal(void)87af26d7d6STamas Ban static enum mhu_v2_x_error_t clear_and_wait_for_next_signal(void)
88af26d7d6STamas Ban {
89af26d7d6STamas Ban enum mhu_v2_x_error_t err;
90af26d7d6STamas Ban struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
91af26d7d6STamas Ban uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
92af26d7d6STamas Ban uint32_t i;
93af26d7d6STamas Ban
94af26d7d6STamas Ban /* Clear all channels */
95af26d7d6STamas Ban for (i = 0; i < num_channels; ++i) {
96af26d7d6STamas Ban err = mhu_v2_x_channel_clear(dev, i);
97af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
98af26d7d6STamas Ban return err;
99af26d7d6STamas Ban }
100af26d7d6STamas Ban }
101af26d7d6STamas Ban
102af26d7d6STamas Ban return wait_for_signal();
103af26d7d6STamas Ban }
104af26d7d6STamas Ban
mhu_init_sender(uintptr_t mhu_sender_base)105af26d7d6STamas Ban enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base)
106af26d7d6STamas Ban {
107af26d7d6STamas Ban enum mhu_v2_x_error_t err;
108af26d7d6STamas Ban
109af26d7d6STamas Ban assert(mhu_sender_base != (uintptr_t)NULL);
110af26d7d6STamas Ban
111af26d7d6STamas Ban MHU1_HSE_DEV.base = mhu_sender_base;
112af26d7d6STamas Ban
113af26d7d6STamas Ban err = mhu_v2_x_driver_init(&MHU1_HSE_DEV, MHU_REV_READ_FROM_HW);
114af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
115af26d7d6STamas Ban }
116af26d7d6STamas Ban
mhu_init_receiver(uintptr_t mhu_receiver_base)117af26d7d6STamas Ban enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base)
118af26d7d6STamas Ban {
119af26d7d6STamas Ban enum mhu_v2_x_error_t err;
120af26d7d6STamas Ban uint32_t num_channels, i;
121af26d7d6STamas Ban
122af26d7d6STamas Ban assert(mhu_receiver_base != (uintptr_t)NULL);
123af26d7d6STamas Ban
124af26d7d6STamas Ban MHU1_SEH_DEV.base = mhu_receiver_base;
125af26d7d6STamas Ban
126af26d7d6STamas Ban err = mhu_v2_x_driver_init(&MHU1_SEH_DEV, MHU_REV_READ_FROM_HW);
127af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
128af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
129af26d7d6STamas Ban }
130af26d7d6STamas Ban
131af26d7d6STamas Ban num_channels = mhu_v2_x_get_num_channel_implemented(&MHU1_SEH_DEV);
132af26d7d6STamas Ban
133af26d7d6STamas Ban /* Mask all channels except the notifying channel */
134af26d7d6STamas Ban for (i = 0; i < (num_channels - 1); ++i) {
135af26d7d6STamas Ban err = mhu_v2_x_channel_mask_set(&MHU1_SEH_DEV, i, UINT32_MAX);
136af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
137af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
138af26d7d6STamas Ban }
139af26d7d6STamas Ban }
140af26d7d6STamas Ban
141af26d7d6STamas Ban /* The last channel is used for notifications */
142af26d7d6STamas Ban err = mhu_v2_x_channel_mask_clear(
143af26d7d6STamas Ban &MHU1_SEH_DEV, (num_channels - 1), UINT32_MAX);
144af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
145af26d7d6STamas Ban }
146af26d7d6STamas Ban
147af26d7d6STamas Ban /*
148af26d7d6STamas Ban * Public function. See mhu.h
149af26d7d6STamas Ban *
150af26d7d6STamas Ban * The basic steps of transferring a message:
151af26d7d6STamas Ban * 1. Initiate MHU transfer.
152af26d7d6STamas Ban * 2. Send over the size of the payload on Channel 1. It is the very first
153af26d7d6STamas Ban * 4 Bytes of the transfer. Continue with Channel 2.
154af26d7d6STamas Ban * 3. Send over the payload, writing the channels one after the other
155af26d7d6STamas Ban * (4 Bytes each). The last available channel is reserved for controlling
156af26d7d6STamas Ban * the transfer.
157af26d7d6STamas Ban * When the last channel is reached or no more data is left, STOP.
158af26d7d6STamas Ban * 4. Notify the receiver using the last channel and wait for acknowledge.
159af26d7d6STamas Ban * If there is still data to transfer, jump to step 3. Otherwise, proceed.
160af26d7d6STamas Ban * 5. Close MHU transfer.
161af26d7d6STamas Ban *
162af26d7d6STamas Ban */
mhu_send_data(const uint8_t * send_buffer,size_t size)163af26d7d6STamas Ban enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size)
164af26d7d6STamas Ban {
165af26d7d6STamas Ban enum mhu_v2_x_error_t err;
166af26d7d6STamas Ban struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV;
167af26d7d6STamas Ban uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
168af26d7d6STamas Ban uint32_t chan = 0;
169af26d7d6STamas Ban uint32_t i;
170af26d7d6STamas Ban uint32_t *p;
171af26d7d6STamas Ban
172af26d7d6STamas Ban /* For simplicity, require the send_buffer to be 4-byte aligned */
173af26d7d6STamas Ban if ((uintptr_t)send_buffer & 0x3U) {
174af26d7d6STamas Ban return MHU_ERR_INVALID_ARG;
175af26d7d6STamas Ban }
176af26d7d6STamas Ban
177af26d7d6STamas Ban err = mhu_v2_x_initiate_transfer(dev);
178af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
179af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
180af26d7d6STamas Ban }
181af26d7d6STamas Ban
182af26d7d6STamas Ban /* First send over the size of the actual message */
183af26d7d6STamas Ban err = mhu_v2_x_channel_send(dev, chan, (uint32_t)size);
184af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
185af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
186af26d7d6STamas Ban }
187af26d7d6STamas Ban chan++;
188af26d7d6STamas Ban
189af26d7d6STamas Ban p = (uint32_t *)send_buffer;
190af26d7d6STamas Ban for (i = 0; i < size; i += 4) {
191af26d7d6STamas Ban err = mhu_v2_x_channel_send(dev, chan, *p++);
192af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
193af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
194af26d7d6STamas Ban }
195af26d7d6STamas Ban if (++chan == (num_channels - 1)) {
196af26d7d6STamas Ban err = signal_and_wait_for_clear();
197af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
198af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
199af26d7d6STamas Ban }
200af26d7d6STamas Ban chan = 0;
201af26d7d6STamas Ban }
202af26d7d6STamas Ban }
203af26d7d6STamas Ban
204af26d7d6STamas Ban /* Signal the end of transfer.
205af26d7d6STamas Ban * It's not required to send a signal when the message was
206af26d7d6STamas Ban * perfectly-aligned (num_channels - 1 channels were used in the last
207af26d7d6STamas Ban * round) preventing it from signaling twice at the end of transfer.
208af26d7d6STamas Ban */
209af26d7d6STamas Ban if (chan != 0) {
210af26d7d6STamas Ban err = signal_and_wait_for_clear();
211af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
212af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
213af26d7d6STamas Ban }
214af26d7d6STamas Ban }
215af26d7d6STamas Ban
216af26d7d6STamas Ban err = mhu_v2_x_close_transfer(dev);
217af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
218af26d7d6STamas Ban }
219af26d7d6STamas Ban
220af26d7d6STamas Ban /*
221af26d7d6STamas Ban * Public function. See mhu.h
222af26d7d6STamas Ban *
223af26d7d6STamas Ban * The basic steps of receiving a message:
224af26d7d6STamas Ban * 1. Read the size of the payload from Channel 1. It is the very first
225af26d7d6STamas Ban * 4 Bytes of the transfer. Continue with Channel 2.
226af26d7d6STamas Ban * 2. Receive the payload, read the channels one after the other
227af26d7d6STamas Ban * (4 Bytes each). The last available channel is reserved for controlling
228af26d7d6STamas Ban * the transfer.
229af26d7d6STamas Ban * When the last channel is reached clear all the channels
230af26d7d6STamas Ban * (also sending an acknowledge on the last channel).
231af26d7d6STamas Ban * 3. If there is still data to receive wait for a notification on the last
232af26d7d6STamas Ban * channel and jump to step 2 as soon as it arrived. Otherwise, proceed.
233af26d7d6STamas Ban * 4. End of transfer.
234af26d7d6STamas Ban *
235af26d7d6STamas Ban */
mhu_receive_data(uint8_t * receive_buffer,size_t * size)236af26d7d6STamas Ban enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size)
237af26d7d6STamas Ban {
238af26d7d6STamas Ban enum mhu_v2_x_error_t err;
239af26d7d6STamas Ban struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
240af26d7d6STamas Ban uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
241af26d7d6STamas Ban uint32_t chan = 0;
242af26d7d6STamas Ban uint32_t message_len;
243af26d7d6STamas Ban uint32_t i;
244af26d7d6STamas Ban uint32_t *p;
245af26d7d6STamas Ban
246af26d7d6STamas Ban /* For simplicity, require:
247af26d7d6STamas Ban * - the receive_buffer to be 4-byte aligned,
248af26d7d6STamas Ban * - the buffer size to be a multiple of 4.
249af26d7d6STamas Ban */
250af26d7d6STamas Ban if (((uintptr_t)receive_buffer & 0x3U) || (*size & 0x3U)) {
251af26d7d6STamas Ban return MHU_ERR_INVALID_ARG;
252af26d7d6STamas Ban }
253af26d7d6STamas Ban
254af26d7d6STamas Ban /* Busy wait for incoming reply */
255af26d7d6STamas Ban err = wait_for_signal();
256af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
257af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
258af26d7d6STamas Ban }
259af26d7d6STamas Ban
260af26d7d6STamas Ban /* The first word is the length of the actual message */
261af26d7d6STamas Ban err = mhu_v2_x_channel_receive(dev, chan, &message_len);
262af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
263af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
264af26d7d6STamas Ban }
265af26d7d6STamas Ban chan++;
266af26d7d6STamas Ban
267af26d7d6STamas Ban if (message_len > *size) {
268af26d7d6STamas Ban /* Message buffer too small */
269af26d7d6STamas Ban *size = message_len;
270af26d7d6STamas Ban return MHU_ERR_BUFFER_TOO_SMALL;
271af26d7d6STamas Ban }
272af26d7d6STamas Ban
273af26d7d6STamas Ban p = (uint32_t *)receive_buffer;
274af26d7d6STamas Ban for (i = 0; i < message_len; i += 4) {
275af26d7d6STamas Ban err = mhu_v2_x_channel_receive(dev, chan, p++);
276af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
277af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
278af26d7d6STamas Ban }
279af26d7d6STamas Ban
280af26d7d6STamas Ban /* Only wait for next transfer if there is still missing data */
281af26d7d6STamas Ban if (++chan == (num_channels - 1) && (message_len - i) > 4) {
282af26d7d6STamas Ban /* Busy wait for next transfer */
283af26d7d6STamas Ban err = clear_and_wait_for_next_signal();
284af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
285af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
286af26d7d6STamas Ban }
287af26d7d6STamas Ban chan = 0;
288af26d7d6STamas Ban }
289af26d7d6STamas Ban }
290af26d7d6STamas Ban
291af26d7d6STamas Ban /* Clear all channels */
292af26d7d6STamas Ban for (i = 0; i < num_channels; ++i) {
293af26d7d6STamas Ban err = mhu_v2_x_channel_clear(dev, i);
294af26d7d6STamas Ban if (err != MHU_V_2_X_ERR_NONE) {
295af26d7d6STamas Ban return error_mapping_to_mhu_error_t(err);
296af26d7d6STamas Ban }
297af26d7d6STamas Ban }
298af26d7d6STamas Ban
299af26d7d6STamas Ban *size = message_len;
300af26d7d6STamas Ban
301af26d7d6STamas Ban return MHU_ERR_NONE;
302af26d7d6STamas Ban }
30331259019SRaef Coles
mhu_get_max_message_size(void)30431259019SRaef Coles size_t mhu_get_max_message_size(void)
30531259019SRaef Coles {
30631259019SRaef Coles struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
30731259019SRaef Coles uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
30831259019SRaef Coles
30931259019SRaef Coles assert(num_channels != 0);
31031259019SRaef Coles
311*5cd10848SSathyam Panda /*
312*5cd10848SSathyam Panda * Returns only usable size of memory. As one channel is specifically
313*5cd10848SSathyam Panda * used to inform about the size of payload, discard it from avialable
314*5cd10848SSathyam Panda * memory size.
315*5cd10848SSathyam Panda */
316*5cd10848SSathyam Panda return (num_channels - 1) * sizeof(uint32_t);
31731259019SRaef Coles }
318