14b4f8505SAziz IDOMAR /* 24b4f8505SAziz IDOMAR * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. 34b4f8505SAziz IDOMAR * 44b4f8505SAziz IDOMAR * SPDX-License-Identifier: BSD-3-Clause 54b4f8505SAziz IDOMAR */ 64b4f8505SAziz IDOMAR 74b4f8505SAziz IDOMAR #include <assert.h> 84b4f8505SAziz IDOMAR #include <stddef.h> 94b4f8505SAziz IDOMAR #include <stdint.h> 104b4f8505SAziz IDOMAR #include <string.h> 114b4f8505SAziz IDOMAR 124b4f8505SAziz IDOMAR #include <drivers/arm/mhu.h> 134b4f8505SAziz IDOMAR 144b4f8505SAziz IDOMAR #include "mhu_v3_x.h" 154b4f8505SAziz IDOMAR 164b4f8505SAziz IDOMAR #define MHU_NOTIFY_VALUE U(1234) 174b4f8505SAziz IDOMAR 184b4f8505SAziz IDOMAR #ifndef ALIGN_UP 194b4f8505SAziz IDOMAR #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) 204b4f8505SAziz IDOMAR #endif 214b4f8505SAziz IDOMAR 224b4f8505SAziz IDOMAR /* 234b4f8505SAziz IDOMAR * MHUv3 Wrapper utility macros 244b4f8505SAziz IDOMAR */ 254b4f8505SAziz IDOMAR #define IS_ALIGNED(val, align) (val == ALIGN_UP(val, align)) 264b4f8505SAziz IDOMAR 274b4f8505SAziz IDOMAR /* 284b4f8505SAziz IDOMAR * MHU devices for host: 294b4f8505SAziz IDOMAR * HSE: Host to Secure Enclave (sender device) 304b4f8505SAziz IDOMAR * SEH: Secure Enclave to Host (receiver device) 314b4f8505SAziz IDOMAR */ 324b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t mhu_hse_dev = {0, MHU_V3_X_PBX_FRAME}; 334b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t mhu_seh_dev = {0, MHU_V3_X_MBX_FRAME}; 344b4f8505SAziz IDOMAR 354b4f8505SAziz IDOMAR /* MHUv3 driver error to MHUv3 wrapper error mapping */ 364b4f8505SAziz IDOMAR static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v3_x_error_t err) 374b4f8505SAziz IDOMAR { 384b4f8505SAziz IDOMAR switch (err) { 394b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_NONE: 404b4f8505SAziz IDOMAR return MHU_ERR_NONE; 414b4f8505SAziz IDOMAR 424b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_NOT_INIT: 434b4f8505SAziz IDOMAR return MHU_ERR_NOT_INIT; 444b4f8505SAziz IDOMAR 454b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_UNSUPPORTED_VERSION: 464b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED_VERSION; 474b4f8505SAziz IDOMAR 484b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_UNSUPPORTED: 494b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 504b4f8505SAziz IDOMAR 514b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_INVALID_PARAM: 524b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 534b4f8505SAziz IDOMAR 544b4f8505SAziz IDOMAR default: 554b4f8505SAziz IDOMAR return MHU_ERR_GENERAL; 564b4f8505SAziz IDOMAR } 574b4f8505SAziz IDOMAR } 584b4f8505SAziz IDOMAR 594b4f8505SAziz IDOMAR static enum mhu_error_t signal_and_wait_for_clear( 604b4f8505SAziz IDOMAR void *mhu_sender_dev, uint32_t value) 614b4f8505SAziz IDOMAR { 624b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 634b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 644b4f8505SAziz IDOMAR uint8_t num_channels; 654b4f8505SAziz IDOMAR uint32_t read_val; 664b4f8505SAziz IDOMAR 674b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev; 684b4f8505SAziz IDOMAR 694b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 704b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 714b4f8505SAziz IDOMAR } 724b4f8505SAziz IDOMAR 734b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 744b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 754b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 764b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 774b4f8505SAziz IDOMAR } 784b4f8505SAziz IDOMAR 794b4f8505SAziz IDOMAR /* Wait for any pending acknowledgment from transmitter side */ 804b4f8505SAziz IDOMAR do { 814b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 824b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 834b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 844b4f8505SAziz IDOMAR } 854b4f8505SAziz IDOMAR } while ((read_val & value) == value); 864b4f8505SAziz IDOMAR 874b4f8505SAziz IDOMAR /* Use the last channel to notify that a transfer is ready */ 884b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_write(dev, num_channels - 1, value); 894b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 904b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 914b4f8505SAziz IDOMAR } 924b4f8505SAziz IDOMAR 934b4f8505SAziz IDOMAR /* Wait until receiver side acknowledges the transfer */ 944b4f8505SAziz IDOMAR do { 954b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 964b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 974b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 984b4f8505SAziz IDOMAR } 994b4f8505SAziz IDOMAR } while ((read_val & value) == value); 1004b4f8505SAziz IDOMAR 1014b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE); 1024b4f8505SAziz IDOMAR } 1034b4f8505SAziz IDOMAR 1044b4f8505SAziz IDOMAR static enum mhu_error_t wait_for_signal( 1054b4f8505SAziz IDOMAR void *mhu_receiver_dev, uint32_t value) 1064b4f8505SAziz IDOMAR { 1074b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 1084b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 1094b4f8505SAziz IDOMAR uint32_t read_val; 1104b4f8505SAziz IDOMAR uint8_t num_channels; 1114b4f8505SAziz IDOMAR 1124b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; 1134b4f8505SAziz IDOMAR 1144b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 1154b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 1164b4f8505SAziz IDOMAR } 1174b4f8505SAziz IDOMAR 1184b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 1194b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 1204b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1214b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1224b4f8505SAziz IDOMAR } 1234b4f8505SAziz IDOMAR 1244b4f8505SAziz IDOMAR do { 1254b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 1264b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1274b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1284b4f8505SAziz IDOMAR } 1294b4f8505SAziz IDOMAR } while (read_val != value); 1304b4f8505SAziz IDOMAR 1314b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1324b4f8505SAziz IDOMAR } 1334b4f8505SAziz IDOMAR 1344b4f8505SAziz IDOMAR static enum mhu_error_t clear_and_wait_for_signal( 1354b4f8505SAziz IDOMAR void *mhu_receiver_dev, uint32_t value) 1364b4f8505SAziz IDOMAR { 1374b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 1384b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 1394b4f8505SAziz IDOMAR uint8_t num_channels; 1404b4f8505SAziz IDOMAR 1414b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; 1424b4f8505SAziz IDOMAR 1434b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 1444b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 1454b4f8505SAziz IDOMAR } 1464b4f8505SAziz IDOMAR 1474b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 1484b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 1494b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1504b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1514b4f8505SAziz IDOMAR } 1524b4f8505SAziz IDOMAR 1534b4f8505SAziz IDOMAR /* Clear all channels */ 1544b4f8505SAziz IDOMAR for (int i = 0; i < num_channels; i++) { 1554b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); 1564b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1574b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1584b4f8505SAziz IDOMAR } 1594b4f8505SAziz IDOMAR } 1604b4f8505SAziz IDOMAR 1614b4f8505SAziz IDOMAR return wait_for_signal(mhu_receiver_dev, value); 1624b4f8505SAziz IDOMAR } 1634b4f8505SAziz IDOMAR 1644b4f8505SAziz IDOMAR static enum mhu_error_t validate_buffer_params(uintptr_t buf_addr) 1654b4f8505SAziz IDOMAR { 1664b4f8505SAziz IDOMAR if ((buf_addr == 0) || (!IS_ALIGNED(buf_addr, sizeof(uint32_t)))) { 1674b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 1684b4f8505SAziz IDOMAR } 1694b4f8505SAziz IDOMAR 1704b4f8505SAziz IDOMAR return MHU_ERR_NONE; 1714b4f8505SAziz IDOMAR } 1724b4f8505SAziz IDOMAR 1734b4f8505SAziz IDOMAR enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base) 1744b4f8505SAziz IDOMAR { 1754b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 1764b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 1774b4f8505SAziz IDOMAR uint8_t num_ch; 1784b4f8505SAziz IDOMAR uint32_t ch; 1794b4f8505SAziz IDOMAR 1804b4f8505SAziz IDOMAR assert(mhu_sender_base != (uintptr_t)NULL); 1814b4f8505SAziz IDOMAR 1824b4f8505SAziz IDOMAR mhu_hse_dev.base = mhu_sender_base; 1834b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; 1844b4f8505SAziz IDOMAR 1854b4f8505SAziz IDOMAR /* Initialize MHUv3 */ 1864b4f8505SAziz IDOMAR err = mhu_v3_x_driver_init(dev); 1874b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1884b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1894b4f8505SAziz IDOMAR } 1904b4f8505SAziz IDOMAR 1914b4f8505SAziz IDOMAR /* Read the number of doorbell channels implemented in the MHU */ 1924b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented( 1934b4f8505SAziz IDOMAR dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); 1944b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 1954b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 1964b4f8505SAziz IDOMAR } else if (num_ch < 2) { 1974b4f8505SAziz IDOMAR /* This wrapper requires at least two channels implemented */ 1984b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 1994b4f8505SAziz IDOMAR } 2004b4f8505SAziz IDOMAR 2014b4f8505SAziz IDOMAR /* 2024b4f8505SAziz IDOMAR * The sender polls the postbox doorbell channel window status register 2034b4f8505SAziz IDOMAR * to get notified about successful transfer. So, disable the doorbell 2044b4f8505SAziz IDOMAR * channel's contribution to postbox combined interrupt. 2054b4f8505SAziz IDOMAR * 2064b4f8505SAziz IDOMAR * Also, clear and disable the postbox doorbell channel transfer 2074b4f8505SAziz IDOMAR * acknowledge interrupt. 2084b4f8505SAziz IDOMAR */ 2094b4f8505SAziz IDOMAR for (ch = 0; ch < num_ch; ch++) { 2104b4f8505SAziz IDOMAR err = mhu_v3_x_channel_interrupt_disable( 2114b4f8505SAziz IDOMAR dev, ch, MHU_V3_X_CHANNEL_TYPE_DBCH); 2124b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2134b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2144b4f8505SAziz IDOMAR } 2154b4f8505SAziz IDOMAR } 2164b4f8505SAziz IDOMAR 2174b4f8505SAziz IDOMAR return MHU_ERR_NONE; 2184b4f8505SAziz IDOMAR } 2194b4f8505SAziz IDOMAR 2204b4f8505SAziz IDOMAR enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base) 2214b4f8505SAziz IDOMAR { 2224b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 2234b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 2244b4f8505SAziz IDOMAR uint32_t ch; 2254b4f8505SAziz IDOMAR uint8_t num_ch; 2264b4f8505SAziz IDOMAR 2274b4f8505SAziz IDOMAR assert(mhu_receiver_base != (uintptr_t)NULL); 2284b4f8505SAziz IDOMAR 2294b4f8505SAziz IDOMAR mhu_seh_dev.base = mhu_receiver_base; 2304b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; 2314b4f8505SAziz IDOMAR 2324b4f8505SAziz IDOMAR /* Initialize MHUv3 */ 2334b4f8505SAziz IDOMAR err = mhu_v3_x_driver_init(dev); 2344b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2354b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2364b4f8505SAziz IDOMAR } 2374b4f8505SAziz IDOMAR 2384b4f8505SAziz IDOMAR /* Read the number of doorbell channels implemented in the MHU */ 2394b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented( 2404b4f8505SAziz IDOMAR dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); 2414b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2424b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2434b4f8505SAziz IDOMAR } else if (num_ch < 2) { 2444b4f8505SAziz IDOMAR /* This wrapper requires at least two channels implemented */ 2454b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 2464b4f8505SAziz IDOMAR } 2474b4f8505SAziz IDOMAR 2484b4f8505SAziz IDOMAR /* Mask all channels except the notifying channel */ 2494b4f8505SAziz IDOMAR for (ch = 0; ch < (num_ch - 1); ch++) { 2504b4f8505SAziz IDOMAR /* Mask interrupts on channels used for data */ 2514b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_mask_set(dev, ch, UINT32_MAX); 2524b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2534b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2544b4f8505SAziz IDOMAR } 2554b4f8505SAziz IDOMAR } 2564b4f8505SAziz IDOMAR 2574b4f8505SAziz IDOMAR /* Unmask doorbell notification channel interrupt */ 2584b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_mask_clear(dev, (num_ch - 1), UINT32_MAX); 2594b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2604b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2614b4f8505SAziz IDOMAR } 2624b4f8505SAziz IDOMAR 2634b4f8505SAziz IDOMAR /* 2644b4f8505SAziz IDOMAR * Enable the doorbell channel's contribution to mailbox combined 2654b4f8505SAziz IDOMAR * interrupt. 2664b4f8505SAziz IDOMAR */ 2674b4f8505SAziz IDOMAR err = mhu_v3_x_channel_interrupt_enable(dev, (num_ch - 1), 2684b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH); 2694b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 2704b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 2714b4f8505SAziz IDOMAR } 2724b4f8505SAziz IDOMAR 2734b4f8505SAziz IDOMAR return MHU_ERR_NONE; 2744b4f8505SAziz IDOMAR } 2754b4f8505SAziz IDOMAR 2764b4f8505SAziz IDOMAR /* 2774b4f8505SAziz IDOMAR * Public function. See mhu.h 2784b4f8505SAziz IDOMAR * 2794b4f8505SAziz IDOMAR * The basic steps of transferring a message: 2804b4f8505SAziz IDOMAR * 1. Send the size of the payload on Channel 0. It is the very first Bytes of 2814b4f8505SAziz IDOMAR * the transfer. Continue with Channel 1. 2824b4f8505SAziz IDOMAR * 2. Send the payload, writing the channels one after the other (4 Bytes 2834b4f8505SAziz IDOMAR * each). The last available channel is reserved for controlling the 2844b4f8505SAziz IDOMAR * transfer. When the last channel is reached or no more data is left, STOP. 2854b4f8505SAziz IDOMAR * 3. Notify the receiver using the last channel and wait for acknowledge. If 2864b4f8505SAziz IDOMAR * there is still data to transfer, jump to step 2. Otherwise, proceed. 2874b4f8505SAziz IDOMAR * 2884b4f8505SAziz IDOMAR */ 2894b4f8505SAziz IDOMAR enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size) 2904b4f8505SAziz IDOMAR { 2914b4f8505SAziz IDOMAR enum mhu_error_t mhu_err; 2924b4f8505SAziz IDOMAR enum mhu_v3_x_error_t mhu_v3_err; 2934b4f8505SAziz IDOMAR uint8_t num_channels; 2944b4f8505SAziz IDOMAR uint8_t chan; 2954b4f8505SAziz IDOMAR uint32_t *buffer; 2964b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 2974b4f8505SAziz IDOMAR 2984b4f8505SAziz IDOMAR if (size == 0) { 2994b4f8505SAziz IDOMAR return MHU_ERR_NONE; 3004b4f8505SAziz IDOMAR } 3014b4f8505SAziz IDOMAR 3024b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; 3034b4f8505SAziz IDOMAR chan = 0; 3044b4f8505SAziz IDOMAR 3054b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 3064b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 3074b4f8505SAziz IDOMAR } 3084b4f8505SAziz IDOMAR 3094b4f8505SAziz IDOMAR mhu_err = validate_buffer_params((uintptr_t)send_buffer); 3104b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 3114b4f8505SAziz IDOMAR return mhu_err; 3124b4f8505SAziz IDOMAR } 3134b4f8505SAziz IDOMAR 3144b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, 3154b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 3164b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 3174b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 3184b4f8505SAziz IDOMAR } 3194b4f8505SAziz IDOMAR 3204b4f8505SAziz IDOMAR /* First send the size of the actual message. */ 3214b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, (uint32_t)size); 3224b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 3234b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 3244b4f8505SAziz IDOMAR } 3254b4f8505SAziz IDOMAR chan++; 3264b4f8505SAziz IDOMAR 3274b4f8505SAziz IDOMAR buffer = (uint32_t *)send_buffer; 3284b4f8505SAziz IDOMAR for (size_t i = 0; i < size; i += 4) { 3294b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, *buffer++); 3304b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 3314b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 3324b4f8505SAziz IDOMAR } 3334b4f8505SAziz IDOMAR 3344b4f8505SAziz IDOMAR if (++chan == (num_channels - 1)) { 3354b4f8505SAziz IDOMAR /* Use the last channel to notify transfer complete */ 3364b4f8505SAziz IDOMAR mhu_err = signal_and_wait_for_clear( 3374b4f8505SAziz IDOMAR dev, MHU_NOTIFY_VALUE); 3384b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 3394b4f8505SAziz IDOMAR return mhu_err; 3404b4f8505SAziz IDOMAR } 3414b4f8505SAziz IDOMAR chan = 0; 3424b4f8505SAziz IDOMAR } 3434b4f8505SAziz IDOMAR } 3444b4f8505SAziz IDOMAR 3454b4f8505SAziz IDOMAR if (chan != 0) { 3464b4f8505SAziz IDOMAR /* Use the last channel to notify transfer complete */ 3474b4f8505SAziz IDOMAR mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE); 3484b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 3494b4f8505SAziz IDOMAR return mhu_err; 3504b4f8505SAziz IDOMAR } 3514b4f8505SAziz IDOMAR } 3524b4f8505SAziz IDOMAR 3534b4f8505SAziz IDOMAR return MHU_ERR_NONE; 3544b4f8505SAziz IDOMAR } 3554b4f8505SAziz IDOMAR 3564b4f8505SAziz IDOMAR /* 3574b4f8505SAziz IDOMAR * Public function. See mhu.h 3584b4f8505SAziz IDOMAR * 3594b4f8505SAziz IDOMAR * The basic steps of receiving a message: 3604b4f8505SAziz IDOMAR * 1. Read the size of the payload from Channel 0. It is the very first 3614b4f8505SAziz IDOMAR * 4 Bytes of the transfer. Continue with Channel 1. 3624b4f8505SAziz IDOMAR * 2. Receive the payload, read the channels one after the other 3634b4f8505SAziz IDOMAR * (4 Bytes each). The last available channel is reserved for controlling 3644b4f8505SAziz IDOMAR * the transfer. 3654b4f8505SAziz IDOMAR * When the last channel is reached clear all the channels 3664b4f8505SAziz IDOMAR * (also sending an acknowledge on the last channel). 3674b4f8505SAziz IDOMAR * 3. If there is still data to receive wait for a notification on the last 3684b4f8505SAziz IDOMAR * channel and jump to step 2 as soon as it arrived. Otherwise, proceed. 3694b4f8505SAziz IDOMAR * 3704b4f8505SAziz IDOMAR */ 3714b4f8505SAziz IDOMAR enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size) 3724b4f8505SAziz IDOMAR { 3734b4f8505SAziz IDOMAR enum mhu_error_t mhu_err; 3744b4f8505SAziz IDOMAR enum mhu_v3_x_error_t mhu_v3_err; 3754b4f8505SAziz IDOMAR uint32_t msg_len; 3764b4f8505SAziz IDOMAR uint8_t num_channels; 3774b4f8505SAziz IDOMAR uint8_t chan; 3784b4f8505SAziz IDOMAR uint32_t *buffer; 3794b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 3804b4f8505SAziz IDOMAR 3814b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; 3824b4f8505SAziz IDOMAR chan = 0; 3834b4f8505SAziz IDOMAR 3844b4f8505SAziz IDOMAR mhu_err = validate_buffer_params((uintptr_t)receive_buffer); 3854b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 3864b4f8505SAziz IDOMAR return mhu_err; 3874b4f8505SAziz IDOMAR } 3884b4f8505SAziz IDOMAR 3894b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, 3904b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 3914b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 3924b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 3934b4f8505SAziz IDOMAR } 3944b4f8505SAziz IDOMAR 3954b4f8505SAziz IDOMAR /* Busy wait for incoming reply */ 3964b4f8505SAziz IDOMAR mhu_err = wait_for_signal(dev, MHU_NOTIFY_VALUE); 3974b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 3984b4f8505SAziz IDOMAR return mhu_err; 3994b4f8505SAziz IDOMAR } 4004b4f8505SAziz IDOMAR 4014b4f8505SAziz IDOMAR /* The first word is the length of the actual message. */ 4024b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, &msg_len); 4034b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 4044b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 4054b4f8505SAziz IDOMAR } 4064b4f8505SAziz IDOMAR chan++; 4074b4f8505SAziz IDOMAR 4084b4f8505SAziz IDOMAR if (*size < msg_len) { 4094b4f8505SAziz IDOMAR /* Message buffer too small */ 4104b4f8505SAziz IDOMAR *size = msg_len; 4114b4f8505SAziz IDOMAR return MHU_ERR_BUFFER_TOO_SMALL; 4124b4f8505SAziz IDOMAR } 4134b4f8505SAziz IDOMAR 4144b4f8505SAziz IDOMAR buffer = (uint32_t *)receive_buffer; 4154b4f8505SAziz IDOMAR for (size_t i = 0; i < msg_len; i += 4) { 4164b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, buffer++); 4174b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 4184b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 4194b4f8505SAziz IDOMAR } 4204b4f8505SAziz IDOMAR 4214b4f8505SAziz IDOMAR /* Only wait for next transfer if still missing data. */ 4224b4f8505SAziz IDOMAR if (++chan == (num_channels - 1) && (msg_len - i) > 4) { 4234b4f8505SAziz IDOMAR /* Busy wait for next transfer */ 4244b4f8505SAziz IDOMAR mhu_err = clear_and_wait_for_signal( 4254b4f8505SAziz IDOMAR dev, MHU_NOTIFY_VALUE); 4264b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 4274b4f8505SAziz IDOMAR return mhu_err; 4284b4f8505SAziz IDOMAR } 4294b4f8505SAziz IDOMAR chan = 0; 4304b4f8505SAziz IDOMAR } 4314b4f8505SAziz IDOMAR } 4324b4f8505SAziz IDOMAR 4334b4f8505SAziz IDOMAR /* Clear all channels */ 4344b4f8505SAziz IDOMAR for (uint8_t i = U(0); i < num_channels; i++) { 4354b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); 4364b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 4374b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 4384b4f8505SAziz IDOMAR } 4394b4f8505SAziz IDOMAR } 4404b4f8505SAziz IDOMAR 4414b4f8505SAziz IDOMAR *size = msg_len; 4424b4f8505SAziz IDOMAR 4434b4f8505SAziz IDOMAR return MHU_ERR_NONE; 4444b4f8505SAziz IDOMAR } 4454b4f8505SAziz IDOMAR 4464b4f8505SAziz IDOMAR size_t mhu_get_max_message_size(void) 4474b4f8505SAziz IDOMAR { 448*e2e8a397SLeo Yan enum mhu_v3_x_error_t err __maybe_unused; 4494b4f8505SAziz IDOMAR uint8_t num_channels; 4504b4f8505SAziz IDOMAR 4514b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(&mhu_seh_dev, 4524b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 4534b4f8505SAziz IDOMAR 4544b4f8505SAziz IDOMAR assert(err == MHU_V_3_X_ERR_NONE); 4554b4f8505SAziz IDOMAR assert(num_channels != U(0)); 4564b4f8505SAziz IDOMAR /* 4574b4f8505SAziz IDOMAR * Returns only usable size of memory. As one channel is specifically 4584b4f8505SAziz IDOMAR * used to inform about the size of payload, discard it from available 4594b4f8505SAziz IDOMAR * memory size. 4604b4f8505SAziz IDOMAR */ 4614b4f8505SAziz IDOMAR return (num_channels - 1) * sizeof(uint32_t); 4624b4f8505SAziz IDOMAR } 463