]> git.baikalelectronics.ru Git - arm-tf.git/commitdiff
feat(drivers/arm/mhu): add MHU driver
authorTamas Ban <tamas.ban@arm.com>
Mon, 10 Jan 2022 16:04:03 +0000 (17:04 +0100)
committerDavid Vincze <david.vincze@arm.com>
Wed, 11 May 2022 13:47:02 +0000 (15:47 +0200)
The Arm Message Handling Unit (MHU) is a mailbox controller used to
communicate with other processing element(s). Adding a driver to
enable the communication:
- Adding generic MHU driver interface,
- Adding MHU_v2_x driver.

Driver supports:
 - Discovering available MHU channels,
 - Sending / receiving words over MHU channels,
 - Signaling happens over a dedicated channel.

Signed-off-by: Tamas Ban <tamas.ban@arm.com>
Signed-off-by: David Vincze <david.vincze@arm.com>
Change-Id: I41a5b968f6b8319cdbdf7907d70bd8837839862e

drivers/arm/mhu/mhu_v2_x.c [new file with mode: 0644]
drivers/arm/mhu/mhu_v2_x.h [new file with mode: 0644]
drivers/arm/mhu/mhu_wrapper_v2_x.c [new file with mode: 0644]
include/drivers/arm/mhu.h [new file with mode: 0644]

