aboutsummaryrefslogtreecommitdiff
path: root/product/morello
diff options
context:
space:
mode:
authorAnurag Koul <anurag.koul@arm.com>2020-06-04 15:48:04 +0100
committerjimqui01 <54316584+jimqui01@users.noreply.github.com>2020-09-15 17:03:53 +0100
commit26147ed9dfd63ca34802a2180b66a08f9787204d (patch)
tree9c9cc085e8c34baa3093ba04a1e20ffdda31e5b3 /product/morello
parentc8487ff6a25825ded5c18833b0be56c8cee21f56 (diff)
morello/module: add morello pll module
Morello has its PLL different from the PLLs used in other platforms (which make use of system_pll module). This patch adds a module, 'morello_pll', for handling Morello PLL. Change-Id: Idc1b7aa15d1a819272f4f4afb651eb5e7c092cc0 Signed-off-by: Anurag Koul <anurag.koul@arm.com>
Diffstat (limited to 'product/morello')
-rw-r--r--product/morello/module/morello_pll/include/internal/morello_pll.h57
-rw-r--r--product/morello/module/morello_pll/include/mod_morello_pll.h105
-rw-r--r--product/morello/module/morello_pll/src/Makefile11
-rw-r--r--product/morello/module/morello_pll/src/mod_morello_pll.c347
4 files changed, 520 insertions, 0 deletions
diff --git a/product/morello/module/morello_pll/include/internal/morello_pll.h b/product/morello/module/morello_pll/include/internal/morello_pll.h
new file mode 100644
index 00000000..08b442e8
--- /dev/null
+++ b/product/morello/module/morello_pll/include/internal/morello_pll.h
@@ -0,0 +1,57 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * MORELLO PLL register definitions
+ */
+
+#ifndef INTERNAL_MORELLO_PLL_H
+#define INTERNAL_MORELLO_PLL_H
+
+#include <fwk_macros.h>
+
+/* PLL Control Register 0 bit positions. */
+#define PLL_HARD_BYPASS_BIT_POS 0
+#define PLL_PD_TIMER_BYPASS_BIT_POS 1
+#define PLL_LOCK_SEL_BIT_POS 2
+#define PLL_DSMEN_BIT_POS 4
+#define PLL_DACEN_BIT_POS 5
+#define PLL_BYPASS_POS 6
+#define PLL_FBDIV_BIT_POS 8
+#define PLL_REFDIV_POS 20
+#define PLL_PLLEN_POS 31
+
+/* PLL Control Register 1 bit positions. */
+#define PLL_FRAC_POS 0
+#define PLL_POSTDIV1_POS 24
+#define PLL_POSTDIV2_POS 28
+#define PLL_LOCK_STATUS_POS 31
+
+/*! The minimum frequency that the PLL hardware can output. */
+#define MOD_MORELLO_PLL_RATE_MIN (50UL * FWK_MHZ)
+
+/*! The maximum frequency that the PLL hardware can output. */
+#define MOD_MORELLO_PLL_RATE_MAX (3200UL * FWK_MHZ)
+
+/*! Step size for the PLL. */
+#define MOD_MORELLO_PLL_STEP_SIZE UINT64_C(1000)
+
+/*! The minimum feedback divider value */
+#define MOD_MORELLO_PLL_FBDIV_MIN 16
+/*! The maximum feedback divider value */
+#define MOD_MORELLO_PLL_FBDIV_MAX 1600
+
+/*! The minimum reference clock divider value */
+#define MOD_MORELLO_PLL_REFDIV_MIN 1
+/*! The maximum reference clock divider value */
+#define MOD_MORELLO_PLL_REFDIV_MAX 63
+
+/*! The minimum post divider value */
+#define MOD_MORELLO_PLL_POSTDIV_MIN 1
+/*! The maximum post divider value */
+#define MOD_MORELLO_PLL_POSTDIV_MAX 7
+
+#endif /* INTERNAL_MORELLO_PLL_H */
diff --git a/product/morello/module/morello_pll/include/mod_morello_pll.h b/product/morello/module/morello_pll/include/mod_morello_pll.h
new file mode 100644
index 00000000..8b520446
--- /dev/null
+++ b/product/morello/module/morello_pll/include/mod_morello_pll.h
@@ -0,0 +1,105 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MORELLO_PLL_H
+#define MOD_MORELLO_PLL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*!
+ * \addtogroup GroupMORELLOModule MORELLO Product Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupMORELLOPLL MORELLO PLL Driver
+ *
+ * \details A driver for PLL hardware in MORELLO product.
+ *
+ * @{
+ */
+
+/*! Timeout value to wait for a PLL to lock. */
+#define MOD_MORELLO_PLL_LOCK_TIMEOUT UINT32_C(0x100000)
+
+/*! Indexes of APIs that the module offers for binding. */
+enum mod_morello_pll_api_types {
+ MOD_MORELLO_PLL_API_TYPE_DEFAULT,
+ MOD_MORELLO_PLL_API_COUNT,
+};
+
+/*!
+ * \brief PLL device configuration.
+ */
+struct mod_morello_pll_dev_config {
+ /*! Pointer to the PLL's control register 0. */
+ volatile uint32_t *const control_reg0;
+
+ /*! Pointer to the PLL's control register 1. */
+ volatile uint32_t *const control_reg1;
+
+ /*! The initial rate the PLL is set to during initialization. */
+ const uint64_t initial_rate;
+
+ /*!
+ * The frequency of the reference clock applied to the PLL. Each PLL
+ * instance has a dedicated reference clock input configured through
+ * the board controller and the same value should be used in module's
+ * element configuration table. This value will be multiplied with a
+ * multiplication factor by the PLL to generate the required output
+ * frequency.
+ */
+ const uint64_t ref_rate;
+
+ /*!
+ * If \c true, the driver will not attempt to set a default frequency, or
+ * to otherwise configure the PLL during the pre-runtime phase. The PLL is
+ * expected to be initialized later in response to a notification or other
+ * event.
+ */
+ const bool defer_initialization;
+};
+
+/*!
+ * \brief PLL parameter values for non-absolute frequencies.
+ */
+struct morello_pll_custom_freq_param_entry {
+ /*! Required output frequency value in MHz */
+ uint16_t freq_value_mhz;
+
+ /*! Feedback divider value for this frequency */
+ uint16_t fbdiv;
+
+ /*! Reference clock divider value for this frequency */
+ uint8_t refdiv;
+
+ /*! Post divider 1 value for this frequency */
+ uint8_t postdiv;
+};
+
+/*!
+ * \brief MORELLO PLL module configuration.
+ */
+struct morello_pll_module_config {
+ /*! Pointer to custom frequency table */
+ struct morello_pll_custom_freq_param_entry *custom_freq_table;
+
+ /*! Size of custom frequency table */
+ size_t custom_freq_table_size;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_MORELLO_PLL_H */
diff --git a/product/morello/module/morello_pll/src/Makefile b/product/morello/module/morello_pll/src/Makefile
new file mode 100644
index 00000000..a4b7ec3a
--- /dev/null
+++ b/product/morello/module/morello_pll/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := MORELLO PLL
+BS_LIB_SOURCES := mod_morello_pll.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/morello/module/morello_pll/src/mod_morello_pll.c b/product/morello/module/morello_pll/src/mod_morello_pll.c
new file mode 100644
index 00000000..ee058b94
--- /dev/null
+++ b/product/morello/module/morello_pll/src/mod_morello_pll.c
@@ -0,0 +1,347 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <internal/morello_pll.h>
+
+#include <mod_clock.h>
+#include <mod_morello_pll.h>
+#include <mod_power_domain.h>
+
+#include <fwk_assert.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_status.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Device context */
+struct morello_pll_dev_ctx {
+ /* Configuration data of the PLL instance */
+ const struct mod_morello_pll_dev_config *config;
+
+ /* Current state of the PLL */
+ enum mod_clock_state current_state;
+
+ /* Current frequency output of the PLL */
+ uint64_t current_rate;
+
+ /* Initialization state of the PLL */
+ bool initialized;
+};
+
+/* Module context */
+struct morello_pll_ctx {
+ /* Pointer to module configuration data */
+ const struct morello_pll_module_config *mod_config;
+
+ /* List of device contexts of all PLLs */
+ struct morello_pll_dev_ctx *dev_ctx_table;
+
+ /* Number of PLL instances */
+ unsigned int dev_count;
+};
+
+static struct morello_pll_ctx module_ctx;
+
+/*
+ * PLL rate configuration function
+ */
+
+static int pll_set_rate(
+ struct morello_pll_dev_ctx *ctx,
+ uint64_t rate,
+ enum mod_clock_round_mode unused)
+{
+ uint64_t rounded_rate;
+ uint16_t fbdiv;
+ uint8_t refdiv;
+ uint8_t postdiv;
+ uint32_t wait_cycles;
+ uint16_t rate_val_mhz;
+ const struct mod_morello_pll_dev_config *config = NULL;
+ struct morello_pll_custom_freq_param_entry *freq_entry = NULL;
+ size_t i;
+
+ fwk_assert(ctx != NULL);
+ fwk_assert(rate <= (UINT16_MAX * FWK_MHZ));
+
+ config = ctx->config;
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ if ((rate < MOD_MORELLO_PLL_RATE_MIN) || (rate > MOD_MORELLO_PLL_RATE_MAX))
+ return FWK_E_RANGE;
+
+ /* Assume initial refdiv and postdiv to be 1 */
+ refdiv = MOD_MORELLO_PLL_REFDIV_MIN;
+ postdiv = MOD_MORELLO_PLL_POSTDIV_MIN;
+ fbdiv = rate / config->ref_rate;
+ rounded_rate = fbdiv * config->ref_rate;
+
+ /*
+ * If required output value is not exact multiplication of reference
+ * clock value then look for the frequency in custom frequencies table.
+ */
+ if (rounded_rate != rate) {
+ rate_val_mhz = (uint16_t)(rate / FWK_MHZ);
+ for (i = 0; i < module_ctx.mod_config->custom_freq_table_size; i++) {
+ freq_entry = &module_ctx.mod_config->custom_freq_table[i];
+ if (freq_entry->freq_value_mhz == rate_val_mhz) {
+ fbdiv = freq_entry->fbdiv;
+ refdiv = freq_entry->refdiv;
+ postdiv = freq_entry->postdiv;
+ goto result;
+ }
+ }
+ /* Custom frequency table does not have matching frequency */
+ return FWK_E_RANGE;
+ }
+
+result:
+ /* Configure PLL settings */
+ *config->control_reg0 =
+ (fbdiv << PLL_FBDIV_BIT_POS) | (refdiv << PLL_REFDIV_POS);
+ *config->control_reg1 =
+ (postdiv << PLL_POSTDIV1_POS) | (1 << PLL_POSTDIV2_POS);
+
+ /* Enable PLL settings */
+ *config->control_reg0 |= (UINT32_C(1) << PLL_PLLEN_POS);
+
+ wait_cycles = MOD_MORELLO_PLL_LOCK_TIMEOUT;
+ /* Wait until the PLL has locked */
+ while ((*config->control_reg1 & (UINT32_C(1) << PLL_LOCK_STATUS_POS)) ==
+ 0) {
+ wait_cycles--;
+ if (wait_cycles == 0)
+ return FWK_E_TIMEOUT;
+ }
+
+ /* Store the current configured PLL rate */
+ ctx->current_rate = rate;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock driver API functions
+ */
+
+static int morello_pll_set_rate(
+ fwk_id_t dev_id,
+ uint64_t rate,
+ enum mod_clock_round_mode unused)
+{
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ return pll_set_rate(ctx, rate, unused);
+}
+
+static int morello_pll_get_rate(fwk_id_t dev_id, uint64_t *rate)
+{
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ if ((!fwk_module_is_valid_element_id(dev_id)) || (rate == NULL))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *rate = ctx->current_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int morello_pll_get_rate_from_index(
+ fwk_id_t dev_id,
+ unsigned int rate_index,
+ uint64_t *rate)
+{
+ /* PLLs have a continuous range of rates and are not indexed */
+ return FWK_E_SUPPORT;
+}
+
+static int morello_pll_set_state(fwk_id_t dev_id, enum mod_clock_state state)
+{
+ if (state == MOD_CLOCK_STATE_RUNNING)
+ return FWK_SUCCESS;
+
+ /* PLLs can only be stopped by a parent power domain state change. */
+ return FWK_E_SUPPORT;
+}
+
+static int morello_pll_get_state(fwk_id_t dev_id, enum mod_clock_state *state)
+{
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ if ((!fwk_module_is_valid_element_id(dev_id)) || (state == NULL))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *state = ctx->current_state;
+
+ return FWK_SUCCESS;
+}
+
+static int morello_pll_get_range(fwk_id_t dev_id, struct mod_clock_range *range)
+{
+ if ((!fwk_module_is_valid_element_id(dev_id)) || (range == NULL))
+ return FWK_E_PARAM;
+
+ range->rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS;
+ range->min = MOD_MORELLO_PLL_RATE_MIN;
+ range->max = MOD_MORELLO_PLL_RATE_MAX;
+ range->step = MOD_MORELLO_PLL_STEP_SIZE;
+
+ return FWK_SUCCESS;
+}
+
+static int morello_pll_power_state_change(fwk_id_t dev_id, unsigned int state)
+{
+ uint64_t rate;
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (state != MOD_PD_STATE_ON)
+ return FWK_SUCCESS;
+
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+
+ if (ctx->initialized) {
+ /* Restore the previous rate */
+ rate = ctx->current_rate;
+ } else {
+ /* Initialize the PLL to its default rate */
+ ctx->initialized = true;
+ rate = ctx->config->initial_rate;
+ }
+
+ return morello_pll_set_rate(dev_id, rate, MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int morello_pll_power_state_pending_change(
+ fwk_id_t dev_id,
+ unsigned int current_state,
+ unsigned int next_state)
+{
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (next_state == MOD_PD_STATE_OFF)
+ /* Just mark the PLL as stopped */
+ ctx->current_state = MOD_CLOCK_STATE_STOPPED;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_drv_api morello_pll_api = {
+ .set_rate = morello_pll_set_rate,
+ .get_rate = morello_pll_get_rate,
+ .get_rate_from_index = morello_pll_get_rate_from_index,
+ .set_state = morello_pll_set_state,
+ .get_state = morello_pll_get_state,
+ .get_range = morello_pll_get_range,
+ .process_power_transition = morello_pll_power_state_change,
+ .process_pending_power_transition = morello_pll_power_state_pending_change,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int morello_pll_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *config)
+{
+ size_t i;
+ struct morello_pll_custom_freq_param_entry *freq_entry;
+
+ if ((element_count == 0) || (config == NULL))
+ return FWK_E_PARAM;
+
+ module_ctx.dev_count = element_count;
+
+ module_ctx.dev_ctx_table =
+ fwk_mm_calloc(element_count, sizeof(struct morello_pll_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ module_ctx.mod_config = config;
+ /* Validate custom frequency table entries */
+ for (i = 0; i < module_ctx.mod_config->custom_freq_table_size; i++) {
+ freq_entry = &module_ctx.mod_config->custom_freq_table[i];
+ if ((freq_entry->fbdiv < MOD_MORELLO_PLL_FBDIV_MIN) ||
+ (freq_entry->fbdiv > MOD_MORELLO_PLL_FBDIV_MAX) ||
+ (freq_entry->refdiv < MOD_MORELLO_PLL_REFDIV_MIN) ||
+ (freq_entry->refdiv > MOD_MORELLO_PLL_REFDIV_MAX) ||
+ (freq_entry->postdiv < MOD_MORELLO_PLL_POSTDIV_MIN) ||
+ (freq_entry->postdiv > MOD_MORELLO_PLL_POSTDIV_MAX))
+ return FWK_E_RANGE;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int morello_pll_element_init(
+ fwk_id_t element_id,
+ unsigned int unused,
+ const void *data)
+{
+ struct morello_pll_dev_ctx *ctx = NULL;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+
+ ctx->config = (struct mod_morello_pll_dev_config *)data;
+
+ /* Check for valid element configuration data */
+ if ((ctx->config->control_reg0 == NULL) ||
+ (ctx->config->control_reg1 == NULL) || (ctx->config->ref_rate == 0))
+ return FWK_E_PARAM;
+
+ if (ctx->config->defer_initialization)
+ return FWK_SUCCESS;
+
+ ctx->initialized = true;
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ return pll_set_rate(
+ ctx, ctx->config->initial_rate, MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int morello_pll_process_bind_request(
+ fwk_id_t requester_id,
+ fwk_id_t id,
+ fwk_id_t api_type,
+ const void **api)
+{
+ *api = &morello_pll_api;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_morello_pll = {
+ .name = "MORELLO PLL Driver",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_MORELLO_PLL_API_COUNT,
+ .event_count = 0,
+ .init = morello_pll_init,
+ .element_init = morello_pll_element_init,
+ .process_bind_request = morello_pll_process_bind_request,
+};