mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 09:34:18 +00:00
feat(drivers/arm/mhu): add MHU driver
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
This commit is contained in:
parent
c3bdd3d3cf
commit
af26d7d6f0
4 changed files with 970 additions and 0 deletions
379
drivers/arm/mhu/mhu_v2_x.c
Normal file
379
drivers/arm/mhu/mhu_v2_x.c
Normal file
|
@ -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;
|
||||
}
|
210
drivers/arm/mhu/mhu_v2_x.h
Normal file
210
drivers/arm/mhu/mhu_v2_x.h
Normal file
|
@ -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 */
|
302
drivers/arm/mhu/mhu_wrapper_v2_x.c
Normal file
302
drivers/arm/mhu/mhu_wrapper_v2_x.c
Normal file
|
@ -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;
|
||||
}
|
79
include/drivers/arm/mhu.h
Normal file
79
include/drivers/arm/mhu.h
Normal file
|
@ -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 */
|
Loading…
Add table
Reference in a new issue