diff --git a/drivers/arm/mhu/mhu_v2_x.c b/drivers/arm/mhu/mhu_v2_x.c
new file mode 100644 (file)
index 0000000..3103b92
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "mhu_v2_x.h"
+
+#define MHU_V2_X_MAX_CHANNELS          124
+#define MHU_V2_1_MAX_CHCOMB_INT                4
+#define ENABLE                         0x1
+#define DISABLE                                0x0
+#define CLEAR_INTR                     0x1
+#define CH_PER_CH_COMB                 0x20
+#define SEND_FRAME(p_mhu)              ((struct mhu_v2_x_send_frame_t *)p_mhu)
+#define RECV_FRAME(p_mhu)              ((struct mhu_v2_x_recv_frame_t *)p_mhu)
+
+#define MHU_MAJOR_REV_V2               0x1u
+#define MHU_MINOR_REV_2_0              0x0u
+#define MHU_MINOR_REV_2_1              0x1u
+
+struct mhu_v2_x_send_ch_window_t {
+       /* Offset: 0x00 (R/ ) Channel Status */
+       volatile uint32_t ch_st;
+       /* Offset: 0x04 (R/ ) Reserved */
+       volatile uint32_t reserved_0;
+       /* Offset: 0x08 (R/ ) Reserved */
+       volatile uint32_t reserved_1;
+       /* Offset: 0x0C ( /W) Channel Set */
+       volatile uint32_t ch_set;
+       /* Offset: 0x10 (R/ ) Channel Interrupt Status (Reserved in 2.0) */
+       volatile uint32_t ch_int_st;
+       /* Offset: 0x14 ( /W) Channel Interrupt Clear  (Reserved in 2.0) */
+       volatile uint32_t ch_int_clr;
+       /* Offset: 0x18 (R/W) Channel Interrupt Enable (Reserved in 2.0) */
+       volatile uint32_t ch_int_en;
+       /* Offset: 0x1C (R/ ) Reserved */
+       volatile uint32_t reserved_2;
+};
+
+struct mhu_v2_x_send_frame_t {
+       /* Offset: 0x000 ( / ) Sender Channel Window 0 -123 */
+       struct mhu_v2_x_send_ch_window_t send_ch_window[MHU_V2_X_MAX_CHANNELS];
+       /* Offset: 0xF80 (R/ ) Message Handling Unit Configuration */
+       volatile uint32_t mhu_cfg;
+       /* Offset: 0xF84 (R/W) Response Configuration */
+       volatile uint32_t resp_cfg;
+       /* Offset: 0xF88 (R/W) Access Request */
+       volatile uint32_t access_request;
+       /* Offset: 0xF8C (R/ ) Access Ready */
+       volatile uint32_t access_ready;
+       /* Offset: 0xF90 (R/ ) Interrupt Status */
+       volatile uint32_t int_st;
+       /* Offset: 0xF94 ( /W) Interrupt Clear */
+       volatile uint32_t int_clr;
+       /* Offset: 0xF98 (R/W) Interrupt Enable */
+       volatile uint32_t int_en;
+       /* Offset: 0xF9C (R/ ) Reserved */
+       volatile uint32_t reserved_0;
+       /* Offset: 0xFA0 (R/W) Channel Combined IRQ Stat (Reserved in 2.0) */
+       volatile uint32_t ch_comb_int_st[MHU_V2_1_MAX_CHCOMB_INT];
+       /* Offset: 0xFC4 (R/ ) Reserved */
+       volatile uint32_t reserved_1[6];
+       /* Offset: 0xFC8 (R/ ) Implementer Identification Register */
+       volatile uint32_t iidr;
+       /* Offset: 0xFCC (R/ ) Architecture Identification Register */
+       volatile uint32_t aidr;
+       /* Offset: 0xFD0 (R/ )  */
+       volatile uint32_t pid_1[4];
+       /* Offset: 0xFE0 (R/ )  */
+       volatile uint32_t pid_0[4];
+       /* Offset: 0xFF0 (R/ )  */
+       volatile uint32_t cid[4];
+};
+
+struct mhu_v2_x_rec_ch_window_t {
+       /* Offset: 0x00 (R/ ) Channel Status */
+       volatile uint32_t ch_st;
+       /* Offset: 0x04 (R/ ) Channel Status Masked */
+       volatile uint32_t ch_st_msk;
+       /* Offset: 0x08 ( /W) Channel Clear */
+       volatile uint32_t ch_clr;
+       /* Offset: 0x0C (R/ ) Reserved */
+       volatile uint32_t reserved_0;
+       /* Offset: 0x10 (R/ ) Channel Mask Status */
+       volatile uint32_t ch_msk_st;
+       /* Offset: 0x14 ( /W) Channel Mask Set */
+       volatile uint32_t ch_msk_set;
+       /* Offset: 0x18 ( /W) Channel Mask Clear */
+       volatile uint32_t ch_msk_clr;
+       /* Offset: 0x1C (R/ ) Reserved */
+       volatile uint32_t reserved_1;
+};
+
+struct mhu_v2_x_recv_frame_t {
+       /* Offset: 0x000 ( / ) Receiver Channel Window 0 -123 */
+       struct mhu_v2_x_rec_ch_window_t rec_ch_window[MHU_V2_X_MAX_CHANNELS];
+       /* Offset: 0xF80 (R/ ) Message Handling Unit Configuration */
+       volatile uint32_t mhu_cfg;
+       /* Offset: 0xF84 (R/ ) Reserved */
+       volatile uint32_t reserved_0[3];
+       /* Offset: 0xF90 (R/ ) Interrupt Status (Reserved in 2.0) */
+       volatile uint32_t int_st;
+       /* Offset: 0xF94 (R/ ) Interrupt Clear  (Reserved in 2.0) */
+       volatile uint32_t int_clr;
+       /* Offset: 0xF98 (R/W) Interrupt Enable (Reserved in 2.0) */
+       volatile uint32_t int_en;
+       /* Offset: 0xF9C (R/ ) Reserved  */
+       volatile uint32_t reserved_1;
+       /* Offset: 0xFA0 (R/ ) Channel Combined IRQ Stat (Reserved in 2.0) */
+       volatile uint32_t ch_comb_int_st[MHU_V2_1_MAX_CHCOMB_INT];
+       /* Offset: 0xFB0 (R/ ) Reserved */
+       volatile uint32_t reserved_2[6];
+       /* Offset: 0xFC8 (R/ ) Implementer Identification Register */
+       volatile uint32_t iidr;
+       /* Offset: 0xFCC (R/ ) Architecture Identification Register */
+       volatile uint32_t aidr;
+       /* Offset: 0xFD0 (R/ )  */
+       volatile uint32_t pid_1[4];
+       /* Offset: 0xFE0 (R/ )  */
+       volatile uint32_t pid_0[4];
+       /* Offset: 0xFF0 (R/ )  */
+       volatile uint32_t cid[4];
+};
+
+union mhu_v2_x_frame {
+       struct mhu_v2_x_send_frame_t send_frame;
+       struct mhu_v2_x_recv_frame_t recv_frame;
+};
+
+enum mhu_v2_x_error_t mhu_v2_x_driver_init(struct mhu_v2_x_dev_t *dev,
+        enum mhu_v2_x_supported_revisions rev)
+{
+       uint32_t AIDR = 0;
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (dev->is_initialized) {
+               return MHU_V_2_X_ERR_ALREADY_INIT;
+       }
+
+       if (rev == MHU_REV_READ_FROM_HW) {
+               /* Read revision from HW */
+               if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
+                       AIDR = p_mhu->recv_frame.aidr;
+               } else {
+                       AIDR = p_mhu->send_frame.aidr;
+               }
+
+               /* Get bits 7:4 to read major revision */
+               if (((AIDR >> 4) & 0b1111) != MHU_MAJOR_REV_V2) {
+                       /* Unsupported MHU version */
+                       return MHU_V_2_X_ERR_UNSUPPORTED_VERSION;
+               } /* No need to save major version, driver only supports MHUv2 */
+
+               /* Get bits 3:0 to read minor revision */
+               dev->subversion = AIDR & 0b1111;
+
+               if (dev->subversion != MHU_MINOR_REV_2_0 &&
+                       dev->subversion != MHU_MINOR_REV_2_1) {
+                       /* Unsupported subversion */
+                       return MHU_V_2_X_ERR_UNSUPPORTED_VERSION;
+               }
+       } else {
+               /* Revisions were provided by caller */
+               if (rev == MHU_REV_2_0) {
+                       dev->subversion = MHU_MINOR_REV_2_0;
+               } else if (rev == MHU_REV_2_1) {
+                       dev->subversion = MHU_MINOR_REV_2_1;
+               } else {
+                       /* Unsupported subversion */
+                       return MHU_V_2_X_ERR_UNSUPPORTED_VERSION;
+               } /* No need to save major version, driver only supports MHUv2 */
+       }
+
+       dev->is_initialized = true;
+
+       return MHU_V_2_X_ERR_NONE;
+}
+
+uint32_t mhu_v2_x_get_num_channel_implemented(const struct mhu_v2_x_dev_t *dev)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_SENDER_FRAME) {
+               return (SEND_FRAME(p_mhu))->mhu_cfg;
+       } else {
+               assert(dev->frame == MHU_V2_X_RECEIVER_FRAME);
+               return (RECV_FRAME(p_mhu))->mhu_cfg;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_send(const struct mhu_v2_x_dev_t *dev,
+        uint32_t channel, uint32_t val)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_SENDER_FRAME) {
+               (SEND_FRAME(p_mhu))->send_ch_window[channel].ch_set = val;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_poll(const struct mhu_v2_x_dev_t *dev,
+        uint32_t channel, uint32_t *value)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_SENDER_FRAME) {
+               *value = (SEND_FRAME(p_mhu))->send_ch_window[channel].ch_st;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_clear(const struct mhu_v2_x_dev_t *dev,
+        uint32_t channel)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
+               (RECV_FRAME(p_mhu))->rec_ch_window[channel].ch_clr = UINT32_MAX;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_receive(
+        const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t *value)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
+               *value = (RECV_FRAME(p_mhu))->rec_ch_window[channel].ch_st;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_mask_set(
+        const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t mask)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
+               (RECV_FRAME(p_mhu))->rec_ch_window[channel].ch_msk_set = mask;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_channel_mask_clear(
+        const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t mask)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
+               (RECV_FRAME(p_mhu))->rec_ch_window[channel].ch_msk_clr = mask;
+               return MHU_V_2_X_ERR_NONE;
+       } else {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+}
+enum mhu_v2_x_error_t mhu_v2_x_initiate_transfer(
+        const struct mhu_v2_x_dev_t *dev)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame != MHU_V2_X_SENDER_FRAME) {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+
+       (SEND_FRAME(p_mhu))->access_request = ENABLE;
+
+       while (!((SEND_FRAME(p_mhu))->access_ready)) {
+               /* Wait in a loop for access ready signal to be high */
+               ;
+       }
+
+       return MHU_V_2_X_ERR_NONE;
+}
+
+enum mhu_v2_x_error_t mhu_v2_x_close_transfer(const struct mhu_v2_x_dev_t *dev)
+{
+       union mhu_v2_x_frame *p_mhu;
+
+       assert(dev != NULL);
+
+       p_mhu = (union mhu_v2_x_frame *)dev->base;
+
+       if (!(dev->is_initialized)) {
+               return MHU_V_2_X_ERR_NOT_INIT;
+       }
+
+       if (dev->frame != MHU_V2_X_SENDER_FRAME) {
+               return MHU_V_2_X_ERR_INVALID_ARG;
+       }
+
+       (SEND_FRAME(p_mhu))->access_request = DISABLE;
+
+       return MHU_V_2_X_ERR_NONE;
+}
diff --git a/drivers/arm/mhu/mhu_v2_x.h b/drivers/arm/mhu/mhu_v2_x.h
new file mode 100644 (file)
index 0000000..10247d2
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MHU_V2_X_H
+#define MHU_V2_X_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define MHU_2_X_INTR_NR2R_OFF          (0x0u)
+#define MHU_2_X_INTR_R2NR_OFF          (0x1u)
+#define MHU_2_1_INTR_CHCOMB_OFF                (0x2u)
+
+#define MHU_2_X_INTR_NR2R_MASK         (0x1u << MHU_2_X_INTR_NR2R_OFF)
+#define MHU_2_X_INTR_R2NR_MASK         (0x1u << MHU_2_X_INTR_R2NR_OFF)
+#define MHU_2_1_INTR_CHCOMB_MASK       (0x1u << MHU_2_1_INTR_CHCOMB_OFF)
+
+enum mhu_v2_x_frame_t {
+       MHU_V2_X_SENDER_FRAME   = 0x0u,
+       MHU_V2_X_RECEIVER_FRAME = 0x1u,
+};
+
+enum mhu_v2_x_supported_revisions {
+       MHU_REV_READ_FROM_HW = 0,
+       MHU_REV_2_0,
+       MHU_REV_2_1,
+};
+
+struct mhu_v2_x_dev_t {
+       uintptr_t base;
+       enum mhu_v2_x_frame_t frame;
+       uint32_t subversion;    /*!< Hardware subversion: v2.X */
+       bool is_initialized;    /*!< Indicates if the MHU driver
+                                *   is initialized and enabled
+                                */
+};
+
+/**
+ * MHU v2 error enumeration types.
+ */
+enum mhu_v2_x_error_t {
+       MHU_V_2_X_ERR_NONE                      =  0,
+       MHU_V_2_X_ERR_NOT_INIT                  = -1,
+       MHU_V_2_X_ERR_ALREADY_INIT              = -2,
+       MHU_V_2_X_ERR_UNSUPPORTED_VERSION       = -3,
+       MHU_V_2_X_ERR_INVALID_ARG               = -4,
+       MHU_V_2_X_ERR_GENERAL                   = -5
+};
+
+/**
+ * Initializes the driver.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * rev         MHU revision (if can't be identified from HW).
+ *
+ * Reads the MHU hardware version.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * MHU revision only has to be specified when versions can't be read
+ * from HW (ARCH_MAJOR_REV reg reads as 0x0).
+ *
+ * This function doesn't check if dev is NULL.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_driver_init(struct mhu_v2_x_dev_t *dev,
+       enum mhu_v2_x_supported_revisions rev);
+
+/**
+ * Returns the number of channels implemented.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ *
+ * This function doesn't check if dev is NULL.
+ */
+uint32_t mhu_v2_x_get_num_channel_implemented(
+               const struct mhu_v2_x_dev_t *dev);
+
+/**
+ * Sends the value over a channel.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * channel     Channel to send the value over.
+ * val         Value to send.
+ *
+ * Sends the value over a channel.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ * This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_send(const struct mhu_v2_x_dev_t *dev,
+       uint32_t channel, uint32_t val);
+
+/**
+ * Polls sender channel status.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * channel     Channel to poll the status of.
+ * value       Pointer to variable that will store the value.
+ *
+ * Polls sender channel status.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ * This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_poll(const struct mhu_v2_x_dev_t *dev,
+       uint32_t channel, uint32_t *value);
+
+/**
+ * Clears the channel after the value is send over it.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * channel     Channel to clear.
+ *
+ * Clears the channel after the value is send over it.
+ *
+ * Returns mhu_v2_x_error_t error code..
+ *
+ * This function doesn't check if dev is NULL.
+ * This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_clear(const struct mhu_v2_x_dev_t *dev,
+       uint32_t channel);
+
+/**
+ * Receives the value over a channel.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * channel     Channel to receive the value from.
+ * value       Pointer to variable that will store the value.
+ *
+ * Receives the value over a channel.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ * This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_receive(
+       const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t *value);
+
+/**
+ * Sets bits in the Channel Mask.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ * channel     Which channel's mask to set.
+ * mask                Mask to be set over a receiver frame.
+ *
+ * Sets bits in the Channel Mask.
+ *
+ * Returns mhu_v2_x_error_t error code..
+ *
+ * This function doesn't check if dev is NULL.
+ *  This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_mask_set(
+       const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t mask);
+
+/**
+ * Clears bits in the Channel Mask.
+ *
+ * dev MHU device struct mhu_v2_x_dev_t.
+ * channel     Which channel's mask to clear.
+ * mask        Mask to be clear over a receiver frame.
+ *
+ * Clears bits in the Channel Mask.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ *  This function doesn't check if channel is implemented.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_channel_mask_clear(
+       const struct mhu_v2_x_dev_t *dev, uint32_t channel, uint32_t mask);
+
+/**
+ * Initiates a MHU transfer with the handshake signals.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ *
+ * Initiates a MHU transfer with the handshake signals in a blocking mode.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_initiate_transfer(
+       const struct mhu_v2_x_dev_t *dev);
+
+/**
+ * Closes a MHU transfer with the handshake signals.
+ *
+ * dev         MHU device struct mhu_v2_x_dev_t.
+ *
+ * Closes a MHU transfer with the handshake signals in a blocking mode.
+ *
+ * Returns mhu_v2_x_error_t error code.
+ *
+ * This function doesn't check if dev is NULL.
+ */
+enum mhu_v2_x_error_t mhu_v2_x_close_transfer(
+       const struct mhu_v2_x_dev_t *dev);
+
+#endif /* MHU_V2_X_H */
diff --git a/drivers/arm/mhu/mhu_wrapper_v2_x.c b/drivers/arm/mhu/mhu_wrapper_v2_x.c
new file mode 100644 (file)
index 0000000..d8b7cfd
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <drivers/arm/mhu.h>
+
+#include "mhu_v2_x.h"
+
+#define MHU_NOTIFY_VALUE       (1234u)
+
+/*
+ * MHU devices for host:
+ * HSE: Host to Secure Enclave (sender device)
+ * SEH: Secure Enclave to Host (receiver device)
+ */
+struct mhu_v2_x_dev_t MHU1_HSE_DEV = {0, MHU_V2_X_SENDER_FRAME};
+struct mhu_v2_x_dev_t MHU1_SEH_DEV = {0, MHU_V2_X_RECEIVER_FRAME};
+
+static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err)
+{
+       switch (err) {
+       case MHU_V_2_X_ERR_NONE:
+               return MHU_ERR_NONE;
+       case MHU_V_2_X_ERR_NOT_INIT:
+               return MHU_ERR_NOT_INIT;
+       case MHU_V_2_X_ERR_ALREADY_INIT:
+               return MHU_ERR_ALREADY_INIT;
+       case MHU_V_2_X_ERR_UNSUPPORTED_VERSION:
+               return MHU_ERR_UNSUPPORTED_VERSION;
+       case MHU_V_2_X_ERR_INVALID_ARG:
+               return MHU_ERR_INVALID_ARG;
+       case MHU_V_2_X_ERR_GENERAL:
+               return MHU_ERR_GENERAL;
+       default:
+               return MHU_ERR_GENERAL;
+       }
+}
+
+static enum mhu_v2_x_error_t signal_and_wait_for_clear(void)
+{
+       enum mhu_v2_x_error_t err;
+       struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV;
+       uint32_t val = MHU_NOTIFY_VALUE;
+       /* Using the last channel for notifications */
+       uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
+
+       err = mhu_v2_x_channel_send(dev, channel_notify, val);
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return err;
+       }
+
+       do {
+               err = mhu_v2_x_channel_poll(dev, channel_notify, &val);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       break;
+               }
+       } while (val != 0);
+
+       return err;
+}
+
+static enum mhu_v2_x_error_t wait_for_signal(void)
+{
+       enum mhu_v2_x_error_t err;
+       struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
+       uint32_t val = 0;
+       /* Using the last channel for notifications */
+       uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1;
+
+       do {
+               err = mhu_v2_x_channel_receive(dev, channel_notify, &val);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       break;
+               }
+       } while (val != MHU_NOTIFY_VALUE);
+
+       return err;
+}
+
+static enum mhu_v2_x_error_t clear_and_wait_for_next_signal(void)
+{
+       enum mhu_v2_x_error_t err;
+       struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
+       uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+       uint32_t i;
+
+       /* Clear all channels */
+       for (i = 0; i < num_channels; ++i) {
+               err = mhu_v2_x_channel_clear(dev, i);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return err;
+               }
+       }
+
+       return wait_for_signal();
+}
+
+enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base)
+{
+       enum mhu_v2_x_error_t err;
+
+       assert(mhu_sender_base != (uintptr_t)NULL);
+
+       MHU1_HSE_DEV.base = mhu_sender_base;
+
+       err = mhu_v2_x_driver_init(&MHU1_HSE_DEV, MHU_REV_READ_FROM_HW);
+       return error_mapping_to_mhu_error_t(err);
+}
+
+enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base)
+{
+       enum mhu_v2_x_error_t err;
+       uint32_t num_channels, i;
+
+       assert(mhu_receiver_base != (uintptr_t)NULL);
+
+       MHU1_SEH_DEV.base = mhu_receiver_base;
+
+       err = mhu_v2_x_driver_init(&MHU1_SEH_DEV, MHU_REV_READ_FROM_HW);
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return error_mapping_to_mhu_error_t(err);
+       }
+
+       num_channels = mhu_v2_x_get_num_channel_implemented(&MHU1_SEH_DEV);
+
+       /* Mask all channels except the notifying channel */
+       for (i = 0; i < (num_channels - 1); ++i) {
+               err = mhu_v2_x_channel_mask_set(&MHU1_SEH_DEV, i, UINT32_MAX);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return error_mapping_to_mhu_error_t(err);
+               }
+       }
+
+       /* The last channel is used for notifications */
+       err = mhu_v2_x_channel_mask_clear(
+               &MHU1_SEH_DEV, (num_channels - 1), UINT32_MAX);
+       return error_mapping_to_mhu_error_t(err);
+}
+
+/*
+ * Public function. See mhu.h
+ *
+ * The basic steps of transferring a message:
+ * 1.  Initiate MHU transfer.
+ * 2.  Send over the size of the payload on Channel 1. It is the very first
+ *     4 Bytes of the transfer. Continue with Channel 2.
+ * 3.  Send over the payload, writing the channels one after the other
+ *     (4 Bytes each). The last available channel is reserved for controlling
+ *     the transfer.
+ *     When the last channel is reached or no more data is left, STOP.
+ * 4.  Notify the receiver using the last channel and wait for acknowledge.
+ *     If there is still data to transfer, jump to step 3. Otherwise, proceed.
+ * 5.  Close MHU transfer.
+ *
+ */
+enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size)
+{
+       enum mhu_v2_x_error_t err;
+       struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV;
+       uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+       uint32_t chan = 0;
+       uint32_t i;
+       uint32_t *p;
+
+       /* For simplicity, require the send_buffer to be 4-byte aligned */
+       if ((uintptr_t)send_buffer & 0x3U) {
+               return MHU_ERR_INVALID_ARG;
+       }
+
+       err = mhu_v2_x_initiate_transfer(dev);
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return error_mapping_to_mhu_error_t(err);
+       }
+
+       /* First send over the size of the actual message */
+       err = mhu_v2_x_channel_send(dev, chan, (uint32_t)size);
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return error_mapping_to_mhu_error_t(err);
+       }
+       chan++;
+
+       p = (uint32_t *)send_buffer;
+       for (i = 0; i < size; i += 4) {
+               err = mhu_v2_x_channel_send(dev, chan, *p++);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return error_mapping_to_mhu_error_t(err);
+               }
+               if (++chan == (num_channels - 1)) {
+                       err = signal_and_wait_for_clear();
+                       if (err != MHU_V_2_X_ERR_NONE) {
+                               return error_mapping_to_mhu_error_t(err);
+                       }
+                       chan = 0;
+               }
+       }
+
+       /* Signal the end of transfer.
+        *   It's not required to send a signal when the message was
+        *   perfectly-aligned (num_channels - 1 channels were used in the last
+        *   round) preventing it from signaling twice at the end of transfer.
+        */
+       if (chan != 0) {
+               err = signal_and_wait_for_clear();
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return error_mapping_to_mhu_error_t(err);
+               }
+       }
+
+       err = mhu_v2_x_close_transfer(dev);
+       return error_mapping_to_mhu_error_t(err);
+}
+
+/*
+ * Public function. See mhu.h
+ *
+ * The basic steps of receiving a message:
+ * 1.  Read the size of the payload from Channel 1. It is the very first
+ *     4 Bytes of the transfer. Continue with Channel 2.
+ * 2.  Receive the payload, read the channels one after the other
+ *     (4 Bytes each). The last available channel is reserved for controlling
+ *     the transfer.
+ *     When the last channel is reached clear all the channels
+ *     (also sending an acknowledge on the last channel).
+ * 3.  If there is still data to receive wait for a notification on the last
+ *     channel and jump to step 2 as soon as it arrived. Otherwise, proceed.
+ * 4.  End of transfer.
+ *
+ */
+enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size)
+{
+       enum mhu_v2_x_error_t err;
+       struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV;
+       uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+       uint32_t chan = 0;
+       uint32_t message_len;
+       uint32_t i;
+       uint32_t *p;
+
+       /* For simplicity, require:
+        * - the receive_buffer to be 4-byte aligned,
+        * - the buffer size to be a multiple of 4.
+        */
+       if (((uintptr_t)receive_buffer & 0x3U) || (*size & 0x3U)) {
+               return MHU_ERR_INVALID_ARG;
+       }
+
+       /* Busy wait for incoming reply */
+       err = wait_for_signal();
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return error_mapping_to_mhu_error_t(err);
+       }
+
+       /* The first word is the length of the actual message */
+       err = mhu_v2_x_channel_receive(dev, chan, &message_len);
+       if (err != MHU_V_2_X_ERR_NONE) {
+               return error_mapping_to_mhu_error_t(err);
+       }
+       chan++;
+
+       if (message_len > *size) {
+               /* Message buffer too small */
+               *size = message_len;
+               return MHU_ERR_BUFFER_TOO_SMALL;
+       }
+
+       p = (uint32_t *)receive_buffer;
+       for (i = 0; i < message_len; i += 4) {
+               err = mhu_v2_x_channel_receive(dev, chan, p++);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return error_mapping_to_mhu_error_t(err);
+               }
+
+               /* Only wait for next transfer if there is still missing data */
+               if (++chan == (num_channels - 1) && (message_len - i) > 4) {
+                       /* Busy wait for next transfer */
+                       err = clear_and_wait_for_next_signal();
+                       if (err != MHU_V_2_X_ERR_NONE) {
+                               return error_mapping_to_mhu_error_t(err);
+                       }
+                       chan = 0;
+               }
+       }
+
+       /* Clear all channels */
+       for (i = 0; i < num_channels; ++i) {
+               err = mhu_v2_x_channel_clear(dev, i);
+               if (err != MHU_V_2_X_ERR_NONE) {
+                       return error_mapping_to_mhu_error_t(err);
+               }
+       }
+
+       *size = message_len;
+
+       return MHU_ERR_NONE;
+}
diff --git a/include/drivers/arm/mhu.h b/include/drivers/arm/mhu.h
new file mode 100644 (file)
index 0000000..7745bd9
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MHU_H
+#define MHU_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Generic MHU error enumeration types.
+ */
+enum mhu_error_t {
+       MHU_ERR_NONE                    =  0,
+       MHU_ERR_NOT_INIT                = -1,
+       MHU_ERR_ALREADY_INIT            = -2,
+       MHU_ERR_UNSUPPORTED_VERSION     = -3,
+       MHU_ERR_UNSUPPORTED             = -4,
+       MHU_ERR_INVALID_ARG             = -5,
+       MHU_ERR_BUFFER_TOO_SMALL        = -6,
+       MHU_ERR_GENERAL                 = -7,
+};
+
+/**
+ * Initializes sender MHU.
+ *
+ * mhu_sender_base     Base address of sender MHU.
+ *
+ * Returns mhu_error_t error code.
+ *
+ * This function must be called before mhu_send_data().
+ */
+enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base);
+
+
+/**
+ * Initializes receiver MHU.
+ *
+ * mhu_receiver_base   Base address of receiver MHU.
+ *
+ * Returns mhu_error_t error code.
+ *
+ * This function must be called before mhu_receive_data().
+ */
+enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base);
+
+/**
+ * Sends data over MHU.
+ *
+ * send_buffer         Pointer to buffer containing the data to be transmitted.
+ * size                        Size of the data to be transmitted in bytes.
+ *
+ * Returns mhu_error_t error code.
+ *
+ * The send_buffer must be 4-byte aligned and its length must be at least
+ * (4 - (size % 4)) bytes bigger than the data size to prevent buffer
+ * over-reading.
+ */
+enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size);
+
+/**
+ * Receives data from MHU.
+ *
+ * receive_buffer      Pointer the buffer where to store the received data.
+ * size                        As input the size of the receive_buffer, as output the
+ *                     number of bytes received. As a limitation,
+ *                     the size of the buffer must be a multiple of 4.
+ *
+ * Returns mhu_error_t error code.
+ *
+ * The receive_buffer must be 4-byte aligned and its length must be a
+ * multiple of 4.
+ */
+enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size);
+
+#endif /* MHU_H */