blob: 1be6a371e14cc7dab207891a2b9647f7d614f422 [file] [log] [blame]
/*
* Copyright (c) 2019, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <drivers/clock_control.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(test);
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock)
#include <drivers/clock_control/nrf_clock_control.h>
#endif
struct device_subsys_data {
clock_control_subsys_t subsys;
uint32_t startup_us;
};
struct device_data {
const char *name;
const struct device_subsys_data *subsys_data;
uint32_t subsys_cnt;
};
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock)
static const struct device_subsys_data subsys_data[] = {
{
.subsys = CLOCK_CONTROL_NRF_SUBSYS_HF,
.startup_us =
IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ?
3000 : 500
},
#ifndef 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.
*/
{
.subsys = CLOCK_CONTROL_NRF_SUBSYS_LF,
.startup_us = (CLOCK_CONTROL_NRF_K32SRC ==
NRF_CLOCK_LFCLK_RC) ? 1000 : 500000
}
#endif /* !CONFIG_SOC_NRF52832 */
};
#endif /* DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock) */
static const struct device_data devices[] = {
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock)
{
.name = DT_LABEL(DT_INST(0, nordic_nrf_clock)),
.subsys_data = subsys_data,
.subsys_cnt = ARRAY_SIZE(subsys_data)
}
#endif
};
typedef void (*test_func_t)(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us);
typedef bool (*test_capability_check_t)(const char *dev_name,
clock_control_subsys_t subsys);
static void setup_instance(const char *dev_name, clock_control_subsys_t subsys)
{
const struct device *dev = device_get_binding(dev_name);
int err;
k_busy_wait(1000);
do {
err = clock_control_off(dev, subsys);
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock)
if (err == -EPERM) {
struct onoff_manager *mgr =
z_nrf_clock_control_get_onoff(subsys);
err = onoff_release(mgr);
if (err >= 0) {
break;
}
}
#endif
} while (clock_control_get_status(dev, subsys) !=
CLOCK_CONTROL_STATUS_OFF);
LOG_INF("setup done");
}
static void tear_down_instance(const char *dev_name,
clock_control_subsys_t subsys)
{
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock)
/* Turn on LF clock using onoff service if it is disabled. */
const struct device *clk =
device_get_binding(DT_LABEL(DT_INST(0, nordic_nrf_clock)));
struct onoff_client cli;
struct onoff_manager *mgr =
z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
int err;
if (clock_control_get_status(clk, CLOCK_CONTROL_NRF_SUBSYS_LF) !=
CLOCK_CONTROL_STATUS_OFF) {
return;
}
sys_notify_init_spinwait(&cli.notify);
err = onoff_request(mgr, &cli);
zassert_true(err >= 0, "");
while (sys_notify_fetch_result(&cli.notify, &err) < 0) {
/*empty*/
}
zassert_true(err >= 0, "");
#endif
}
static void test_with_single_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_time,
test_func_t func,
test_capability_check_t capability_check)
{
setup_instance(dev_name, subsys);
if ((capability_check == NULL) || capability_check(dev_name, subsys)) {
func(dev_name, subsys, startup_time);
} else {
PRINT("test skipped for subsys:%d\n", (int)subsys);
}
tear_down_instance(dev_name, subsys);
/* Allow logs to be printed. */
k_sleep(K_MSEC(100));
}
static void test_all_instances(test_func_t func,
test_capability_check_t capability_check)
{
for (size_t i = 0; i < ARRAY_SIZE(devices); i++) {
for (size_t j = 0; j < devices[i].subsys_cnt; j++) {
test_with_single_instance(devices[i].name,
devices[i].subsys_data[j].subsys,
devices[i].subsys_data[j].startup_us,
func, capability_check);
}
}
}
/*
* Basic test for checking correctness of getting clock status.
*/
static void test_on_off_status_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us)
{
const struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
zassert_true(dev != NULL, "%s: Unknown device", dev_name);
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_on(dev, subsys);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
status = clock_control_get_status(dev, subsys);
zassert_equal(status, CLOCK_CONTROL_STATUS_ON,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_off(dev, subsys);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
}
static void test_on_off_status(void)
{
test_all_instances(test_on_off_status_instance, NULL);
}
static void async_capable_callback(const struct device *dev,
clock_control_subsys_t subsys,
void *user_data)
{
/* empty */
}
/* Function checks if clock supports asynchronous starting. */
static bool async_capable(const char *dev_name, clock_control_subsys_t subsys)
{
const struct device *dev = device_get_binding(dev_name);
int err;
err = clock_control_async_on(dev, subsys, async_capable_callback, NULL);
if (err < 0) {
printk("failed %d", err);
return false;
}
while (clock_control_get_status(dev, subsys) !=
CLOCK_CONTROL_STATUS_ON) {
/* pend util clock is started */
}
err = clock_control_off(dev, subsys);
if (err < 0) {
printk("clock_control_off failed %d", err);
return false;
}
return true;
}
/*
* Test checks that callbacks are called after clock is started.
*/
static void clock_on_callback(const struct device *dev,
clock_control_subsys_t subsys,
void *user_data)
{
bool *executed = (bool *)user_data;
*executed = true;
}
static void test_async_on_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us)
{
const struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
bool executed = false;
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_async_on(dev, subsys, clock_on_callback, &executed);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
/* wait for clock started. */
k_busy_wait(startup_us);
zassert_true(executed, "%s: Expected flag to be true", dev_name);
zassert_equal(CLOCK_CONTROL_STATUS_ON,
clock_control_get_status(dev, subsys),
"Unexpected clock status");
}
static void test_async_on(void)
{
test_all_instances(test_async_on_instance, async_capable);
}
/*
* Test checks that when asynchronous clock enabling is scheduled but clock
* is disabled before being started then callback is never called and error
* is reported.
*/
static void test_async_on_stopped_on_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us)
{
const struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
int key;
bool executed = false;
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
/* lock to prevent clock interrupt for fast starting clocks.*/
key = irq_lock();
err = clock_control_async_on(dev, subsys, clock_on_callback, &executed);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
/* Attempt to stop clock while it is being started. */
err = clock_control_off(dev, subsys);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
irq_unlock(key);
k_busy_wait(10000);
zassert_false(executed, "%s: Expected flag to be false", dev_name);
}
static void test_async_on_stopped(void)
{
test_all_instances(test_async_on_stopped_on_instance, async_capable);
}
/*
* Test checks that that second start returns error.
*/
static void test_double_start_on_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us)
{
const struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_on(dev, subsys);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
err = clock_control_on(dev, subsys);
zassert_true(err < 0, "%s: Unexpected return value:%d", dev_name, err);
}
static void test_double_start(void)
{
test_all_instances(test_double_start_on_instance, NULL);
}
/*
* Test checks that that second stop returns 0.
* Test precondition: clock is stopped.
*/
static void test_double_stop_on_instance(const char *dev_name,
clock_control_subsys_t subsys,
uint32_t startup_us)
{
const struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
status = clock_control_get_status(dev, subsys);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_off(dev, subsys);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
}
static void test_double_stop(void)
{
test_all_instances(test_double_stop_on_instance, NULL);
}
void test_main(void)
{
ztest_test_suite(test_clock_control,
ztest_unit_test(test_on_off_status),
ztest_unit_test(test_async_on),
ztest_unit_test(test_async_on_stopped),
ztest_unit_test(test_double_start),
ztest_unit_test(test_double_stop)
);
ztest_run_test_suite(test_clock_control);
}