1*4b4f8505SAziz IDOMAR /* 2*4b4f8505SAziz IDOMAR * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. 3*4b4f8505SAziz IDOMAR * 4*4b4f8505SAziz IDOMAR * SPDX-License-Identifier: BSD-3-Clause 5*4b4f8505SAziz IDOMAR */ 6*4b4f8505SAziz IDOMAR 7*4b4f8505SAziz IDOMAR #include <assert.h> 8*4b4f8505SAziz IDOMAR #include <stddef.h> 9*4b4f8505SAziz IDOMAR #include <stdint.h> 10*4b4f8505SAziz IDOMAR #include <string.h> 11*4b4f8505SAziz IDOMAR 12*4b4f8505SAziz IDOMAR #include <drivers/arm/mhu.h> 13*4b4f8505SAziz IDOMAR 14*4b4f8505SAziz IDOMAR #include "mhu_v3_x.h" 15*4b4f8505SAziz IDOMAR 16*4b4f8505SAziz IDOMAR #define MHU_NOTIFY_VALUE U(1234) 17*4b4f8505SAziz IDOMAR 18*4b4f8505SAziz IDOMAR #ifndef ALIGN_UP 19*4b4f8505SAziz IDOMAR #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) 20*4b4f8505SAziz IDOMAR #endif 21*4b4f8505SAziz IDOMAR 22*4b4f8505SAziz IDOMAR /* 23*4b4f8505SAziz IDOMAR * MHUv3 Wrapper utility macros 24*4b4f8505SAziz IDOMAR */ 25*4b4f8505SAziz IDOMAR #define IS_ALIGNED(val, align) (val == ALIGN_UP(val, align)) 26*4b4f8505SAziz IDOMAR 27*4b4f8505SAziz IDOMAR /* 28*4b4f8505SAziz IDOMAR * MHU devices for host: 29*4b4f8505SAziz IDOMAR * HSE: Host to Secure Enclave (sender device) 30*4b4f8505SAziz IDOMAR * SEH: Secure Enclave to Host (receiver device) 31*4b4f8505SAziz IDOMAR */ 32*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t mhu_hse_dev = {0, MHU_V3_X_PBX_FRAME}; 33*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t mhu_seh_dev = {0, MHU_V3_X_MBX_FRAME}; 34*4b4f8505SAziz IDOMAR 35*4b4f8505SAziz IDOMAR /* MHUv3 driver error to MHUv3 wrapper error mapping */ 36*4b4f8505SAziz IDOMAR static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v3_x_error_t err) 37*4b4f8505SAziz IDOMAR { 38*4b4f8505SAziz IDOMAR switch (err) { 39*4b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_NONE: 40*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 41*4b4f8505SAziz IDOMAR 42*4b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_NOT_INIT: 43*4b4f8505SAziz IDOMAR return MHU_ERR_NOT_INIT; 44*4b4f8505SAziz IDOMAR 45*4b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_UNSUPPORTED_VERSION: 46*4b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED_VERSION; 47*4b4f8505SAziz IDOMAR 48*4b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_UNSUPPORTED: 49*4b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 50*4b4f8505SAziz IDOMAR 51*4b4f8505SAziz IDOMAR case MHU_V_3_X_ERR_INVALID_PARAM: 52*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 53*4b4f8505SAziz IDOMAR 54*4b4f8505SAziz IDOMAR default: 55*4b4f8505SAziz IDOMAR return MHU_ERR_GENERAL; 56*4b4f8505SAziz IDOMAR } 57*4b4f8505SAziz IDOMAR } 58*4b4f8505SAziz IDOMAR 59*4b4f8505SAziz IDOMAR static enum mhu_error_t signal_and_wait_for_clear( 60*4b4f8505SAziz IDOMAR void *mhu_sender_dev, uint32_t value) 61*4b4f8505SAziz IDOMAR { 62*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 63*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 64*4b4f8505SAziz IDOMAR uint8_t num_channels; 65*4b4f8505SAziz IDOMAR uint32_t read_val; 66*4b4f8505SAziz IDOMAR 67*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev; 68*4b4f8505SAziz IDOMAR 69*4b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 70*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 71*4b4f8505SAziz IDOMAR } 72*4b4f8505SAziz IDOMAR 73*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 74*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 75*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 76*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 77*4b4f8505SAziz IDOMAR } 78*4b4f8505SAziz IDOMAR 79*4b4f8505SAziz IDOMAR /* Wait for any pending acknowledgment from transmitter side */ 80*4b4f8505SAziz IDOMAR do { 81*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 82*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 83*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 84*4b4f8505SAziz IDOMAR } 85*4b4f8505SAziz IDOMAR } while ((read_val & value) == value); 86*4b4f8505SAziz IDOMAR 87*4b4f8505SAziz IDOMAR /* Use the last channel to notify that a transfer is ready */ 88*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_write(dev, num_channels - 1, value); 89*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 90*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 91*4b4f8505SAziz IDOMAR } 92*4b4f8505SAziz IDOMAR 93*4b4f8505SAziz IDOMAR /* Wait until receiver side acknowledges the transfer */ 94*4b4f8505SAziz IDOMAR do { 95*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 96*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 97*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 98*4b4f8505SAziz IDOMAR } 99*4b4f8505SAziz IDOMAR } while ((read_val & value) == value); 100*4b4f8505SAziz IDOMAR 101*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE); 102*4b4f8505SAziz IDOMAR } 103*4b4f8505SAziz IDOMAR 104*4b4f8505SAziz IDOMAR static enum mhu_error_t wait_for_signal( 105*4b4f8505SAziz IDOMAR void *mhu_receiver_dev, uint32_t value) 106*4b4f8505SAziz IDOMAR { 107*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 108*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 109*4b4f8505SAziz IDOMAR uint32_t read_val; 110*4b4f8505SAziz IDOMAR uint8_t num_channels; 111*4b4f8505SAziz IDOMAR 112*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; 113*4b4f8505SAziz IDOMAR 114*4b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 115*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 116*4b4f8505SAziz IDOMAR } 117*4b4f8505SAziz IDOMAR 118*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 119*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 120*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 121*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 122*4b4f8505SAziz IDOMAR } 123*4b4f8505SAziz IDOMAR 124*4b4f8505SAziz IDOMAR do { 125*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val); 126*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 127*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 128*4b4f8505SAziz IDOMAR } 129*4b4f8505SAziz IDOMAR } while (read_val != value); 130*4b4f8505SAziz IDOMAR 131*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 132*4b4f8505SAziz IDOMAR } 133*4b4f8505SAziz IDOMAR 134*4b4f8505SAziz IDOMAR static enum mhu_error_t clear_and_wait_for_signal( 135*4b4f8505SAziz IDOMAR void *mhu_receiver_dev, uint32_t value) 136*4b4f8505SAziz IDOMAR { 137*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 138*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 139*4b4f8505SAziz IDOMAR uint8_t num_channels; 140*4b4f8505SAziz IDOMAR 141*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev; 142*4b4f8505SAziz IDOMAR 143*4b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 144*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 145*4b4f8505SAziz IDOMAR } 146*4b4f8505SAziz IDOMAR 147*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(dev, 148*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 149*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 150*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 151*4b4f8505SAziz IDOMAR } 152*4b4f8505SAziz IDOMAR 153*4b4f8505SAziz IDOMAR /* Clear all channels */ 154*4b4f8505SAziz IDOMAR for (int i = 0; i < num_channels; i++) { 155*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); 156*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 157*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 158*4b4f8505SAziz IDOMAR } 159*4b4f8505SAziz IDOMAR } 160*4b4f8505SAziz IDOMAR 161*4b4f8505SAziz IDOMAR return wait_for_signal(mhu_receiver_dev, value); 162*4b4f8505SAziz IDOMAR } 163*4b4f8505SAziz IDOMAR 164*4b4f8505SAziz IDOMAR static enum mhu_error_t validate_buffer_params(uintptr_t buf_addr) 165*4b4f8505SAziz IDOMAR { 166*4b4f8505SAziz IDOMAR if ((buf_addr == 0) || (!IS_ALIGNED(buf_addr, sizeof(uint32_t)))) { 167*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 168*4b4f8505SAziz IDOMAR } 169*4b4f8505SAziz IDOMAR 170*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 171*4b4f8505SAziz IDOMAR } 172*4b4f8505SAziz IDOMAR 173*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base) 174*4b4f8505SAziz IDOMAR { 175*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 176*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 177*4b4f8505SAziz IDOMAR uint8_t num_ch; 178*4b4f8505SAziz IDOMAR uint32_t ch; 179*4b4f8505SAziz IDOMAR 180*4b4f8505SAziz IDOMAR assert(mhu_sender_base != (uintptr_t)NULL); 181*4b4f8505SAziz IDOMAR 182*4b4f8505SAziz IDOMAR mhu_hse_dev.base = mhu_sender_base; 183*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; 184*4b4f8505SAziz IDOMAR 185*4b4f8505SAziz IDOMAR /* Initialize MHUv3 */ 186*4b4f8505SAziz IDOMAR err = mhu_v3_x_driver_init(dev); 187*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 188*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 189*4b4f8505SAziz IDOMAR } 190*4b4f8505SAziz IDOMAR 191*4b4f8505SAziz IDOMAR /* Read the number of doorbell channels implemented in the MHU */ 192*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented( 193*4b4f8505SAziz IDOMAR dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); 194*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 195*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 196*4b4f8505SAziz IDOMAR } else if (num_ch < 2) { 197*4b4f8505SAziz IDOMAR /* This wrapper requires at least two channels implemented */ 198*4b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 199*4b4f8505SAziz IDOMAR } 200*4b4f8505SAziz IDOMAR 201*4b4f8505SAziz IDOMAR /* 202*4b4f8505SAziz IDOMAR * The sender polls the postbox doorbell channel window status register 203*4b4f8505SAziz IDOMAR * to get notified about successful transfer. So, disable the doorbell 204*4b4f8505SAziz IDOMAR * channel's contribution to postbox combined interrupt. 205*4b4f8505SAziz IDOMAR * 206*4b4f8505SAziz IDOMAR * Also, clear and disable the postbox doorbell channel transfer 207*4b4f8505SAziz IDOMAR * acknowledge interrupt. 208*4b4f8505SAziz IDOMAR */ 209*4b4f8505SAziz IDOMAR for (ch = 0; ch < num_ch; ch++) { 210*4b4f8505SAziz IDOMAR err = mhu_v3_x_channel_interrupt_disable( 211*4b4f8505SAziz IDOMAR dev, ch, MHU_V3_X_CHANNEL_TYPE_DBCH); 212*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 213*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 214*4b4f8505SAziz IDOMAR } 215*4b4f8505SAziz IDOMAR } 216*4b4f8505SAziz IDOMAR 217*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 218*4b4f8505SAziz IDOMAR } 219*4b4f8505SAziz IDOMAR 220*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base) 221*4b4f8505SAziz IDOMAR { 222*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 223*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 224*4b4f8505SAziz IDOMAR uint32_t ch; 225*4b4f8505SAziz IDOMAR uint8_t num_ch; 226*4b4f8505SAziz IDOMAR 227*4b4f8505SAziz IDOMAR assert(mhu_receiver_base != (uintptr_t)NULL); 228*4b4f8505SAziz IDOMAR 229*4b4f8505SAziz IDOMAR mhu_seh_dev.base = mhu_receiver_base; 230*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; 231*4b4f8505SAziz IDOMAR 232*4b4f8505SAziz IDOMAR /* Initialize MHUv3 */ 233*4b4f8505SAziz IDOMAR err = mhu_v3_x_driver_init(dev); 234*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 235*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 236*4b4f8505SAziz IDOMAR } 237*4b4f8505SAziz IDOMAR 238*4b4f8505SAziz IDOMAR /* Read the number of doorbell channels implemented in the MHU */ 239*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented( 240*4b4f8505SAziz IDOMAR dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch); 241*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 242*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 243*4b4f8505SAziz IDOMAR } else if (num_ch < 2) { 244*4b4f8505SAziz IDOMAR /* This wrapper requires at least two channels implemented */ 245*4b4f8505SAziz IDOMAR return MHU_ERR_UNSUPPORTED; 246*4b4f8505SAziz IDOMAR } 247*4b4f8505SAziz IDOMAR 248*4b4f8505SAziz IDOMAR /* Mask all channels except the notifying channel */ 249*4b4f8505SAziz IDOMAR for (ch = 0; ch < (num_ch - 1); ch++) { 250*4b4f8505SAziz IDOMAR /* Mask interrupts on channels used for data */ 251*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_mask_set(dev, ch, UINT32_MAX); 252*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 253*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 254*4b4f8505SAziz IDOMAR } 255*4b4f8505SAziz IDOMAR } 256*4b4f8505SAziz IDOMAR 257*4b4f8505SAziz IDOMAR /* Unmask doorbell notification channel interrupt */ 258*4b4f8505SAziz IDOMAR err = mhu_v3_x_doorbell_mask_clear(dev, (num_ch - 1), UINT32_MAX); 259*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 260*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 261*4b4f8505SAziz IDOMAR } 262*4b4f8505SAziz IDOMAR 263*4b4f8505SAziz IDOMAR /* 264*4b4f8505SAziz IDOMAR * Enable the doorbell channel's contribution to mailbox combined 265*4b4f8505SAziz IDOMAR * interrupt. 266*4b4f8505SAziz IDOMAR */ 267*4b4f8505SAziz IDOMAR err = mhu_v3_x_channel_interrupt_enable(dev, (num_ch - 1), 268*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH); 269*4b4f8505SAziz IDOMAR if (err != MHU_V_3_X_ERR_NONE) { 270*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(err); 271*4b4f8505SAziz IDOMAR } 272*4b4f8505SAziz IDOMAR 273*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 274*4b4f8505SAziz IDOMAR } 275*4b4f8505SAziz IDOMAR 276*4b4f8505SAziz IDOMAR /* 277*4b4f8505SAziz IDOMAR * Public function. See mhu.h 278*4b4f8505SAziz IDOMAR * 279*4b4f8505SAziz IDOMAR * The basic steps of transferring a message: 280*4b4f8505SAziz IDOMAR * 1. Send the size of the payload on Channel 0. It is the very first Bytes of 281*4b4f8505SAziz IDOMAR * the transfer. Continue with Channel 1. 282*4b4f8505SAziz IDOMAR * 2. Send the payload, writing the channels one after the other (4 Bytes 283*4b4f8505SAziz IDOMAR * each). The last available channel is reserved for controlling the 284*4b4f8505SAziz IDOMAR * transfer. When the last channel is reached or no more data is left, STOP. 285*4b4f8505SAziz IDOMAR * 3. Notify the receiver using the last channel and wait for acknowledge. If 286*4b4f8505SAziz IDOMAR * there is still data to transfer, jump to step 2. Otherwise, proceed. 287*4b4f8505SAziz IDOMAR * 288*4b4f8505SAziz IDOMAR */ 289*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size) 290*4b4f8505SAziz IDOMAR { 291*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_err; 292*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t mhu_v3_err; 293*4b4f8505SAziz IDOMAR uint8_t num_channels; 294*4b4f8505SAziz IDOMAR uint8_t chan; 295*4b4f8505SAziz IDOMAR uint32_t *buffer; 296*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 297*4b4f8505SAziz IDOMAR 298*4b4f8505SAziz IDOMAR if (size == 0) { 299*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 300*4b4f8505SAziz IDOMAR } 301*4b4f8505SAziz IDOMAR 302*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev; 303*4b4f8505SAziz IDOMAR chan = 0; 304*4b4f8505SAziz IDOMAR 305*4b4f8505SAziz IDOMAR if ((dev == NULL) || (dev->base == 0)) { 306*4b4f8505SAziz IDOMAR return MHU_ERR_INVALID_ARG; 307*4b4f8505SAziz IDOMAR } 308*4b4f8505SAziz IDOMAR 309*4b4f8505SAziz IDOMAR mhu_err = validate_buffer_params((uintptr_t)send_buffer); 310*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 311*4b4f8505SAziz IDOMAR return mhu_err; 312*4b4f8505SAziz IDOMAR } 313*4b4f8505SAziz IDOMAR 314*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, 315*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 316*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 317*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 318*4b4f8505SAziz IDOMAR } 319*4b4f8505SAziz IDOMAR 320*4b4f8505SAziz IDOMAR /* First send the size of the actual message. */ 321*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, (uint32_t)size); 322*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 323*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 324*4b4f8505SAziz IDOMAR } 325*4b4f8505SAziz IDOMAR chan++; 326*4b4f8505SAziz IDOMAR 327*4b4f8505SAziz IDOMAR buffer = (uint32_t *)send_buffer; 328*4b4f8505SAziz IDOMAR for (size_t i = 0; i < size; i += 4) { 329*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, *buffer++); 330*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 331*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 332*4b4f8505SAziz IDOMAR } 333*4b4f8505SAziz IDOMAR 334*4b4f8505SAziz IDOMAR if (++chan == (num_channels - 1)) { 335*4b4f8505SAziz IDOMAR /* Use the last channel to notify transfer complete */ 336*4b4f8505SAziz IDOMAR mhu_err = signal_and_wait_for_clear( 337*4b4f8505SAziz IDOMAR dev, MHU_NOTIFY_VALUE); 338*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 339*4b4f8505SAziz IDOMAR return mhu_err; 340*4b4f8505SAziz IDOMAR } 341*4b4f8505SAziz IDOMAR chan = 0; 342*4b4f8505SAziz IDOMAR } 343*4b4f8505SAziz IDOMAR } 344*4b4f8505SAziz IDOMAR 345*4b4f8505SAziz IDOMAR if (chan != 0) { 346*4b4f8505SAziz IDOMAR /* Use the last channel to notify transfer complete */ 347*4b4f8505SAziz IDOMAR mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE); 348*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 349*4b4f8505SAziz IDOMAR return mhu_err; 350*4b4f8505SAziz IDOMAR } 351*4b4f8505SAziz IDOMAR } 352*4b4f8505SAziz IDOMAR 353*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 354*4b4f8505SAziz IDOMAR } 355*4b4f8505SAziz IDOMAR 356*4b4f8505SAziz IDOMAR /* 357*4b4f8505SAziz IDOMAR * Public function. See mhu.h 358*4b4f8505SAziz IDOMAR * 359*4b4f8505SAziz IDOMAR * The basic steps of receiving a message: 360*4b4f8505SAziz IDOMAR * 1. Read the size of the payload from Channel 0. It is the very first 361*4b4f8505SAziz IDOMAR * 4 Bytes of the transfer. Continue with Channel 1. 362*4b4f8505SAziz IDOMAR * 2. Receive the payload, read the channels one after the other 363*4b4f8505SAziz IDOMAR * (4 Bytes each). The last available channel is reserved for controlling 364*4b4f8505SAziz IDOMAR * the transfer. 365*4b4f8505SAziz IDOMAR * When the last channel is reached clear all the channels 366*4b4f8505SAziz IDOMAR * (also sending an acknowledge on the last channel). 367*4b4f8505SAziz IDOMAR * 3. If there is still data to receive wait for a notification on the last 368*4b4f8505SAziz IDOMAR * channel and jump to step 2 as soon as it arrived. Otherwise, proceed. 369*4b4f8505SAziz IDOMAR * 370*4b4f8505SAziz IDOMAR */ 371*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size) 372*4b4f8505SAziz IDOMAR { 373*4b4f8505SAziz IDOMAR enum mhu_error_t mhu_err; 374*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t mhu_v3_err; 375*4b4f8505SAziz IDOMAR uint32_t msg_len; 376*4b4f8505SAziz IDOMAR uint8_t num_channels; 377*4b4f8505SAziz IDOMAR uint8_t chan; 378*4b4f8505SAziz IDOMAR uint32_t *buffer; 379*4b4f8505SAziz IDOMAR struct mhu_v3_x_dev_t *dev; 380*4b4f8505SAziz IDOMAR 381*4b4f8505SAziz IDOMAR dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev; 382*4b4f8505SAziz IDOMAR chan = 0; 383*4b4f8505SAziz IDOMAR 384*4b4f8505SAziz IDOMAR mhu_err = validate_buffer_params((uintptr_t)receive_buffer); 385*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 386*4b4f8505SAziz IDOMAR return mhu_err; 387*4b4f8505SAziz IDOMAR } 388*4b4f8505SAziz IDOMAR 389*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev, 390*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 391*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 392*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 393*4b4f8505SAziz IDOMAR } 394*4b4f8505SAziz IDOMAR 395*4b4f8505SAziz IDOMAR /* Busy wait for incoming reply */ 396*4b4f8505SAziz IDOMAR mhu_err = wait_for_signal(dev, MHU_NOTIFY_VALUE); 397*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 398*4b4f8505SAziz IDOMAR return mhu_err; 399*4b4f8505SAziz IDOMAR } 400*4b4f8505SAziz IDOMAR 401*4b4f8505SAziz IDOMAR /* The first word is the length of the actual message. */ 402*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, &msg_len); 403*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 404*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 405*4b4f8505SAziz IDOMAR } 406*4b4f8505SAziz IDOMAR chan++; 407*4b4f8505SAziz IDOMAR 408*4b4f8505SAziz IDOMAR if (*size < msg_len) { 409*4b4f8505SAziz IDOMAR /* Message buffer too small */ 410*4b4f8505SAziz IDOMAR *size = msg_len; 411*4b4f8505SAziz IDOMAR return MHU_ERR_BUFFER_TOO_SMALL; 412*4b4f8505SAziz IDOMAR } 413*4b4f8505SAziz IDOMAR 414*4b4f8505SAziz IDOMAR buffer = (uint32_t *)receive_buffer; 415*4b4f8505SAziz IDOMAR for (size_t i = 0; i < msg_len; i += 4) { 416*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, buffer++); 417*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 418*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 419*4b4f8505SAziz IDOMAR } 420*4b4f8505SAziz IDOMAR 421*4b4f8505SAziz IDOMAR /* Only wait for next transfer if still missing data. */ 422*4b4f8505SAziz IDOMAR if (++chan == (num_channels - 1) && (msg_len - i) > 4) { 423*4b4f8505SAziz IDOMAR /* Busy wait for next transfer */ 424*4b4f8505SAziz IDOMAR mhu_err = clear_and_wait_for_signal( 425*4b4f8505SAziz IDOMAR dev, MHU_NOTIFY_VALUE); 426*4b4f8505SAziz IDOMAR if (mhu_err != MHU_ERR_NONE) { 427*4b4f8505SAziz IDOMAR return mhu_err; 428*4b4f8505SAziz IDOMAR } 429*4b4f8505SAziz IDOMAR chan = 0; 430*4b4f8505SAziz IDOMAR } 431*4b4f8505SAziz IDOMAR } 432*4b4f8505SAziz IDOMAR 433*4b4f8505SAziz IDOMAR /* Clear all channels */ 434*4b4f8505SAziz IDOMAR for (uint8_t i = U(0); i < num_channels; i++) { 435*4b4f8505SAziz IDOMAR mhu_v3_err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX); 436*4b4f8505SAziz IDOMAR if (mhu_v3_err != MHU_V_3_X_ERR_NONE) { 437*4b4f8505SAziz IDOMAR return error_mapping_to_mhu_error_t(mhu_v3_err); 438*4b4f8505SAziz IDOMAR } 439*4b4f8505SAziz IDOMAR } 440*4b4f8505SAziz IDOMAR 441*4b4f8505SAziz IDOMAR *size = msg_len; 442*4b4f8505SAziz IDOMAR 443*4b4f8505SAziz IDOMAR return MHU_ERR_NONE; 444*4b4f8505SAziz IDOMAR } 445*4b4f8505SAziz IDOMAR 446*4b4f8505SAziz IDOMAR size_t mhu_get_max_message_size(void) 447*4b4f8505SAziz IDOMAR { 448*4b4f8505SAziz IDOMAR enum mhu_v3_x_error_t err; 449*4b4f8505SAziz IDOMAR uint8_t num_channels; 450*4b4f8505SAziz IDOMAR 451*4b4f8505SAziz IDOMAR err = mhu_v3_x_get_num_channel_implemented(&mhu_seh_dev, 452*4b4f8505SAziz IDOMAR MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels); 453*4b4f8505SAziz IDOMAR 454*4b4f8505SAziz IDOMAR assert(err == MHU_V_3_X_ERR_NONE); 455*4b4f8505SAziz IDOMAR assert(num_channels != U(0)); 456*4b4f8505SAziz IDOMAR /* 457*4b4f8505SAziz IDOMAR * Returns only usable size of memory. As one channel is specifically 458*4b4f8505SAziz IDOMAR * used to inform about the size of payload, discard it from available 459*4b4f8505SAziz IDOMAR * memory size. 460*4b4f8505SAziz IDOMAR */ 461*4b4f8505SAziz IDOMAR return (num_channels - 1) * sizeof(uint32_t); 462*4b4f8505SAziz IDOMAR } 463