diff options
-rw-r--r-- | module/mock_clock/include/mod_mock_clock.h | 102 | ||||
-rw-r--r-- | module/mock_clock/src/Makefile | 12 | ||||
-rw-r--r-- | module/mock_clock/src/mod_mock_clock.c | 273 |
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, +}; |