diff --git a/drivers/clock_control/clock_control_nrf2_common.c b/drivers/clock_control/clock_control_nrf2_common.c index da3b6b86863..f070d5d7b69 100644 --- a/drivers/clock_control/clock_control_nrf2_common.c +++ b/drivers/clock_control/clock_control_nrf2_common.c @@ -81,6 +81,12 @@ static void onoff_stop_option(struct onoff_manager *mgr, notify(mgr, 0); } +static void onoff_reset_option(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + notify(mgr, 0); +} + static inline uint8_t get_index_of_highest_bit(uint32_t value) { return value ? (uint8_t)(31 - __builtin_clz(value)) : 0; @@ -129,7 +135,8 @@ int clock_config_init(void *clk_cfg, uint8_t onoff_cnt, k_work_handler_t update_ for (int i = 0; i < onoff_cnt; ++i) { static const struct onoff_transitions transitions = { .start = onoff_start_option, - .stop = onoff_stop_option + .stop = onoff_stop_option, + .reset = onoff_reset_option, }; int rc; @@ -148,6 +155,22 @@ int clock_config_init(void *clk_cfg, uint8_t onoff_cnt, k_work_handler_t update_ return 0; } +int clock_config_request(struct onoff_manager *mgr, struct onoff_client *cli) +{ + /* If the on-off service recorded earlier an error, its state must be + * reset before a new request is made, otherwise the request would fail + * immediately. + */ + if (onoff_has_error(mgr)) { + struct onoff_client reset_cli; + + sys_notify_init_spinwait(&reset_cli.notify); + onoff_reset(mgr, &reset_cli); + } + + return onoff_request(mgr, cli); +} + uint8_t clock_config_update_begin(struct k_work *work) { struct clock_config_generic *cfg = diff --git a/drivers/clock_control/clock_control_nrf2_common.h b/drivers/clock_control/clock_control_nrf2_common.h index 1f08e5b090f..7f934fdac5b 100644 --- a/drivers/clock_control/clock_control_nrf2_common.h +++ b/drivers/clock_control/clock_control_nrf2_common.h @@ -59,6 +59,20 @@ int lfosc_get_accuracy(uint16_t *accuracy); */ int clock_config_init(void *clk_cfg, uint8_t onoff_cnt, k_work_handler_t update_work_handler); +/** + * @brief Helper function for requesting a clock configuration handled by + * a given on-off manager. + * + * If needed, the function resets the on-off service prior to making the new + * request. + * + * @param mgr pointer to the manager for which the request is to be done. + * @param cli pointer to a client state structure to be used for the request. + * + * @return result returned by onoff_request(). + */ +int clock_config_request(struct onoff_manager *mgr, struct onoff_client *cli); + /** * @brief Starts a clock configuration update. * diff --git a/drivers/clock_control/clock_control_nrf2_fll16m.c b/drivers/clock_control/clock_control_nrf2_fll16m.c index 69b551e1a45..293a455dac2 100644 --- a/drivers/clock_control/clock_control_nrf2_fll16m.c +++ b/drivers/clock_control/clock_control_nrf2_fll16m.c @@ -174,7 +174,7 @@ static int api_request_fll16m(const struct device *dev, struct onoff_manager *mgr = fll16m_find_mgr(dev, spec); if (mgr) { - return onoff_request(mgr, cli); + return clock_config_request(mgr, cli); } return -EINVAL; diff --git a/drivers/clock_control/clock_control_nrf2_global_hsfll.c b/drivers/clock_control/clock_control_nrf2_global_hsfll.c index 69053c99ff2..50f3396429c 100644 --- a/drivers/clock_control/clock_control_nrf2_global_hsfll.c +++ b/drivers/clock_control/clock_control_nrf2_global_hsfll.c @@ -101,7 +101,7 @@ static int api_request_global_hsfll(const struct device *dev, struct onoff_manager *mgr = global_hsfll_find_mgr(dev, spec); if (mgr) { - return onoff_request(mgr, cli); + return clock_config_request(mgr, cli); } return -EINVAL; diff --git a/drivers/clock_control/clock_control_nrf2_hsfll.c b/drivers/clock_control/clock_control_nrf2_hsfll.c index f67373070f1..eb2dfde989c 100644 --- a/drivers/clock_control/clock_control_nrf2_hsfll.c +++ b/drivers/clock_control/clock_control_nrf2_hsfll.c @@ -140,7 +140,7 @@ static int api_request_hsfll(const struct device *dev, struct onoff_manager *mgr = hsfll_find_mgr(dev, spec); if (mgr) { - return onoff_request(mgr, cli); + return clock_config_request(mgr, cli); } return -EINVAL; diff --git a/drivers/clock_control/clock_control_nrf2_lfclk.c b/drivers/clock_control/clock_control_nrf2_lfclk.c index 2cbef9b8bf0..c5f70c1497b 100644 --- a/drivers/clock_control/clock_control_nrf2_lfclk.c +++ b/drivers/clock_control/clock_control_nrf2_lfclk.c @@ -146,7 +146,7 @@ static int api_request_lfclk(const struct device *dev, struct onoff_manager *mgr = lfclk_find_mgr(dev, spec); if (mgr) { - return onoff_request(mgr, cli); + return clock_config_request(mgr, cli); } return -EINVAL; diff --git a/tests/drivers/clock_control/nrf_clock_control/src/main.c b/tests/drivers/clock_control/nrf_clock_control/src/main.c index 7d3305df5ce..87778bbaae5 100644 --- a/tests/drivers/clock_control/nrf_clock_control/src/main.c +++ b/tests/drivers/clock_control/nrf_clock_control/src/main.c @@ -194,6 +194,9 @@ static void test_clock_control_request(const struct test_clk_context *clk_contex clk_dev = clk_context->clk_dev; clk_spec = &clk_context->clk_specs[u]; + zassert_true(device_is_ready(clk_dev), + "%s is not ready", clk_dev->name); + TC_PRINT("Applying clock (%s) spec: frequency %d, accuracy %d, precision " "%d\n", clk_dev->name, clk_spec->frequency, clk_spec->accuracy, @@ -206,10 +209,7 @@ static void test_clock_control_request(const struct test_clk_context *clk_contex #if CONFIG_BOARD_NRF54H20DK_NRF54H20_CPUAPP ZTEST(nrf2_clock_control, test_cpuapp_hsfll_control) { - TC_PRINT("APPLICATION DOMAIN HSFLL test\n"); - /* Wait for the DVFS init to complete */ - k_msleep(3000); test_clock_control_request(cpuapp_hsfll_test_clk_contexts, ARRAY_SIZE(cpuapp_hsfll_test_clk_contexts)); } @@ -240,6 +240,9 @@ ZTEST(nrf2_clock_control, test_invalid_fll16m_clock_spec_response) clk_dev = clk_context->clk_dev; clk_spec = &clk_context->clk_specs[u]; + zassert_true(device_is_ready(clk_dev), + "%s is not ready", clk_dev->name); + TC_PRINT("Applying clock (%s) spec: frequency %d, accuracy %d, precision " "%d\n", clk_dev->name, clk_spec->frequency, clk_spec->accuracy, @@ -285,6 +288,9 @@ ZTEST(nrf2_clock_control, test_safe_request_cancellation) const struct device *clk_dev = clk_context->clk_dev; const struct nrf_clock_spec *clk_spec = &test_clk_specs_lfclk[0]; + zassert_true(device_is_ready(clk_dev), + "%s is not ready", clk_dev->name); + TC_PRINT("Safe clock request cancellation\n"); TC_PRINT("Clock under test: %s\n", clk_dev->name); sys_notify_init_spinwait(&cli.notify); @@ -298,4 +304,56 @@ ZTEST(nrf2_clock_control, test_safe_request_cancellation) zassert_between_inclusive(ret, ONOFF_STATE_ON, ONOFF_STATE_TO_ON); } -ZTEST_SUITE(nrf2_clock_control, NULL, NULL, NULL, NULL, NULL); +static void *setup(void) +{ +#if defined(CONFIG_BOARD_NRF54H20DK_NRF54H20_CPUAPP) + const struct device *clk_dev = DEVICE_DT_GET(DT_NODELABEL(cpuapp_hsfll)); + const struct nrf_clock_spec clk_spec = { + .frequency = MHZ(64), + }; + struct onoff_client cli; + uint32_t start_uptime; + const uint32_t timeout_ms = 3000; + + zassert_true(device_is_ready(clk_dev), + "%s is not ready", clk_dev->name); + + /* Constantly make requests to DVFS until one is successful (what also + * means that the service has finished its initialization). This loop + * also verifies that the clock control driver is able to recover after + * an unsuccesful attempt to start a clock (at least one initial request + * is expected to fail here due to DFVS not being initialized yet). + */ + TC_PRINT("Polling DVFS until it is ready\n"); + start_uptime = k_uptime_get_32(); + while (1) { + int status; + int ret; + + sys_notify_init_spinwait(&cli.notify); + ret = nrf_clock_control_request(clk_dev, &clk_spec, &cli); + /* The on-off manager for this clock controller is expected to + * always be in the off state when a request is done (its error + * state is expected to be cleared by the clock control driver). + */ + zassert_equal(ret, ONOFF_STATE_OFF, "request result: %d", ret); + do { + ret = sys_notify_fetch_result(&cli.notify, &status); + k_yield(); + } while (ret == -EAGAIN); + + if (status == 0) { + TC_PRINT("DVFS is ready\n"); + break; + } else if ((k_uptime_get_32() - start_uptime) >= timeout_ms) { + TC_PRINT("DVFS is not ready after %u ms\n", timeout_ms); + ztest_test_fail(); + break; + } + } +#endif + + return NULL; +} + +ZTEST_SUITE(nrf2_clock_control, NULL, setup, NULL, NULL, NULL);