blob: 29ae25b9a4e86c89bfcc157c2d621ae6f0acc472 [file] [log] [blame]
/*
* Copyright (c) 2019-2020, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <hal/nrf_clock.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(test);
#ifndef CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC
#error "LFCLK must use RC source"
#endif
#define CALIBRATION_PROCESS_TIME_MS 35
extern void mock_temp_nrf5_value_set(struct sensor_value *val);
static void turn_on_clock(const struct device *dev,
clock_control_subsys_t subsys)
{
int err;
int res;
struct onoff_client cli;
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(subsys);
sys_notify_init_spinwait(&cli.notify);
err = onoff_request(mgr, &cli);
if (err < 0) {
zassert_false(true, "Failed to start clock");
}
while (sys_notify_fetch_result(&cli.notify, &res) != 0) {
}
}
static void turn_off_clock(const struct device *dev,
clock_control_subsys_t subsys)
{
int err;
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(subsys);
do {
err = onoff_release(mgr);
} while (err >= 0);
while (clock_control_get_status(dev, subsys) !=
CLOCK_CONTROL_STATUS_OFF) {
}
}
#define TEST_CALIBRATION(exp_cal, exp_skip, sleep_ms) \
test_calibration(exp_cal, exp_skip, sleep_ms, __LINE__)
/* Function tests if during given time expected number of calibrations and
* skips occurs.
*/
static void test_calibration(uint32_t exp_cal, uint32_t exp_skip,
uint32_t sleep_ms, uint32_t line)
{
int cal_cnt;
int skip_cnt;
cal_cnt = z_nrf_clock_calibration_count();
skip_cnt = z_nrf_clock_calibration_skips_count();
k_sleep(K_MSEC(sleep_ms));
cal_cnt = z_nrf_clock_calibration_count() - cal_cnt;
skip_cnt = z_nrf_clock_calibration_skips_count() - skip_cnt;
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);
}
/* Function pends until calibration counter is performed. When function leaves,
* it is just after calibration.
*/
static void sync_just_after_calibration(void)
{
uint32_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.
*/
ZTEST(nrf_clock_calibration, test_basic_clock_calibration)
{
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. */
ZTEST(nrf_clock_calibration, test_calibration_after_enabling_lfclk)
{
if (IS_ENABLED(CONFIG_SOC_NRF52832)) {
/* On nrf52832 LF clock cannot be stopped because it leads
* to RTC COUNTER register reset and that is unexpected by
* system clock which is disrupted and may hang in the test.
*/
ztest_test_skip();
}
const struct device *const clk_dev = DEVICE_DT_GET_ONE(nordic_nrf_clock);
struct sensor_value value = { .val1 = 0, .val2 = 0 };
zassert_true(device_is_ready(clk_dev), "Device is not ready");
mock_temp_nrf5_value_set(&value);
turn_off_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF);
k_busy_wait(10000);
turn_on_clock(clk_dev, CLOCK_CONTROL_NRF_SUBSYS_LF);
TEST_CALIBRATION(1, 0,
CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD);
}
/* Test checks if temperature change triggers calibration. */
ZTEST(nrf_clock_calibration, test_temp_change_triggers_calibration)
{
struct sensor_value value = { .val1 = 0, .val2 = 0 };
mock_temp_nrf5_value_set(&value);
sync_just_after_calibration();
/* 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);
}
/* Test checks if z_nrf_clock_calibration_force_start() results in immediate
* calibration.
*/
ZTEST(nrf_clock_calibration, test_force_calibration)
{
sync_just_after_calibration();
z_nrf_clock_calibration_force_start();
/*expect immediate calibration */
TEST_CALIBRATION(1, 0,
CALIBRATION_PROCESS_TIME_MS + 5);
/* 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);
}
ZTEST_SUITE(nrf_clock_calibration, NULL, NULL, NULL, NULL, NULL);