Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2019-2020, Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <ztest.h>
#include <drivers/entropy.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/nrf_clock_control.h>
#include <hal/nrf_clock.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(test);

#define TEST_TIME_MS 10000

#define HF_STARTUP_TIME_US 400

static bool test_end;

#include <hal/nrf_gpio.h>

static struct onoff_manager *hf_mgr;
static uint32_t iteration;

static void setup(void)
{
	hf_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
	zassert_true(hf_mgr, NULL);

	iteration = 0;
}

static void teardown(void)
{
	/* empty */
}

static void bt_timeout_handler(struct k_timer *timer)
{
	static bool on;

	nrf_gpio_cfg_output(27);
	if (on) {
		on = false;
		nrf_gpio_pin_clear(27);
		z_nrf_clock_bt_ctlr_hf_release();
	} else {
		nrf_gpio_pin_set(27);
		on = true;
		z_nrf_clock_bt_ctlr_hf_request();
	}

	if (!(test_end && !on)) {
		k_timeout_t timeout;
		static bool long_timeout;

		if (!on) {
			timeout = Z_TIMEOUT_US(200);
		} else {
			timeout = Z_TIMEOUT_US(long_timeout ? 300 : 100);
			long_timeout = !long_timeout;
		}
		k_timer_start(timer, timeout, K_NO_WAIT);
	}
}

K_TIMER_DEFINE(timer1, bt_timeout_handler, NULL);

static void check_hf_status(const struct device *dev, bool exp_on,
			    bool sw_check)
{
	nrf_clock_hfclk_t type;

	nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_HFCLK, &type);
	zassert_equal(type, exp_on ? NRF_CLOCK_HFCLK_HIGH_ACCURACY :
				NRF_CLOCK_HFCLK_LOW_ACCURACY,
			"%d: Clock expected to be %s",
			iteration, exp_on ? "on" : "off");

	if (sw_check) {
		enum clock_control_status status =
		     clock_control_get_status(dev, CLOCK_CONTROL_NRF_SUBSYS_HF);

		zassert_equal(status, exp_on ? CLOCK_CONTROL_STATUS_ON :
						CLOCK_CONTROL_STATUS_OFF,
				"%d: Unexpected status: %d", iteration, status);
	}

}

/* Test controls HF clock from two contexts: thread and timer interrupt.
 * In thread context clock is requested and released through standard onoff
 * API and in the timeout handler it is requested and released using API
 * dedicated to be used by Bluetooth Controller.
 *
 * Test runs in the loop to eventually lead to cases when clock controlling is
 * preempted by timeout handler. At certain points clock status is validated.
 */
static void test_onoff_interrupted(void)
{
	const struct device *clock_dev =
		device_get_binding(DT_LABEL(DT_INST(0, nordic_nrf_clock)));
	const struct device *entropy =
		device_get_binding(DT_CHOSEN_ZEPHYR_ENTROPY_LABEL);
	struct onoff_client cli;
	uint64_t start_time = k_uptime_get();
	uint64_t elapsed;
	uint64_t checkpoint = 1000;
	int err;
	uint8_t rand;
	int backoff;

	k_timer_start(&timer1, K_MSEC(1), K_NO_WAIT);

	while (1) {
		iteration++;

		err = entropy_get_entropy(entropy, &rand, 1);
		zassert_equal(err, 0, NULL);
		backoff = 3 * rand;

		sys_notify_init_spinwait(&cli.notify);
		err = onoff_request(hf_mgr, &cli);
		zassert_true(err >= 0, NULL);

		k_busy_wait(backoff);

		if (backoff > HF_STARTUP_TIME_US) {
			check_hf_status(clock_dev, true, true);
		}

		err = onoff_cancel_or_release(hf_mgr, &cli);
		zassert_true(err >= 0, NULL);

		elapsed = k_uptime_get() - start_time;
		if (elapsed > checkpoint) {
			printk("test continues\n");
			checkpoint += 1000;
		}

		if (elapsed > TEST_TIME_MS) {
			test_end = true;
			break;
		}
	}

	k_msleep(100);
	check_hf_status(clock_dev, false, true);
}

static void onoff_timeout_handler(struct k_timer *timer)
{
	static bool on;
	static struct onoff_client cli;
	static uint32_t cnt;
	int err;

	cnt++;
	if (on) {
		on = false;
		err = onoff_cancel_or_release(hf_mgr, &cli);
		zassert_true(err >= 0, NULL);
	} else {
		on = true;
		sys_notify_init_spinwait(&cli.notify);
		err = onoff_request(hf_mgr, &cli);
		zassert_true(err >= 0, "%d: Unexpected err: %d", cnt, err);
	}

	if (!(test_end && !on)) {
		k_timeout_t timeout;
		static bool long_timeout;

		if (!on) {
			timeout = Z_TIMEOUT_US(200);
		} else {
			timeout = Z_TIMEOUT_US(long_timeout ? 300 : 100);
			long_timeout = !long_timeout;
		}
		k_timer_start(timer, timeout, K_NO_WAIT);
	}
}

K_TIMER_DEFINE(timer2, onoff_timeout_handler, NULL);

/* Test controls HF clock from two contexts: thread and timer interrupt.
 * In thread context clock is requested and released through API
 * dedicated to be used by Bluetooth Controller and in the timeout handler it is
 * requested and released using standard onoffAPI .
 *
 * Test runs in the loop to eventually lead to cases when clock controlling is
 * preempted by timeout handler. At certain points clock status is validated.
 */
static void test_bt_interrupted(void)
{
	const struct device *clock_dev =
		device_get_binding(DT_LABEL(DT_INST(0, nordic_nrf_clock)));
	const struct device *entropy =
		device_get_binding(DT_CHOSEN_ZEPHYR_ENTROPY_LABEL);
	uint64_t start_time = k_uptime_get();
	uint64_t elapsed;
	uint64_t checkpoint = 1000;
	int err;
	uint8_t rand;
	int backoff;

	k_timer_start(&timer2, K_MSEC(1), K_NO_WAIT);

	while (1) {
		iteration++;

		err = entropy_get_entropy(entropy, &rand, 1);
		zassert_equal(err, 0, NULL);
		backoff = 3 * rand;

		z_nrf_clock_bt_ctlr_hf_request();

		k_busy_wait(backoff);

		if (backoff > HF_STARTUP_TIME_US) {
			check_hf_status(clock_dev, true, false);
		}

		z_nrf_clock_bt_ctlr_hf_release();

		elapsed = k_uptime_get() - start_time;
		if (elapsed > checkpoint) {
			printk("test continues\n");
			checkpoint += 1000;
		}

		if (elapsed > TEST_TIME_MS) {
			test_end = true;
			break;
		}
	}

	k_msleep(100);
	check_hf_status(clock_dev, false, true);
}

void test_main(void)
{
	ztest_test_suite(test_nrf_onoff_and_bt,
		ztest_unit_test_setup_teardown(test_onoff_interrupted,
					       setup, teardown),
		ztest_unit_test_setup_teardown(test_bt_interrupted,
					       setup, teardown)
			);
	ztest_run_test_suite(test_nrf_onoff_and_bt);
}