aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Custodio <Pedro.Krewinkelcustodio@arm.com>2018-08-14 16:28:44 +0100
committerronald-cron-arm <39518861+ronald-cron-arm@users.noreply.github.com>2019-01-08 17:14:25 +0000
commit3064c5411946bceaa71e912920bfed3a9991f32e (patch)
tree28bc2054fb691f167f333dfcfa61882954a72f60
parentc54aae2e80c02af05e7269e9128ececf28c64a4f (diff)
module: MHUv2 support
Change-Id: I3335f0511b2bb00811ebb317273ff693b1dc903b Signed-off-by: Elieva Pignat <Elieva.Pignat@arm.com> Signed-off-by: Chris Kay <chris.kay@arm.com>
-rw-r--r--module/mhu2/include/mod_mhu2.h66
-rw-r--r--module/mhu2/src/Makefile11
-rw-r--r--module/mhu2/src/mhu2.h67
-rw-r--r--module/mhu2/src/mod_mhu2.c292
4 files changed, 436 insertions, 0 deletions
diff --git a/module/mhu2/include/mod_mhu2.h b/module/mhu2/include/mod_mhu2.h
new file mode 100644
index 00000000..3f87e780
--- /dev/null
+++ b/module/mhu2/include/mod_mhu2.h
@@ -0,0 +1,66 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Message Handling Unit (MHU) v2 Device Driver.
+ */
+
+#ifndef MOD_MHU2_H
+#define MOD_MHU2_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupMHUv2 Message Handling Unit (MHU) v2 Driver
+ * @{
+ */
+
+/*!
+ * \brief MHU v2 api indicies
+ */
+enum mod_mhu2_api_idx {
+ /*! SMT driver API */
+ MOD_MHU2_API_IDX_SMT_DRIVER,
+ /*! Number of APIs */
+ MOD_MHU2_API_IDX_COUNT,
+};
+
+/*!
+ * \brief MHU v2 device
+ *
+ * \details Abstract representation of a bidirectional MHU channel that consists
+ * of a single receive interrupt line and a pair of register sets, one for
+ * each direction of communication.
+ */
+struct mod_mhu2_channel_config {
+ /*! IRQ number of the receive interrupt line */
+ unsigned int irq;
+
+ /*! Base address of the registers of the incoming MHU */
+ uintptr_t recv;
+
+ /*! Base address of the registers of the outgoing MHU */
+ uintptr_t send;
+
+ /*! Channel number */
+ unsigned int channel;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_MHU2_H */
diff --git a/module/mhu2/src/Makefile b/module/mhu2/src/Makefile
new file mode 100644
index 00000000..0e44a3cc
--- /dev/null
+++ b/module/mhu2/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := MHU2
+BS_LIB_SOURCES := mod_mhu2.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/mhu2/src/mhu2.h b/module/mhu2/src/mhu2.h
new file mode 100644
index 00000000..38bbec03
--- /dev/null
+++ b/module/mhu2/src/mhu2.h
@@ -0,0 +1,67 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MHU2_H
+#define MHU2_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+#define CHANNEL_MAX 124
+
+struct mhu2_id_reg {
+ FWK_R uint32_t PID4;
+ uint8_t RESERVED1[0x10 - 0x4];
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t COMPID0;
+ FWK_R uint32_t COMPID1;
+ FWK_R uint32_t COMPID2;
+ FWK_R uint32_t COMPID3;
+};
+
+struct mhu2_send_channel_reg {
+ FWK_R uint32_t STAT;
+ uint8_t RESERVED0[0xC - 0x4];
+ FWK_W uint32_t STAT_SET;
+ uint8_t RESERVED1[0x20 - 0x10];
+};
+
+struct mhu2_send_reg {
+ struct mhu2_send_channel_reg channel[CHANNEL_MAX];
+ FWK_R uint32_t MSG_NO_CAP;
+ FWK_RW uint32_t RESP_CFG;
+ FWK_RW uint32_t ACCESS_REQUEST;
+ FWK_R uint32_t ACCESS_READY;
+ FWK_R uint32_t INT_ACCESS_STAT;
+ FWK_W uint32_t INT_ACCESS_CLR;
+ FWK_W uint32_t INT_ACCESS_EN;
+ uint8_t RESERVED0[0xFD0 - 0xF9C];
+ struct mhu2_id_reg id;
+};
+
+struct mhu2_recv_channel_reg {
+ FWK_R uint32_t STAT;
+ FWK_R uint32_t STAT_PEND;
+ FWK_W uint32_t STAT_CLEAR;
+ uint8_t RESERVED0[0x10 - 0x0C];
+ FWK_R uint32_t MASK;
+ FWK_W uint32_t MASK_SET;
+ FWK_W uint32_t MASK_CLEAR;
+ uint8_t RESERVED1[0x20 - 0x1C];
+};
+
+struct mhu2_recv_reg {
+ struct mhu2_recv_channel_reg channel[CHANNEL_MAX];
+ FWK_R uint32_t MSG_NO_CAP;
+ uint8_t RESERVED0[0xFD0 - 0xF84];
+ struct mhu2_id_reg id;
+};
+
+#endif /* MHU2_H */
diff --git a/module/mhu2/src/mod_mhu2.c b/module/mhu2/src/mod_mhu2.c
new file mode 100644
index 00000000..8e776c2f
--- /dev/null
+++ b/module/mhu2/src/mod_mhu2.c
@@ -0,0 +1,292 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Message Handling Unit (MHU) v2 Device Driver.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_assert.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_mhu2.h>
+#include <mhu2.h>
+#include <mod_smt.h>
+
+#define MHU_SLOT_COUNT_MAX 32
+
+struct mhu2_smt_channel {
+ fwk_id_t id;
+ const struct mod_smt_driver_input_api *api;
+};
+
+/* MHU channel context */
+struct mhu2_channel_ctx {
+ /* Pointer to the channel configuration */
+ const struct mod_mhu2_channel_config *config;
+
+ /* Pointer to send register set */
+ struct mhu2_send_reg *send;
+
+ /* Pointers to channel-specific register sets */
+ struct mhu2_send_channel_reg *send_channel;
+ struct mhu2_recv_channel_reg *recv_channel;
+
+ /* Number of slots (represented by sub-elements) */
+ unsigned int slot_count;
+
+ /* Mask of slots that are bound to an SMT channel */
+ uint32_t bound_slots;
+
+ /* Table of SMT channels bound to the channel */
+ struct mhu2_smt_channel *smt_channel_table;
+};
+
+/* MHU v2 context */
+static struct mhu2_ctx {
+ /* Table of channel contexts */
+ struct mhu2_channel_ctx *channel_ctx_table;
+
+ /* Number of channels in the channel context table*/
+ unsigned int channel_count;
+} ctx;
+
+static void mhu2_isr(uintptr_t ctx_param)
+{
+ struct mhu2_channel_ctx *channel_ctx = (struct mhu2_channel_ctx *)ctx_param;
+ unsigned int slot;
+ struct mhu2_smt_channel *smt_channel;
+
+ assert(channel_ctx != NULL);
+
+ while (channel_ctx->recv_channel->STAT != 0) {
+ slot = __builtin_ctz(channel_ctx->recv_channel->STAT);
+
+ /*
+ * If the slot is bound to an SMT channel, signal the message to the
+ * SMT channel.
+ */
+ if (channel_ctx->bound_slots & (1 << slot)) {
+ smt_channel = &channel_ctx->smt_channel_table[slot];
+ smt_channel->api->signal_message(smt_channel->id);
+ }
+
+ /* Acknowledge the interrupt */
+ channel_ctx->recv_channel->STAT_CLEAR = 1 << slot;
+ }
+}
+
+/*
+ * SMT module driver API
+ */
+
+static int raise_interrupt(fwk_id_t slot_id)
+{
+ int status;
+ unsigned int slot;
+ struct mhu2_channel_ctx *channel_ctx;
+ struct mhu2_send_reg *send;
+
+ status = fwk_module_check_call(slot_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(slot_id)];
+ slot = fwk_id_get_sub_element_idx(slot_id);
+ send = channel_ctx->send;
+
+ /* Turn on receiver */
+ send->ACCESS_REQUEST = 1;
+ while (send->ACCESS_READY != 1)
+ continue;
+
+ channel_ctx->send_channel->STAT_SET |= (1 << slot);
+
+ /* Signal that the receiver is no longer needed */
+ send->ACCESS_REQUEST = 0;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_smt_driver_api mhu2_mod_smt_driver_api = {
+ .raise_interrupt = raise_interrupt,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int mhu2_init(fwk_id_t module_id,
+ unsigned int channel_count,
+ const void *unused)
+{
+ if (channel_count == 0) {
+ /* There must be at least 1 mhu channel */
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ ctx.channel_ctx_table = fwk_mm_calloc(channel_count,
+ sizeof(ctx.channel_ctx_table[0]));
+ if (ctx.channel_ctx_table == NULL) {
+ /* Unable to allocate memory for channel context table */
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ ctx.channel_count = channel_count;
+
+ return FWK_SUCCESS;
+}
+
+static int mhu2_channel_init(fwk_id_t channel_id,
+ unsigned int slot_count,
+ const void *data)
+{
+ const struct mod_mhu2_channel_config *config = data;
+ struct mhu2_channel_ctx *channel_ctx;
+ struct mhu2_recv_reg *recv_reg;
+
+ if ((config == NULL) || (config->recv == 0) || (config->send == 0)) {
+ assert(false);
+ return FWK_E_DATA;
+ }
+
+ channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+ channel_ctx->send = (struct mhu2_send_reg *)config->send;
+
+ if (config->channel >= channel_ctx->send->MSG_NO_CAP) {
+ assert(false);
+ return FWK_E_DATA;
+ }
+
+ channel_ctx->config = config;
+ channel_ctx->slot_count = slot_count;
+ channel_ctx->send_channel = &channel_ctx->send->channel[config->channel];
+ recv_reg = (struct mhu2_recv_reg *)config->recv;
+ channel_ctx->recv_channel = &recv_reg->channel[config->channel];
+
+ channel_ctx->smt_channel_table =
+ fwk_mm_calloc(slot_count, sizeof(channel_ctx->smt_channel_table[0]));
+ if (channel_ctx->smt_channel_table == NULL) {
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int mhu2_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct mhu2_channel_ctx *channel_ctx;
+ unsigned int slot;
+ struct mhu2_smt_channel *smt_channel;
+
+ if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
+ channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
+
+ for (slot = 0; slot < MHU_SLOT_COUNT_MAX; slot++) {
+ if (!(channel_ctx->bound_slots & (1 << slot)))
+ continue;
+
+ smt_channel = &channel_ctx->smt_channel_table[slot];
+
+ status = fwk_module_bind(smt_channel->id,
+ FWK_ID_API(FWK_MODULE_IDX_SMT,
+ MOD_SMT_API_IDX_DRIVER_INPUT),
+ &smt_channel->api);
+ if (status != FWK_SUCCESS) {
+ /* Unable to bind back to SMT channel */
+ assert(false);
+ return status;
+ }
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int mhu2_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ struct mhu2_channel_ctx *channel_ctx;
+ unsigned int slot;
+
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_SUB_ELEMENT)) {
+ /*
+ * Something tried to bind to the module or an element. Only binding to
+ * a slot (sub-element) is allowed.
+ */
+ assert(false);
+ return FWK_E_ACCESS;
+ }
+
+ channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(target_id)];
+ slot = fwk_id_get_sub_element_idx(target_id);
+
+ if (channel_ctx->bound_slots & (1 << slot)) {
+ /* Something tried to bind to a slot that has already been bound to */
+ assert(false);
+ return FWK_E_ACCESS;
+ }
+
+ channel_ctx->smt_channel_table[slot].id = source_id;
+ channel_ctx->bound_slots |= 1 << slot;
+
+ *api = &mhu2_mod_smt_driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int mhu2_start(fwk_id_t id)
+{
+ int status;
+ struct mhu2_channel_ctx *channel_ctx;
+
+ if (fwk_id_get_type(id) == FWK_ID_TYPE_MODULE)
+ return FWK_SUCCESS;
+
+ channel_ctx = &ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
+
+ if (channel_ctx->bound_slots != 0) {
+ status = fwk_interrupt_set_isr_param(channel_ctx->config->irq,
+ &mhu2_isr,
+ (uintptr_t)channel_ctx);
+ if (status != FWK_SUCCESS) {
+ /* Failed to set isr */
+ assert(false);
+ return status;
+ }
+ status = fwk_interrupt_enable(channel_ctx->config->irq);
+ if (status != FWK_SUCCESS) {
+ /* Failed to enable isr */
+ assert(false);
+ return status;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+/* MHU v2 module definition */
+const struct fwk_module module_mhu2 = {
+ .name = "MHU2",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_MHU2_API_IDX_COUNT,
+ .init = mhu2_init,
+ .element_init = mhu2_channel_init,
+ .bind = mhu2_bind,
+ .start = mhu2_start,
+ .process_bind_request = mhu2_process_bind_request,
+};