blob: 56c96464837d43433ae633d057b8e7dcc1228ef2 [file] [log] [blame]
/*
* Copyright (c) 2019, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <clock_control.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(test);
#ifdef DT_INST_0_NORDIC_NRF_CLOCK_LABEL
#include <drivers/clock_control/nrf_clock_control.h>
#endif
struct device_data {
const char *name;
u32_t startup_us;
};
static const struct device_data devices[] = {
#ifdef DT_INST_0_NORDIC_NRF_CLOCK_LABEL
{
.name = DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M",
.startup_us = IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? 3000 : 400
},
{
.name = DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K",
.startup_us = (CLOCK_CONTROL_NRF_K32SRC == NRF_CLOCK_LFCLK_RC) ?
1000 : 300000
}
#endif
};
typedef void (*test_func_t)(const char *dev_name, u32_t startup_us);
typedef bool (*test_capability_check_t)(const char *dev_name);
static void setup_instance(const char *dev_name)
{
struct device *dev = device_get_binding(dev_name);
int err;
k_busy_wait(1000);
do {
err = clock_control_off(dev, 0);
} while (err == 0);
LOG_INF("setup done");
}
static void tear_down_instance(const char *dev_name)
{
struct device *dev = device_get_binding(dev_name);
clock_control_on(dev, NULL);
}
static void test_all_instances(test_func_t func,
test_capability_check_t capability_check)
{
for (int i = 0; i < ARRAY_SIZE(devices); i++) {
if ((capability_check == NULL) ||
capability_check(devices[i].name)) {
setup_instance(devices[i].name);
func(devices[i].name, devices[i].startup_us);
tear_down_instance(devices[i].name);
/* Allow logs to be printed. */
k_sleep(K_MSEC(100));
}
}
}
/*
* Basic test for checking correctness of getting clock status.
*/
static void test_on_off_status_instance(const char *dev_name, u32_t startup_us)
{
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, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_on(dev, NULL);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
status = clock_control_get_status(dev, NULL);
zassert_true((status == CLOCK_CONTROL_STATUS_STARTING) ||
(status == CLOCK_CONTROL_STATUS_ON),
"%s: Unexpected status (%d)", dev_name, status);
k_busy_wait(startup_us);
status = clock_control_get_status(dev, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_ON, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_off(dev, 0);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
status = clock_control_get_status(dev, NULL);
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);
}
/*
* Test validates that if number of enabling requests matches disabling requests
* then clock is disabled.
*/
static void test_multiple_users_instance(const char *dev_name, u32_t startup_us)
{
struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int users = 5;
int err;
status = clock_control_get_status(dev, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
for (int i = 0; i < users; i++) {
err = clock_control_on(dev, NULL);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
}
status = clock_control_get_status(dev, NULL);
zassert_true((status == CLOCK_CONTROL_STATUS_STARTING) ||
(status == CLOCK_CONTROL_STATUS_ON),
"%s: Unexpected status (%d)", dev_name, status);
for (int i = 0; i < users; i++) {
err = clock_control_off(dev, 0);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
}
status = clock_control_get_status(dev, NULL);
zassert_true(status == CLOCK_CONTROL_STATUS_OFF,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_off(dev, 0);
zassert_equal(-EALREADY, err, "%s: Unexpected err (%d)", dev_name, err);
}
static void test_multiple_users(void)
{
test_all_instances(test_multiple_users_instance, NULL);
}
static bool async_capable(const char *dev_name)
{
struct device *dev = device_get_binding(dev_name);
if (clock_control_async_on(dev, 0, NULL) != 0) {
return false;
}
clock_control_off(dev, 0);
return true;
}
/*
* Test checks that callbacks are called after clock is started.
*/
static void clock_on_callback(struct device *dev, void *user_data)
{
bool *executed = (bool *)user_data;
*executed = true;
}
static void test_async_on_instance(const char *dev_name, u32_t startup_us)
{
struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
bool executed1 = false;
bool executed2 = false;
struct clock_control_async_data data1 = {
.cb = clock_on_callback,
.user_data = &executed1
};
struct clock_control_async_data data2 = {
.cb = clock_on_callback,
.user_data = &executed2
};
status = clock_control_get_status(dev, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_async_on(dev, 0, &data1);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
err = clock_control_async_on(dev, 0, &data2);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
/* wait for clock started. */
k_busy_wait(startup_us);
zassert_true(executed1, "%s: Expected flag to be true", dev_name);
zassert_true(executed2, "%s: Expected flag to be true", dev_name);
}
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.
*/
static void test_async_on_stopped_on_instance(const char *dev_name,
u32_t startup_us)
{
struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
int key;
bool executed1 = false;
struct clock_control_async_data data1 = {
.cb = clock_on_callback,
.user_data = &executed1
};
status = clock_control_get_status(dev, NULL);
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, 0, &data1);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
err = clock_control_off(dev, 0);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
irq_unlock(key);
k_busy_wait(10000);
zassert_false(executed1, "%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 when asynchronous clock enabling is called when clock is
* running then callback is immediate.
*/
static void test_immediate_cb_when_clock_on_on_instance(const char *dev_name,
u32_t startup_us)
{
struct device *dev = device_get_binding(dev_name);
enum clock_control_status status;
int err;
bool executed1 = false;
struct clock_control_async_data data1 = {
.cb = clock_on_callback,
.user_data = &executed1
};
status = clock_control_get_status(dev, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_OFF, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_on(dev, NULL);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
/* wait for clock started. */
k_busy_wait(startup_us);
status = clock_control_get_status(dev, NULL);
zassert_equal(CLOCK_CONTROL_STATUS_ON, status,
"%s: Unexpected status (%d)", dev_name, status);
err = clock_control_async_on(dev, 0, &data1);
zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err);
zassert_true(executed1, "%s: Expected flag to be false", dev_name);
}
static void test_immediate_cb_when_clock_on(void)
{
test_all_instances(test_immediate_cb_when_clock_on_on_instance,
async_capable);
}
void test_main(void)
{
ztest_test_suite(test_clock_control,
ztest_unit_test(test_on_off_status),
ztest_unit_test(test_multiple_users),
ztest_unit_test(test_async_on),
ztest_unit_test(test_async_on_stopped),
ztest_unit_test(test_immediate_cb_when_clock_on)
);
ztest_run_test_suite(test_clock_control);
}