blob: 934c77826e425d7c981108e278895653b82b6b9f [file] [log] [blame]
/*
* Copyright (c) 2019, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <clock_control.h>
#include <drivers/clock_control/nrf_clock_control.h>
#include <hal/nrf_clock.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(test);
#ifndef CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC
#error "LFCLK must use RC source"
#endif
#if CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD != 1
#error "Expected 250ms calibration period"
#endif
static void turn_off_clock(struct device *dev)
{
int err;
do {
err = clock_control_off(dev, 0);
} while (err == 0);
}
static void lfclk_started_cb(struct device *dev, void *user_data)
{
*(bool *)user_data = true;
}
/* 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.
*/
static void test_clock_calibration(void)
{
struct device *hfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M");
struct device *lfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K");
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;
turn_off_clock(hfclk_dev);
turn_off_clock(lfclk_dev);
/* In case calibration needs to be completed. */
k_busy_wait(100000);
clock_control_async_on(lfclk_dev, NULL, &lfclk_data);
while (started == false) {
}
k_busy_wait(35000);
key = irq_lock();
while (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO) == 0) {
k_busy_wait(1000);
cnt++;
if (cnt == max_cnt) {
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
zassert_true(false, "");
}
}
zassert_within(cnt, 250, 10, "Expected 250ms period");
irq_unlock(key);
while (clock_control_get_status(hfclk_dev, NULL)
!= CLOCK_CONTROL_STATUS_ON) {
}
key = irq_lock();
cnt = 0;
while (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE) == 0) {
k_busy_wait(1000);
cnt++;
if (cnt == max_cnt) {
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
zassert_true(false, "");
}
}
irq_unlock(key);
zassert_equal(clock_control_get_status(hfclk_dev, NULL),
CLOCK_CONTROL_STATUS_OFF,
"Expected hfclk off after calibration.");
key = irq_lock();
cnt = 0;
while (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO) == 0) {
k_busy_wait(1000);
cnt++;
if (cnt == max_cnt) {
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
zassert_true(false, "");
}
}
zassert_within(cnt, 250, 10, "Expected 250ms period (got %d)", cnt);
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
}
/* 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.
*/
static void test_stopping_when_calibration(void)
{
struct device *hfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M");
struct device *lfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K");
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;
turn_off_clock(hfclk_dev);
turn_off_clock(lfclk_dev);
/* In case calibration needs to be completed. */
k_busy_wait(100000);
clock_control_async_on(lfclk_dev, NULL, &lfclk_data);
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_EVENT_CTTO) == 0) {
k_busy_wait(1000);
cnt++;
if (cnt == max_cnt) {
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
zassert_true(false, "");
}
}
zassert_within(cnt, 250, 10, "Expected 250ms period");
irq_unlock(key);
while (clock_control_get_status(hfclk_dev, NULL)
!= CLOCK_CONTROL_STATUS_ON) {
}
/* calibration started */
key = irq_lock();
clock_control_off(lfclk_dev, NULL);
zassert_true(nrf_clock_lf_is_running(), "Expected LF still on");
while (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE) == 0) {
k_busy_wait(1000);
cnt++;
if (cnt == max_cnt) {
irq_unlock(key);
clock_control_off(lfclk_dev, NULL);
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(), "Expected LF off");
clock_control_off(lfclk_dev, NULL);
}
static u32_t pend_on_next_calibration(void)
{
u32_t stamp = k_uptime_get_32();
int cnt = z_nrf_clock_calibration_count();
while (cnt == z_nrf_clock_calibration_count()) {
if ((k_uptime_get_32() - stamp) > 300) {
zassert_true(false, "Expected calibration");
return UINT32_MAX;
}
}
return k_uptime_get_32() - stamp;
}
static void test_clock_calibration_force(void)
{
struct device *hfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M");
struct device *lfclk_dev =
device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K");
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;
turn_off_clock(hfclk_dev);
turn_off_clock(lfclk_dev);
/* In case calibration needs to be completed. */
k_busy_wait(100000);
clock_control_async_on(lfclk_dev, NULL, &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);
/* 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);
}
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_run_test_suite(test_nrf_clock_calibration);
}