From fd441b4dcc687bdd1f10851a940504b9bf3cfa55 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Thu, 21 Nov 2024 13:53:47 +0100 Subject: [PATCH] [nrf fromlist] drivers: clock_control: nrf2: add support for global hfsll clock Add device driver support for global hsfll clock. Upstream PR #: 81735 Signed-off-by: Bjarki Arge Andreasen --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig.nrf | 13 + .../clock_control_nrf2_global_hsfll.c | 259 ++++++++++++++++++ .../clock_control/clock_control_nrf2_hsfll.c | 16 +- 4 files changed, 274 insertions(+), 15 deletions(-) create mode 100644 drivers/clock_control/clock_control_nrf2_global_hsfll.c diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index ffc19a3b729..d3d1807d6cf 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RENESAS_RA_CGC clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AMBIQ clock_control_ambiq.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_PWM clock_control_pwm.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RPI_PICO clock_control_rpi_pico.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL clock_control_nrf2_global_hsfll.c) if(CONFIG_CLOCK_CONTROL_NRF2) zephyr_library_sources(clock_control_nrf2_common.c) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index f7b0de3db09..74d2cf3b0a5 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -193,4 +193,17 @@ config CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS int "Timeout waiting for nrfs clock service callback in milliseconds" default 1000 +config CLOCK_CONTROL_NRF2_GLOBAL_HSFLL + bool "Clock control for global HSFLL" + depends on NRFS_GDFS_SERVICE_ENABLED + default y + +if CLOCK_CONTROL_NRF2_GLOBAL_HSFLL + +config CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_TIMEOUT_MS + int "Frequency request timeout in milliseconds" + default 1000 + +endif # CLOCK_CONTROL_NRF2_GLOBAL_HSFLL + endif # CLOCK_CONTROL_NRF2 diff --git a/drivers/clock_control/clock_control_nrf2_global_hsfll.c b/drivers/clock_control/clock_control_nrf2_global_hsfll.c new file mode 100644 index 00000000000..161e5a76116 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf2_global_hsfll.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_nrf_global_hsfll + +#include "clock_control_nrf2_common.h" +#include +#include +#include + +#include +LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define GLOBAL_HSFLL_CLOCK_FREQUENCIES \ + DT_INST_PROP(0, supported_clock_frequencies) + +#define GLOBAL_HSFLL_CLOCK_FREQUENCIES_IDX(idx) \ + DT_INST_PROP_BY_IDX(0, supported_clock_frequencies, idx) + +#define GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE \ + DT_INST_PROP_LEN(0, supported_clock_frequencies) + +#define GLOBAL_HSFLL_FREQ_REQ_TIMEOUT \ + K_MSEC(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_TIMEOUT_MS) + +BUILD_ASSERT(GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE == 4); +BUILD_ASSERT(GLOBAL_HSFLL_CLOCK_FREQUENCIES_IDX(0) == 64000000); +BUILD_ASSERT(GLOBAL_HSFLL_CLOCK_FREQUENCIES_IDX(1) == 128000000); +BUILD_ASSERT(GLOBAL_HSFLL_CLOCK_FREQUENCIES_IDX(2) == 256000000); +BUILD_ASSERT(GLOBAL_HSFLL_CLOCK_FREQUENCIES_IDX(3) == 320000000); +BUILD_ASSERT(GDFS_FREQ_COUNT == 4); +BUILD_ASSERT(GDFS_FREQ_HIGH == 0); +BUILD_ASSERT(GDFS_FREQ_MEDHIGH == 1); +BUILD_ASSERT(GDFS_FREQ_MEDLOW == 2); +BUILD_ASSERT(GDFS_FREQ_LOW == 3); + +struct global_hsfll_dev_config { + uint32_t clock_frequencies[GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE]; +}; + +struct global_hsfll_dev_data { + STRUCT_CLOCK_CONFIG(global_hsfll, GLOBAL_HSFLL_CLOCK_FREQUENCIES_SIZE) clk_cfg; + const struct device *dev; + struct k_work evt_work; + nrfs_gdfs_evt_type_t evt; + struct k_work_delayable timeout_dwork; +}; + +static uint32_t global_hsfll_get_max_clock_frequency(const struct device *dev) +{ + const struct global_hsfll_dev_config *dev_config = dev->config; + + return dev_config->clock_frequencies[ARRAY_SIZE(dev_config->clock_frequencies) - 1]; +} + +static struct onoff_manager *global_hsfll_find_mgr(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + struct global_hsfll_dev_data *dev_data = dev->data; + const struct global_hsfll_dev_config *dev_config = dev->config; + uint32_t frequency; + + if (!spec) { + return &dev_data->clk_cfg.onoff[0].mgr; + } + + if (spec->accuracy || spec->precision) { + LOG_ERR("invalid specification of accuracy or precision"); + return NULL; + } + + frequency = spec->frequency == NRF_CLOCK_CONTROL_FREQUENCY_MAX + ? global_hsfll_get_max_clock_frequency(dev) + : spec->frequency; + + for (uint8_t i = 0; i < ARRAY_SIZE(dev_config->clock_frequencies); i++) { + if (dev_config->clock_frequencies[i] < frequency) { + continue; + } + + return &dev_data->clk_cfg.onoff[i].mgr; + } + + LOG_ERR("invalid frequency"); + return NULL; +} + +static int api_request_global_hsfll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); + + if (mgr) { + return onoff_request(mgr, cli); + } + + return -EINVAL; +} + +static int api_release_global_hsfll(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); + + if (mgr) { + return onoff_release(mgr); + } + + return -EINVAL; +} + +static int api_cancel_or_release_global_hsfll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); + + if (mgr) { + return onoff_cancel_or_release(mgr, cli); + } + + return -EINVAL; +} + +static struct nrf_clock_control_driver_api driver_api = { + .std_api = { + .on = api_nosys_on_off, + .off = api_nosys_on_off, + }, + .request = api_request_global_hsfll, + .release = api_release_global_hsfll, + .cancel_or_release = api_cancel_or_release_global_hsfll, +}; + +static enum gdfs_frequency_setting global_hsfll_freq_idx_to_nrfs_freq(const struct device *dev, + uint8_t freq_idx) +{ + const struct global_hsfll_dev_config *dev_config = dev->config; + + return ARRAY_SIZE(dev_config->clock_frequencies) - 1 - freq_idx; +} + +static const char *global_hsfll_gdfs_freq_to_str(enum gdfs_frequency_setting freq) +{ + switch (freq) { + case GDFS_FREQ_HIGH: + return "GDFS_FREQ_HIGH"; + case GDFS_FREQ_MEDHIGH: + return "GDFS_FREQ_MEDHIGH"; + case GDFS_FREQ_MEDLOW: + return "GDFS_FREQ_MEDLOW"; + case GDFS_FREQ_LOW: + return "GDFS_FREQ_LOW"; + default: + break; + } + + return "UNKNOWN"; +} + +static void global_hsfll_work_handler(struct k_work *work) +{ + struct global_hsfll_dev_data *dev_data = + CONTAINER_OF(work, struct global_hsfll_dev_data, clk_cfg.work); + const struct device *dev = dev_data->dev; + uint8_t freq_idx; + enum gdfs_frequency_setting target_freq; + nrfs_err_t err; + + freq_idx = clock_config_update_begin(work); + target_freq = global_hsfll_freq_idx_to_nrfs_freq(dev, freq_idx); + + LOG_DBG("requesting %s", global_hsfll_gdfs_freq_to_str(target_freq)); + err = nrfs_gdfs_request_freq(target_freq, dev_data); + if (err != NRFS_SUCCESS) { + clock_config_update_end(&dev_data->clk_cfg, -EIO); + return; + } + + k_work_schedule(&dev_data->timeout_dwork, GLOBAL_HSFLL_FREQ_REQ_TIMEOUT); +} + +static void global_hsfll_evt_handler(struct k_work *work) +{ + struct global_hsfll_dev_data *dev_data = + CONTAINER_OF(work, struct global_hsfll_dev_data, evt_work); + int rc; + + k_work_cancel_delayable(&dev_data->timeout_dwork); + rc = dev_data->evt == NRFS_GDFS_EVT_FREQ_CONFIRMED ? 0 : -EIO; + clock_config_update_end(&dev_data->clk_cfg, rc); +} + +static void global_hfsll_nrfs_gdfs_evt_handler(nrfs_gdfs_evt_t const *p_evt, void *context) +{ + struct global_hsfll_dev_data *dev_data = context; + + if (k_work_is_pending(&dev_data->evt_work)) { + return; + } + + dev_data->evt = p_evt->type; + k_work_submit(&dev_data->evt_work); +} + +static void global_hsfll_timeout_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct global_hsfll_dev_data *dev_data = + CONTAINER_OF(dwork, struct global_hsfll_dev_data, timeout_dwork); + + clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT); +} + +static int global_hfsll_init(const struct device *dev) +{ + struct global_hsfll_dev_data *dev_data = dev->data; + int rc; + nrfs_err_t err; + + rc = clock_config_init(&dev_data->clk_cfg, + ARRAY_SIZE(dev_data->clk_cfg.onoff), + global_hsfll_work_handler); + if (rc < 0) { + return rc; + } + + k_work_init_delayable(&dev_data->timeout_dwork, global_hsfll_timeout_handler); + k_work_init(&dev_data->evt_work, global_hsfll_evt_handler); + + err = nrfs_gdfs_init(global_hfsll_nrfs_gdfs_evt_handler); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + return 0; +} + +static struct global_hsfll_dev_data driver_data = { + .dev = DEVICE_DT_INST_GET(0), +}; + +static const struct global_hsfll_dev_config driver_config = { + GLOBAL_HSFLL_CLOCK_FREQUENCIES +}; + +DEVICE_DT_INST_DEFINE( + 0, + global_hfsll_init, + NULL, + &driver_data, + &driver_config, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &driver_api +); diff --git a/drivers/clock_control/clock_control_nrf2_hsfll.c b/drivers/clock_control/clock_control_nrf2_hsfll.c index 026edf9abbd..73a422bb7b0 100644 --- a/drivers/clock_control/clock_control_nrf2_hsfll.c +++ b/drivers/clock_control/clock_control_nrf2_hsfll.c @@ -8,7 +8,6 @@ #include "clock_control_nrf2_common.h" #include #include -#include #include LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); @@ -48,7 +47,7 @@ static const struct clock_options { }; struct hsfll_dev_data { - STRUCT_CLOCK_CONFIG(hsfll, ARRAY_SIZE(clock_options)) clk_cfg; + STRUCT_CLOCK_CONFIG(global_hsfll, ARRAY_SIZE(clock_options)) clk_cfg; struct k_timer timer; }; @@ -183,18 +182,6 @@ static int api_cancel_or_release_hsfll(const struct device *dev, #endif } -static int api_get_rate_hsfll(const struct device *dev, - clock_control_subsys_t sys, - uint32_t *rate) -{ - ARG_UNUSED(dev); - ARG_UNUSED(sys); - - *rate = nrf_hsfll_clkctrl_mult_get(NRF_HSFLL) * MHZ(16); - - return 0; -} - static int hsfll_init(const struct device *dev) { #ifdef CONFIG_NRFS_DVFS_LOCAL_DOMAIN @@ -221,7 +208,6 @@ static struct nrf_clock_control_driver_api hsfll_drv_api = { .std_api = { .on = api_nosys_on_off, .off = api_nosys_on_off, - .get_rate = api_get_rate_hsfll, }, .request = api_request_hsfll, .release = api_release_hsfll,