aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Vizzarro <Luca.Vizzarro@arm.com>2020-09-20 23:07:12 +0100
committernicola-mazzucato-arm <42373140+nicola-mazzucato-arm@users.noreply.github.com>2020-11-11 14:24:21 +0000
commitfd1a72483414ff74bb3f45dcec8efbfeed85139f (patch)
treebd836264c69fa2c342e11db5351dcb0d33987202
parent3deb59c5f4c64aa092bb3a4ebc018acf3f746f47 (diff)
module: Add Mock Clock module
The mock clock module allows to smoothly test the firmware while using an emulated system, e.g. FVP. Some clock device drivers require hardware features, such as I2C, in order to function correctly. Under an emulated environment, these features cannot be always reproduced, so the mock clock module allows to replace these clock drivers for a smooth operation. Change-Id: I022c56cdf0cadb062742f83d413fa9bdf4adc5aa Signed-off-by: Luca Vizzarro <Luca.Vizzarro@arm.com>
-rw-r--r--module/mock_clock/include/mod_mock_clock.h102
-rw-r--r--module/mock_clock/src/Makefile12
-rw-r--r--module/mock_clock/src/mod_mock_clock.c273
3 files changed, 387 insertions, 0 deletions
diff --git a/module/mock_clock/include/mod_mock_clock.h b/module/mock_clock/include/mod_mock_clock.h
new file mode 100644
index 00000000..e4ff156a
--- /dev/null
+++ b/module/mock_clock/include/mod_mock_clock.h
@@ -0,0 +1,102 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MOCK_CLOCK_H
+#define MOD_MOCK_CLOCK_H
+
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*!
+ * \ingroup GroupModules
+ * \defgroup GroupMockClock Mock Clock Driver
+ *
+ * \details The `mock_clock` module provides a mock clock driver for use
+ * alongside the `clock` interface on systems that do not provide a real
+ * clock driver.
+ *
+ * \warning When using this module to mock a pre-existing driver
+ * for any reason, this driver must be forced to bind to the
+ * ::MOD_MOCK_CLOCK_API_TYPE_RESPONSE_DRIVER API through its module
+ * configuration. This will avoid any conflict between the pre-existing
+ * driver and the mocked one.
+ * \{
+ */
+
+/*!
+ * \brief Rate lookup table entry.
+ */
+struct mod_mock_clock_rate {
+ /*! Rate of the clock in Hertz. */
+ uint32_t rate;
+
+ /*! The clock divider used to attain the rate. */
+ unsigned int divider;
+};
+
+/*!
+ * \brief Element configuration.
+ */
+struct mod_mock_clock_element_cfg {
+ /*! Pointer to the clock's rate lookup table. */
+ const struct mod_mock_clock_rate *rate_table;
+
+ /*! The number of rates in the rate lookup table. */
+ unsigned int rate_count;
+};
+
+/*!
+ * \brief API indices.
+ *
+ * \warning The mock clock implements the clock driver API only. The response
+ * driver API only acts as a pointer to be bound to.
+ */
+enum mod_mock_clock_api_type {
+ /*!
+ * \brief Clock driver.
+ *
+ * \note This API implements the mod_clock::mod_clock_driver_api interface.
+ *
+ * \warning Binding to this API must occur through an element of this
+ * module.
+ */
+ MOD_MOCK_CLOCK_API_TYPE_DRIVER,
+
+ /*!
+ * \brief Clock driver response.
+ *
+ * \note This API implements the mod_clock::mod_clock_driver_response_api
+ * interface.
+ *
+ * \warning Binding to this API must occur through an element of this
+ * module.
+ */
+ MOD_MOCK_CLOCK_API_TYPE_RESPONSE_DRIVER,
+
+ /*!
+ * \brief Number of defined APIs.
+ */
+ MOD_MOCK_CLOCK_API_COUNT,
+};
+
+/*!
+ * \brief Driver API identifier.
+ *
+ * \note This identifier corresponds to the ::MOD_MOCK_CLOCK_API_TYPE_DRIVER API
+ * index.
+ */
+static const fwk_id_t mod_mock_clock_api_id_driver =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_CLOCK, MOD_MOCK_CLOCK_API_TYPE_DRIVER);
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_MOCK_CLOCK_H */
diff --git a/module/mock_clock/src/Makefile b/module/mock_clock/src/Makefile
new file mode 100644
index 00000000..e220b42b
--- /dev/null
+++ b/module/mock_clock/src/Makefile
@@ -0,0 +1,12 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := mock_clock
+BS_LIB_SOURCES := \
+ mod_mock_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/mock_clock/src/mod_mock_clock.c b/module/mock_clock/src/mod_mock_clock.c
new file mode 100644
index 00000000..bf9ff1dc
--- /dev/null
+++ b/module/mock_clock/src/mod_mock_clock.c
@@ -0,0 +1,273 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mod_clock.h>
+#include <mod_mock_clock.h>
+
+#include <fwk_assert.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_status.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static struct mod_mock_clock_element_ctx {
+ const struct mod_mock_clock_element_cfg *config;
+ unsigned int current_rate_index;
+ enum mod_clock_state state;
+
+ /*
+ * We don't rely on the bootloader leaving the clocks
+ * in any specific state.
+ */
+ bool rate_initialized;
+} * elements_ctx;
+
+static struct mod_mock_clock_element_ctx *mod_mock_clock_get_ctx(
+ fwk_id_t element_id)
+{
+ unsigned int element_idx = fwk_id_get_element_idx(element_id);
+
+ return &elements_ctx[element_idx];
+}
+
+static int get_rate_entry(
+ struct mod_mock_clock_element_ctx *ctx,
+ uint64_t target_rate,
+ const struct mod_mock_clock_rate **entry)
+{
+ unsigned int i;
+
+ fwk_assert(ctx != NULL);
+ fwk_assert(entry != NULL);
+
+ /* Find the entry matching the requested rate */
+ for (i = 0; i < ctx->config->rate_count; i++) {
+ if (ctx->config->rate_table[i].rate == target_rate) {
+ *entry = (struct mod_mock_clock_rate *)&ctx->config->rate_table[i];
+ return FWK_SUCCESS;
+ }
+ }
+
+ return FWK_E_PARAM;
+}
+
+/*
+ * Mock clock driver functions
+ */
+
+static int mod_mock_clock_set_rate(
+ fwk_id_t clock_id,
+ uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ struct mod_mock_clock_element_ctx *ctx;
+ const struct mod_mock_clock_rate *rate_entry;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ if (ctx->state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_STATE;
+
+ /*
+ * Look up the divider and source settings. We do not perform any rounding
+ * on the clock rate given as input which has to be precise in order not to
+ * be refused with an FWK_E_PARAM error code.
+ */
+ status = get_rate_entry(ctx, rate, &rate_entry);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PARAM;
+
+ ctx->current_rate_index = rate_entry - ctx->config->rate_table;
+
+ ctx->rate_initialized = true;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_get_rate(fwk_id_t clock_id, uint64_t *rate)
+{
+ struct mod_mock_clock_element_ctx *ctx;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ if (!ctx->rate_initialized)
+ return FWK_E_STATE;
+
+ *rate = ctx->config->rate_table[ctx->current_rate_index].rate;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_get_rate_from_index(
+ fwk_id_t clock_id,
+ unsigned int rate_index,
+ uint64_t *rate)
+{
+ struct mod_mock_clock_element_ctx *ctx;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ if (rate_index >= ctx->config->rate_count)
+ return FWK_E_PARAM;
+
+ *rate = ctx->config->rate_table[rate_index].rate;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_set_state(
+ fwk_id_t clock_id,
+ enum mod_clock_state state)
+{
+ struct mod_mock_clock_element_ctx *ctx;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ ctx->state = state;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_get_state(
+ fwk_id_t clock_id,
+ enum mod_clock_state *state)
+{
+ struct mod_mock_clock_element_ctx *ctx;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ *state = ctx->state;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_get_range(
+ fwk_id_t clock_id,
+ struct mod_clock_range *range)
+{
+ struct mod_mock_clock_element_ctx *ctx;
+
+ ctx = mod_mock_clock_get_ctx(clock_id);
+
+ range->rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE;
+ range->min = ctx->config->rate_table[0].rate;
+ range->max = ctx->config->rate_table[ctx->config->rate_count - 1].rate;
+ range->rate_count = ctx->config->rate_count;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Notification handler invoked after the state of a clock's power domain
+ * has changed.
+ */
+static int mod_mock_clock_process_power_transition(
+ fwk_id_t clock_id,
+ unsigned int state)
+{
+ /* Noop, the power state won't affect this module */
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_drv_api mod_mock_clock_driver_api = {
+ .set_rate = mod_mock_clock_set_rate,
+ .get_rate = mod_mock_clock_get_rate,
+ .get_rate_from_index = mod_mock_clock_get_rate_from_index,
+ .set_state = mod_mock_clock_set_state,
+ .get_state = mod_mock_clock_get_state,
+ .get_range = mod_mock_clock_get_range,
+ .process_power_transition = mod_mock_clock_process_power_transition,
+};
+
+static const struct mod_clock_driver_response_api
+ mod_mock_clock_response_driver_api = { 0 };
+
+static int mod_mock_clock_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ fwk_check(data == NULL);
+
+ elements_ctx = fwk_mm_calloc(element_count, sizeof(elements_ctx[0]));
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_element_init(
+ fwk_id_t element_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ unsigned int rate_index = 0;
+ uint64_t rate;
+ uint64_t last_rate = 0;
+ struct mod_mock_clock_element_ctx *ctx;
+ const struct mod_mock_clock_element_cfg *cfg = data;
+
+ fwk_check(sub_element_count == 0);
+
+ ctx = mod_mock_clock_get_ctx(element_id);
+
+ /* Verify that the rate entries in the lookup table are ordered */
+ for (rate_index = 0; rate_index < cfg->rate_count; rate_index++) {
+ rate = cfg->rate_table[rate_index].rate;
+
+ /* The rate entries must be in ascending order */
+ if (rate < last_rate)
+ return FWK_E_DATA;
+
+ last_rate = rate;
+ }
+
+ ctx->config = cfg;
+ ctx->state = MOD_CLOCK_STATE_RUNNING;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_mock_clock_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ enum mod_mock_clock_api_type api_type = fwk_id_get_api_idx(api_id);
+
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_ACCESS;
+
+ switch (api_type) {
+ case MOD_MOCK_CLOCK_API_TYPE_DRIVER:
+ *api = &mod_mock_clock_driver_api;
+ return FWK_SUCCESS;
+
+ case MOD_MOCK_CLOCK_API_TYPE_RESPONSE_DRIVER:
+ *api = &mod_mock_clock_response_driver_api;
+ return FWK_SUCCESS;
+
+ default:
+ return FWK_E_ACCESS;
+ }
+}
+
+const struct fwk_module module_mock_clock = {
+ .name = "mock_clock",
+ .type = FWK_MODULE_TYPE_DRIVER,
+
+ .init = mod_mock_clock_init,
+ .element_init = mod_mock_clock_element_init,
+
+ .api_count = MOD_MOCK_CLOCK_API_COUNT,
+ .process_bind_request = mod_mock_clock_process_bind_request,
+};