diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index 425f8433bdf..14e980fece0 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -53,9 +53,8 @@ config CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION if CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION config CLOCK_CONTROL_NRF_CALIBRATION_PERIOD - int "Calibration opportunity period (in 250ms units)" - default 16 - range 1 127 + int "Calibration opportunity period in milliseconds" + default 4000 help Periodically, calibration action is performed. Action includes temperature measurement followed by clock calibration. Calibration may diff --git a/drivers/clock_control/nrf_clock_calibration.c b/drivers/clock_control/nrf_clock_calibration.c index 050c75ab917..036f9c8bfd6 100644 --- a/drivers/clock_control/nrf_clock_calibration.c +++ b/drivers/clock_control/nrf_clock_calibration.c @@ -13,36 +13,32 @@ LOG_MODULE_DECLARE(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL); -/* For platforms that do not have CTSTOPPED event CT timer can be started - * immediately after stop. Redefined events to avoid ifdefs in the code, - * CTSTOPPED interrupt handling will be removed during compilation. +/** + * Terms: + * - calibration - overall process of LFRC clock calibration which is performed + * periodically, calibration may include temperature monitoring, hf XTAL + * starting and stopping. + * - cycle - all calibration phases (waiting, temperature monitoring, + * calibration). + * - process - calibration process which may consists of hf XTAL clock + * requesting, performing hw calibration and releasing hf clock. + * - hw_cal - calibration action performed by the hardware. + * + * Those terms are later on used in function names. + * + * In order to ensure that low frequency clock is not released when calibration + * is ongoing, it is requested by the calibration process and released when + * calibration is done. */ -#ifndef CLOCK_EVENTS_CTSTOPPED_EVENTS_CTSTOPPED_Msk -#define NRF_CLOCK_EVENT_CTSTOPPED 0 +#ifndef DT_INST_0_NORDIC_NRF_TEMP_LABEL +#define DT_INST_0_NORDIC_NRF_TEMP_LABEL "" #endif -#ifndef CLOCK_INTENSET_CTSTOPPED_Msk -#define NRF_CLOCK_INT_CTSTOPPED_MASK 0 -#endif - -#define TEMP_SENSOR_NAME \ - COND_CODE_1(CONFIG_TEMP_NRF5, (DT_INST_0_NORDIC_NRF_TEMP_LABEL), (NULL)) - -/* Calibration state enum */ -enum nrf_cal_state { - CAL_OFF, - CAL_IDLE, /* Calibration timer active, waiting for expiration. */ - CAL_HFCLK_REQ, /* HFCLK XTAL requested. */ - CAL_TEMP_REQ, /* Temperature measurement requested. */ - CAL_ACTIVE, /* Ongoing calibration. */ - CAL_ACTIVE_OFF /* Ongoing calibration, off requested. */ -}; - -static enum nrf_cal_state cal_state; /* Calibration state. */ +static atomic_t cal_process_in_progress; static s16_t prev_temperature; /* Previous temperature measurement. */ static u8_t calib_skip_cnt; /* Counting down skipped calibrations. */ -static int total_cnt; /* Total number of calibrations. */ -static int total_skips_cnt; /* Total number of skipped calibrations. */ +static volatile int total_cnt; /* Total number of calibrations. */ +static volatile int total_skips_cnt; /* Total number of skipped calibrations. */ /* Callback called on hfclk started. */ static void cal_hf_on_callback(struct device *dev, @@ -51,6 +47,11 @@ static void cal_hf_on_callback(struct device *dev, static struct clock_control_async_data cal_hf_on_data = { .cb = cal_hf_on_callback }; +static void cal_lf_on_callback(struct device *dev, + clock_control_subsys_t subsys, void *user_data); +static struct clock_control_async_data cal_lf_on_data = { + .cb = cal_lf_on_callback +}; static struct device *clk_dev; static struct device *temp_sensor; @@ -58,102 +59,40 @@ static struct device *temp_sensor; static void measure_temperature(struct k_work *work); static K_WORK_DEFINE(temp_measure_work, measure_temperature); -static bool clock_event_check_and_clean(u32_t evt, u32_t intmask) +static void timeout_handler(struct k_timer *timer); +static K_TIMER_DEFINE(backoff_timer, timeout_handler, NULL); + +static void hf_request(void) { - bool ret = nrf_clock_event_check(NRF_CLOCK, evt) && - nrf_clock_int_enable_check(NRF_CLOCK, intmask); - - if (ret) { - nrf_clock_event_clear(NRF_CLOCK, evt); - } - - return ret; -} - -bool z_nrf_clock_calibration_start(struct device *dev) -{ - bool ret; - int key = irq_lock(); - - if (cal_state != CAL_ACTIVE_OFF) { - ret = true; - } else { - ret = false; - } - - cal_state = CAL_IDLE; - - irq_unlock(key); - - calib_skip_cnt = 0; - - return ret; -} - -void z_nrf_clock_calibration_lfclk_started(struct device *dev) -{ - /* Trigger unconditional calibration when lfclk is started. */ - cal_state = CAL_HFCLK_REQ; clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF, &cal_hf_on_data); } -bool z_nrf_clock_calibration_stop(struct device *dev) +static void hf_release(void) { - int key; - bool ret = true; - - key = irq_lock(); - - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTOP); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); - - /* If calibration is active then pend until completed. - * Currently (and most likely in the future), LFCLK is never stopped so - * it is not an issue. - */ - if (cal_state == CAL_ACTIVE) { - cal_state = CAL_ACTIVE_OFF; - ret = false; - } else { - cal_state = CAL_OFF; - } - - irq_unlock(key); - LOG_DBG("Stop requested %s.", (cal_state == CAL_ACTIVE_OFF) ? - "during ongoing calibration" : ""); - - return ret; + clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); } -void z_nrf_clock_calibration_init(struct device *dev) +static void lf_request(void) { - /* Anomaly 36: After watchdog timeout reset, CPU lockup reset, soft - * reset, or pin reset EVENTS_DONE and EVENTS_CTTO are not reset. - */ - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); - - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_DONE_MASK | - NRF_CLOCK_INT_CTTO_MASK | - NRF_CLOCK_INT_CTSTOPPED_MASK); - nrf_clock_cal_timer_timeout_set(NRF_CLOCK, - CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD); - - if (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP != 0) { - temp_sensor = device_get_binding(TEMP_SENSOR_NAME); - } - - clk_dev = dev; - total_cnt = 0; - total_skips_cnt = 0; + clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF, + &cal_lf_on_data); } -/* Start calibration assuming that HFCLK XTAL is on. */ -static void start_calibration(void) +static void lf_release(void) { - cal_state = CAL_ACTIVE; + clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); +} +static void cal_lf_on_callback(struct device *dev, + clock_control_subsys_t subsys, void *user_data) +{ + hf_request(); +} + +/* Start actual HW calibration assuming that HFCLK XTAL is on. */ +static void start_hw_cal(void) +{ /* Workaround for Errata 192 */ if (IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)) { *(volatile uint32_t *)0x40000C34 = 0x00000002; @@ -163,12 +102,59 @@ static void start_calibration(void) calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP; } -/* Restart calibration timer, release HFCLK XTAL. */ -static void to_idle(void) +/* Start cycle by starting backoff timer and releasing HFCLK XTAL. */ +static void start_cycle(void) { - cal_state = CAL_IDLE; - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTART); + k_timer_start(&backoff_timer, + K_MSEC(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD), 0); + hf_release(); + lf_release(); + cal_process_in_progress = 0; +} + +static void start_cal_process(void) +{ + if (atomic_cas(&cal_process_in_progress, 0, 1) == false) { + return; + } + + /* LF clock is probably running but it is requested to ensure that + * it is not released while calibration process in ongoing. If system + * releases the clock during calibration process it will be released + * at the end of calibration process and stopped in consequence. + */ + lf_request(); +} + +static void timeout_handler(struct k_timer *timer) +{ + start_cal_process(); +} + +/* Called when HFCLK XTAL is on. Schedules temperature measurement or triggers + * calibration. + */ +static void cal_hf_on_callback(struct device *dev, + clock_control_subsys_t subsys, void *user_data) +{ + if ((temp_sensor == NULL) || !IS_ENABLED(CONFIG_MULTITHREADING)) { + start_hw_cal(); + } else { + k_work_submit(&temp_measure_work); + } +} + +static void on_hw_cal_done(void) +{ + /* Workaround for Errata 192 */ + if (IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)) { + *(volatile uint32_t *)0x40000C34 = 0x00000000; + } + + total_cnt++; + LOG_DBG("Calibration done."); + + start_cycle(); } /* Convert sensor value to 0.25'C units. */ @@ -200,139 +186,84 @@ static int get_temperature(s16_t *tvp) static void measure_temperature(struct k_work *work) { s16_t temperature = 0; - s16_t diff; + s16_t diff = 0; bool started = false; - int key; int rc; rc = get_temperature(&temperature); - key = irq_lock(); - if (rc != 0) { - /* Temperature read failed: retry later */ - to_idle(); - goto out; + /* Temperature read failed, force calibration. */ + calib_skip_cnt = 0; + } else { + diff = abs(temperature - prev_temperature); } - diff = abs(temperature - prev_temperature); - - if (cal_state != CAL_OFF) { - if ((calib_skip_cnt == 0) || - (diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) { - prev_temperature = temperature; - start_calibration(); - started = true; - } else { - to_idle(); - calib_skip_cnt--; - total_skips_cnt++; - } + if ((calib_skip_cnt == 0) || + (diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) { + prev_temperature = temperature; + started = true; + start_hw_cal(); + } else { + calib_skip_cnt--; + total_skips_cnt++; + start_cycle(); } -out: - irq_unlock(key); - LOG_DBG("Calibration %s. Temperature diff: %d (in 0.25'C units).", started ? "started" : "skipped", diff); } -/* Called when HFCLK XTAL is on. Schedules temperature measurement or triggers - * calibration. - */ -static void cal_hf_on_callback(struct device *dev, - clock_control_subsys_t subsys, - void *user_data) +void z_nrf_clock_calibration_init(struct device *dev) { - int key = irq_lock(); + /* Anomaly 36: After watchdog timeout reset, CPU lockup reset, soft + * reset, or pin reset EVENTS_DONE and EVENTS_CTTO are not reset. + */ + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_DONE_MASK); - if (cal_state == CAL_HFCLK_REQ) { - if ((CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP == 0) || - (IS_ENABLED(CONFIG_MULTITHREADING) == false)) { - start_calibration(); - } else { - cal_state = CAL_TEMP_REQ; - k_work_submit(&temp_measure_work); - } - } else { - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); + if (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP != 0) { + temp_sensor = + device_get_binding(DT_INST_0_NORDIC_NRF_TEMP_LABEL); } - irq_unlock(key); + clk_dev = dev; + total_cnt = 0; + total_skips_cnt = 0; } -static void on_cal_done(void) +static void start_unconditional_cal_process(void) { - /* Workaround for Errata 192 */ - if (IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)) { - *(volatile uint32_t *)0x40000C34 = 0x00000000; - } - - total_cnt++; - LOG_DBG("Calibration done."); - - int key = irq_lock(); - - if (cal_state == CAL_ACTIVE_OFF) { - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTOP); - cal_state = CAL_OFF; - } else { - to_idle(); - } - - irq_unlock(key); + calib_skip_cnt = 0; + start_cal_process(); } void z_nrf_clock_calibration_force_start(void) { - int key = irq_lock(); - - calib_skip_cnt = 0; - - if (cal_state == CAL_IDLE) { - cal_state = CAL_HFCLK_REQ; - clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF, - &cal_hf_on_data); + /* if it's already in progress that is good enough. */ + if (cal_process_in_progress) { + return; } - irq_unlock(key); + start_unconditional_cal_process(); +} + +void z_nrf_clock_calibration_lfclk_started(void) +{ + start_unconditional_cal_process(); +} + +void z_nrf_clock_calibration_lfclk_stopped(void) +{ + k_timer_stop(&backoff_timer); + LOG_DBG("Calibration stopped"); } void z_nrf_clock_calibration_isr(void) { - if (clock_event_check_and_clean(NRF_CLOCK_EVENT_CTTO, - NRF_CLOCK_INT_CTTO_MASK)) { - LOG_DBG("Calibration timeout."); - - /* Start XTAL HFCLK. It is needed for temperature measurement - * and calibration. - */ - if (cal_state == CAL_IDLE) { - cal_state = CAL_HFCLK_REQ; - clock_control_async_on(clk_dev, - CLOCK_CONTROL_NRF_SUBSYS_HF, - &cal_hf_on_data); - } - } - - if (clock_event_check_and_clean(NRF_CLOCK_EVENT_DONE, - NRF_CLOCK_INT_DONE_MASK)) { - on_cal_done(); - } - - if ((NRF_CLOCK_INT_CTSTOPPED_MASK != 0) && - clock_event_check_and_clean(NRF_CLOCK_EVENT_CTSTOPPED, - NRF_CLOCK_INT_CTSTOPPED_MASK)) { - LOG_INF("CT stopped."); - if (cal_state == CAL_IDLE) { - /* If LF clock was restarted then CT might not be - * started because it was not yet stopped. - */ - LOG_INF("restarting"); - nrf_clock_task_trigger(NRF_CLOCK, - NRF_CLOCK_TASK_CTSTART); - } + if (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_DONE)) { + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); + on_hw_cal_done(); } } diff --git a/drivers/clock_control/nrf_clock_calibration.h b/drivers/clock_control/nrf_clock_calibration.h index 63e366170c1..d1f5d9c34db 100644 --- a/drivers/clock_control/nrf_clock_calibration.h +++ b/drivers/clock_control/nrf_clock_calibration.h @@ -26,39 +26,15 @@ void z_nrf_clock_calibration_init(struct device *hfclk_dev); */ void z_nrf_clock_calibration_isr(void); -/** - * @brief Start calibration. - * - * Function called when LFCLK RC clock is being started. - * - * @param dev LFCLK device. - * - * @retval true if clock can be started. - * @retval false if clock was not stopped due to ongoing calibration and don't - * need to be started again because it is still on. - */ -bool z_nrf_clock_calibration_start(struct device *dev); - /** * @brief Notify calibration module about LF clock start - * - * @param dev LFCLK device. */ -void z_nrf_clock_calibration_lfclk_started(struct device *dev); +void z_nrf_clock_calibration_lfclk_started(void); /** - * @brief Stop calibration. - * - * Function called when LFCLK RC clock is being stopped. - * - * @param dev LFCLK device. - * - * @retval true if clock can be stopped. - * @retval false if due to ongoing calibration clock cannot be stopped. In that - * case calibration module will stop clock when calibration is - * completed. + * @brief Notify calibration module about LF clock stop */ -bool z_nrf_clock_calibration_stop(struct device *dev); +void z_nrf_clock_calibration_lfclk_stopped(void); #ifdef __cplusplus diff --git a/drivers/clock_control/nrf_power_clock.c b/drivers/clock_control/nrf_power_clock.c index e410e7a3358..4f3b472c87c 100644 --- a/drivers/clock_control/nrf_power_clock.c +++ b/drivers/clock_control/nrf_power_clock.c @@ -30,11 +30,6 @@ LOG_MODULE_REGISTER(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL); #define INF(dev, subsys, ...) CLOCK_LOG(INF, dev, subsys, __VA_ARGS__) #define DBG(dev, subsys, ...) CLOCK_LOG(DBG, dev, subsys, __VA_ARGS__) -/* returns true if clock stopping or starting can be performed. If false then - * start/stop will be deferred and performed later on by handler owner. - */ -typedef bool (*nrf_clock_handler_t)(struct device *dev); - /* Clock subsys structure */ struct nrf_clock_control_sub_data { sys_slist_t list; /* List of users requesting callback */ @@ -44,8 +39,6 @@ struct nrf_clock_control_sub_data { /* Clock subsys static configuration */ struct nrf_clock_control_sub_config { - nrf_clock_handler_t start_handler; /* Called before start */ - nrf_clock_handler_t stop_handler; /* Called before stop */ nrf_clock_event_t started_evt; /* Clock started event */ nrf_clock_task_t start_tsk; /* Clock start task */ nrf_clock_task_t stop_tsk; /* Clock stop task */ @@ -161,25 +154,16 @@ static int clock_stop(struct device *dev, clock_control_subsys_t subsys) } data->ref--; if (data->ref == 0) { - bool do_stop; - DBG(dev, subsys, "Stopping"); sys_slist_init(&data->list); - do_stop = (config->stop_handler) ? - config->stop_handler(dev) : true; - - if (do_stop) { - nrf_clock_task_trigger(NRF_CLOCK, config->stop_tsk); - /* It may happen that clock is being stopped when it - * has just been started and start is not yet handled - * (due to irq_lock). In that case after stopping the - * clock, started event is cleared to prevent false - * interrupt being triggered. - */ - nrf_clock_event_clear(NRF_CLOCK, config->started_evt); + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) + && (subsys == CLOCK_CONTROL_NRF_SUBSYS_LF)) { + z_nrf_clock_calibration_lfclk_stopped(); } + nrf_clock_task_trigger(NRF_CLOCK, config->stop_tsk); + data->started = false; } @@ -286,31 +270,14 @@ static int clock_async_start(struct device *dev, } if (ref == 1) { - bool do_start; + DBG(dev, subsys, "Triggering start task"); - do_start = (config->start_handler) ? - config->start_handler(dev) : true; - if (do_start) { - DBG(dev, subsys, "Triggering start task"); - - if (IS_ENABLED(CONFIG_NRF52_ANOMALY_132_WORKAROUND) && - (subsys == CLOCK_CONTROL_NRF_SUBSYS_LF)) { - anomaly_132_workaround(); - } - - nrf_clock_task_trigger(NRF_CLOCK, - config->start_tsk); - } else { - /* If external start_handler indicated that clcok is - * still running (it may happen in case of LF RC clock - * which was requested to be stopped during ongoing - * calibration (clock will not be stopped in that case) - * and requested to be started before calibration is - * completed. In that case clock is still running and - * we can notify enlisted requests. - */ - clkstarted_handle(dev, type); + if (IS_ENABLED(CONFIG_NRF52_ANOMALY_132_WORKAROUND) && + (subsys == CLOCK_CONTROL_NRF_SUBSYS_LF)) { + anomaly_132_workaround(); } + + nrf_clock_task_trigger(NRF_CLOCK, config->start_tsk); } return 0; @@ -376,13 +343,6 @@ static const struct nrf_clock_control_config config = { .started_evt = NRF_CLOCK_EVENT_LFCLKSTARTED, .stop_tsk = NRF_CLOCK_TASK_LFCLKSTOP, IF_ENABLED(CONFIG_LOG, (.name = "lfclk",)) - IF_ENABLED( - CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION, - ( - .start_handler = z_nrf_clock_calibration_start, - .stop_handler = z_nrf_clock_calibration_stop, - ) - ) } } }; @@ -466,7 +426,7 @@ void nrf_power_clock_isr(void *arg) NRF_CLOCK_INT_LF_STARTED_MASK)) { if (IS_ENABLED( CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION)) { - z_nrf_clock_calibration_lfclk_started(dev); + z_nrf_clock_calibration_lfclk_started(); } clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_LFCLK); } diff --git a/tests/drivers/clock_control/nrf_clock_calibration/Kconfig b/tests/drivers/clock_control/nrf_clock_calibration/Kconfig new file mode 100644 index 00000000000..e454ad5dac9 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_NRF_USES_TEMP_SENSOR + bool "Internal" + help + Used internally to enable mocking of temperature sensor + +source "Kconfig.zephyr" diff --git a/tests/drivers/clock_control/nrf_clock_calibration/prj.conf b/tests/drivers/clock_control/nrf_clock_calibration/prj.conf index 45e9febc1d2..00618e58c37 100644 --- a/tests/drivers/clock_control/nrf_clock_calibration/prj.conf +++ b/tests/drivers/clock_control/nrf_clock_calibration/prj.conf @@ -1,6 +1,11 @@ CONFIG_ZTEST=y CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y -CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP=0 -CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD=1 +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP=1 +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF=2 +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD=150 CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG=y +#disable temperature sensor, mock is used +CONFIG_SENSOR=y +CONFIG_TEMP_NRF5=n +CONFIG_CLOCK_CONTROL_NRF_USES_TEMP_SENSOR=n diff --git a/tests/drivers/clock_control/nrf_clock_calibration/src/mock_temp_nrf5.c b/tests/drivers/clock_control/nrf_clock_calibration/src/mock_temp_nrf5.c new file mode 100644 index 00000000000..4b7a4cd97c0 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/src/mock_temp_nrf5.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +/* Mock of internal temperature sensore. */ +#ifdef CONFIG_TEMP_NRF5 +#error "Cannot be enabled because it is being mocked" +#endif + +static struct sensor_value value; + +void mock_temp_nrf5_value_set(struct sensor_value *val) +{ + value = *val; +} + +static int mock_temp_nrf5_init(struct device *dev) +{ + return 0; +} + +static int mock_temp_nrf5_sample_fetch(struct device *dev, + enum sensor_channel chan) +{ + k_sleep(K_MSEC(1)); + return 0; +} + +static int mock_temp_nrf5_channel_get(struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + *val = value; + return 0; +} + +static const struct sensor_driver_api mock_temp_nrf5_driver_api = { + .sample_fetch = mock_temp_nrf5_sample_fetch, + .channel_get = mock_temp_nrf5_channel_get, +}; + +DEVICE_AND_API_INIT(mock_temp_nrf5, + DT_INST_0_NORDIC_NRF_TEMP_LABEL, + mock_temp_nrf5_init, + NULL, + NULL, + POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, + &mock_temp_nrf5_driver_api); diff --git a/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c index a609ce1085b..4cc17f799fb 100644 --- a/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c +++ b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Nordic Semiconductor ASA + * Copyright (c) 2019-2020, Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,7 @@ #include #include #include +#include #include LOG_MODULE_REGISTER(test); @@ -14,9 +15,18 @@ LOG_MODULE_REGISTER(test); #error "LFCLK must use RC source" #endif -#if CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD != 1 -#error "Expected 250ms calibration period" -#endif +#define CALIBRATION_PROCESS_TIME_MS 35 + +extern void mock_temp_nrf5_value_set(struct sensor_value *val); + +static void turn_on_clock(struct device *dev, clock_control_subsys_t subsys) +{ + clock_control_on(dev, subsys); + while (clock_control_get_status(dev, subsys) != + CLOCK_CONTROL_STATUS_ON) { + + } +} static void turn_off_clock(struct device *dev, clock_control_subsys_t subsys) { @@ -27,255 +37,141 @@ static void turn_off_clock(struct device *dev, clock_control_subsys_t subsys) } while (err == 0); } -static void lfclk_started_cb(struct device *dev, - clock_control_subsys_t subsys, - void *user_data) -{ - *(bool *)user_data = true; -} +#define TEST_CALIBRATION(exp_cal, exp_skip, sleep_ms) \ + test_calibration(exp_cal, exp_skip, sleep_ms, __LINE__) -/* Test checks if calibration clock is running and generates interrupt as - * expected and starts calibration. Validates that HF clock is turned on - * for calibration and turned off once calibration is done. +/* Function tests if during given time expected number of calibrations and + * skips occurs. */ -static void test_clock_calibration(void) +static void test_calibration(u32_t exp_cal, u32_t exp_skip, + u32_t sleep_ms, u32_t line) { - struct device *clk_dev = - device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL); - volatile bool started = false; - struct clock_control_async_data lfclk_data = { - .cb = lfclk_started_cb, - .user_data = (void *)&started - }; - int key; - u32_t cnt = 0; - u32_t max_cnt = 1000; + int cal_cnt; + int skip_cnt; - turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); + cal_cnt = z_nrf_clock_calibration_count(); + skip_cnt = z_nrf_clock_calibration_skips_count(); - /* In case calibration needs to be completed. */ - k_busy_wait(100000); + k_sleep(K_MSEC(sleep_ms)); - clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF, - &lfclk_data); + cal_cnt = z_nrf_clock_calibration_count() - cal_cnt; + skip_cnt = z_nrf_clock_calibration_skips_count() - skip_cnt; - while (started == false) { - } - - k_busy_wait(35000); - - key = irq_lock(); - while (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO) == 0) { - k_busy_wait(1000); - cnt++; - if (cnt == max_cnt) { - irq_unlock(key); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - zassert_true(false, ""); - } - } - - zassert_within(cnt, 250, 10, "Expected 250ms period"); - - irq_unlock(key); - - while (clock_control_get_status(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF) - != CLOCK_CONTROL_STATUS_ON) { - } - - key = irq_lock(); - cnt = 0; - while (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_DONE) == 0) { - k_busy_wait(1000); - cnt++; - if (cnt == max_cnt) { - irq_unlock(key); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - zassert_true(false, ""); - } - } - - irq_unlock(key); - - zassert_equal(clock_control_get_status( - clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF), - CLOCK_CONTROL_STATUS_OFF, - "Expected hfclk off after calibration."); - - key = irq_lock(); - cnt = 0; - - while (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO) == 0) { - k_busy_wait(1000); - cnt++; - if (cnt == max_cnt) { - irq_unlock(key); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - zassert_true(false, ""); - } - } - - zassert_within(cnt, 250, 10, "Expected 250ms period (got %d)", cnt); - - irq_unlock(key); - - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); + zassert_equal(cal_cnt, exp_cal, + "%d: Unexpected number of calibrations (%d, exp:%d)", + line, cal_cnt, exp_cal); + zassert_equal(skip_cnt, exp_skip, + "%d: Unexpected number of skips (%d, exp:%d)", + line, skip_cnt, exp_skip); } -/* Test checks that when calibration is active then LF clock is not stopped. - * Stopping is deferred until calibration is done. Test validates that after - * completing calibration LF clock is shut down. +/* Function pends until calibration counter is performed. When function leaves, + * it is just after calibration. */ -static void test_stopping_when_calibration(void) +static void sync_just_after_calibration(void) +{ + u32_t cal_cnt = z_nrf_clock_calibration_count(); + + /* wait until calibration is performed. */ + while (z_nrf_clock_calibration_count() == cal_cnt) { + k_sleep(K_MSEC(1)); + } +} + +/* Test checks if calibration and calibration skips are performed according + * to timing configuration. + */ +static void test_basic_clock_calibration(void) +{ + int wait_ms = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * + (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + + CALIBRATION_PROCESS_TIME_MS; + struct sensor_value value = { .val1 = 0, .val2 = 0 }; + + mock_temp_nrf5_value_set(&value); + sync_just_after_calibration(); + + TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, + wait_ms); +} + +/* Test checks if calibration happens just after clock is enabled. */ +static void test_calibration_after_enabling_lfclk(void) { struct device *clk_dev = device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL); - volatile bool started = false; - struct clock_control_async_data lfclk_data = { - .cb = lfclk_started_cb, - .user_data = (void *)&started - }; - int key; - u32_t cnt = 0; - u32_t max_cnt = 1000; + struct sensor_value value = { .val1 = 0, .val2 = 0 }; + + mock_temp_nrf5_value_set(&value); turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); - /* In case calibration needs to be completed. */ - k_busy_wait(100000); + k_busy_wait(10000); - clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF, - &lfclk_data); + turn_on_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - while (started == false) { - } - - /* when lfclk is started first calibration is performed. Wait until it - * is done. - */ - k_busy_wait(35000); - - key = irq_lock(); - while (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO) == 0) { - k_busy_wait(1000); - cnt++; - if (cnt == max_cnt) { - irq_unlock(key); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - zassert_true(false, ""); - } - } - - zassert_within(cnt, 250, 10, "Expected 250ms period"); - - irq_unlock(key); - - while (clock_control_get_status(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF) - != CLOCK_CONTROL_STATUS_ON) { - } - - /* calibration started */ - key = irq_lock(); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - - zassert_true(nrf_clock_lf_is_running(NRF_CLOCK), - "Expected LF still on"); - - while (nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_DONE) == 0) { - k_busy_wait(1000); - cnt++; - if (cnt == max_cnt) { - irq_unlock(key); - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - zassert_true(false, ""); - } - } - - irq_unlock(key); - - /* wait some time after which clock should be off. */ - k_busy_wait(300); - - zassert_false(nrf_clock_lf_is_running(NRF_CLOCK), "Expected LF off"); - - clock_control_off(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); + TEST_CALIBRATION(1, 0, + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD); } -static u32_t pend_on_next_calibration(void) +/* Test checks if temperature change triggers calibration. */ +static void test_temp_change_triggers_calibration(void) { - u32_t stamp = k_uptime_get_32(); - int cnt = z_nrf_clock_calibration_count(); + struct sensor_value value = { .val1 = 0, .val2 = 0 }; - while (cnt == z_nrf_clock_calibration_count()) { - if ((k_uptime_get_32() - stamp) > 300) { - zassert_true(false, "Expected calibration"); - return UINT32_MAX; - } - } + mock_temp_nrf5_value_set(&value); + sync_just_after_calibration(); - return k_uptime_get_32() - stamp; + /* change temperature by 0.25'C which should not trigger calibration */ + value.val2 += ((CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF - 1) * + 250000); + mock_temp_nrf5_value_set(&value); + + /* expected one skip */ + TEST_CALIBRATION(0, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP * + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + + CALIBRATION_PROCESS_TIME_MS); + + TEST_CALIBRATION(1, 0, + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + 40); + + value.val2 += (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF * 250000); + mock_temp_nrf5_value_set(&value); + + /* expect calibration due to temp change. */ + TEST_CALIBRATION(1, 0, + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + 40); } -static void test_clock_calibration_force(void) +/* Test checks if z_nrf_clock_calibration_force_start() results in immediate + * calibration. + */ +static void test_force_calibration(void) { - struct device *clk_dev = - device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL); - volatile bool started = false; - struct clock_control_async_data lfclk_data = { - .cb = lfclk_started_cb, - .user_data = (void *)&started - }; - u32_t cnt = 0; - u32_t period; + sync_just_after_calibration(); - turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF); - turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_HF); - - /* In case calibration needs to be completed. */ - k_busy_wait(100000); - - clock_control_async_on(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF, - &lfclk_data); - - while (started == false) { - } - - cnt = z_nrf_clock_calibration_count(); - - pend_on_next_calibration(); - - zassert_equal(cnt + 1, z_nrf_clock_calibration_count(), - "Unexpected number of calibrations %d (expected: %d)", - z_nrf_clock_calibration_count(), cnt + 1); - - /* Next calibration should happen in 250ms, after forcing it should be - * done sooner. - */ z_nrf_clock_calibration_force_start(); - period = pend_on_next_calibration(); - zassert_equal(cnt + 2, z_nrf_clock_calibration_count(), - "Unexpected number of calibrations"); - zassert_true(period < 100, "Unexpected calibration period."); - k_busy_wait(80000); + /*expect immediate calibration */ + TEST_CALIBRATION(1, 0, + CALIBRATION_PROCESS_TIME_MS + 5); - /* Next calibration should happen as scheduled. */ - period = pend_on_next_calibration(); - zassert_equal(cnt + 3, z_nrf_clock_calibration_count(), - "Unexpected number of calibrations"); - zassert_true(period < 200, - "Unexpected calibration period %d ms.", period); + /* and back to scheduled operation. */ + TEST_CALIBRATION(1, CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP, + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD * + (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + 1) + + CALIBRATION_PROCESS_TIME_MS); } void test_main(void) { ztest_test_suite(test_nrf_clock_calibration, - ztest_unit_test(test_clock_calibration), - ztest_unit_test(test_stopping_when_calibration), - ztest_unit_test(test_clock_calibration_force) + ztest_unit_test(test_basic_clock_calibration), + ztest_unit_test(test_calibration_after_enabling_lfclk), + ztest_unit_test(test_temp_change_triggers_calibration), + ztest_unit_test(test_force_calibration) ); ztest_run_test_suite(test_nrf_clock_calibration); }