diff --git a/tests/drivers/counter/counter_basic_api/CMakeLists.txt b/tests/drivers/counter/counter_basic_api/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..00a6a90b7a9fb86690112a858bdd7dbb5078a60e --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(counter_basic_api) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.conf b/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.conf new file mode 100644 index 0000000000000000000000000000000000000000..c6c3d33e1c7f8abfb9d0814b69cc156ebe8152c6 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.conf @@ -0,0 +1,2 @@ +CONFIG_COUNTER_CSK6_GPTCHANNEL=y +# CONFIG_COUNTER_CSK6_DUAL=y diff --git a/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.overlay b/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.overlay new file mode 100644 index 0000000000000000000000000000000000000000..1d9f1149d248b4c5c568fc4b1d074c10d5914ca2 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/csk6011a_nano.overlay @@ -0,0 +1,49 @@ +&gpt0 { + gptchannel0:gptchannel0 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel1:gptchannel1 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel2:gptchannel2 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel3:gptchannel3 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel4:gptchannel4 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel5:gptchannel5 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel6:gptchannel6 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + + gptchannel7:gptchannel7 { + compatible = "listenai,csk-gptchannel"; + status = "okay"; + }; + +}; + +&dual_timer{ + compatible = "listenai,csk-dual"; + reg = <0x44100000 0x1000000>; + interrupts = <6 0>; + label = "DUL_TIM"; +}; diff --git a/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.conf b/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.conf new file mode 100644 index 0000000000000000000000000000000000000000..ed8462a3be515fffadc54a1ad7afc02f57ab0931 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.conf @@ -0,0 +1 @@ +CONFIG_COUNTER_CSK6_GPTCHANNEL=y diff --git a/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.overlay b/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.overlay new file mode 100644 index 0000000000000000000000000000000000000000..e392e4fe29b03d342cb5f0638314d49e9705e36d --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/csk6012_nano.overlay @@ -0,0 +1,66 @@ +&gpt0 { + gptchannel0:gptchannel0 { + status = "okay"; + + pwm0 { + status = "disabled"; + }; + }; + + gptchannel1:gptchannel1 { + status = "okay"; + + pwm1 { + status = "disabled"; + }; + }; + + gptchannel2:gptchannel2 { + status = "okay"; + + pwm2 { + status = "disabled"; + }; + }; + + gptchannel3:gptchannel3 { + status = "okay"; + + pwm3 { + status = "disabled"; + }; + }; + + gptchannel4:gptchannel4 { + status = "okay"; + + pwm4 { + status = "disabled"; + }; + }; + + gptchannel5:gptchannel5 { + status = "okay"; + + pwm5 { + status = "disabled"; + }; + }; + + gptchannel6:gptchannel6 { + status = "okay"; + + pwm6 { + status = "disabled"; + }; + }; + + gptchannel7:gptchannel7 { + status = "okay"; + + pwm7 { + status = "disabled"; + }; + }; + +}; diff --git a/tests/drivers/counter/counter_basic_api/prj.conf b/tests/drivers/counter/counter_basic_api/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..c244ce13cd753fd6a8276b5f4dde6fed627d3f21 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/prj.conf @@ -0,0 +1,6 @@ +CONFIG_COUNTER=y +CONFIG_BT=n +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y +CONFIG_LOG=y +CONFIG_COUNTER_LOG_LEVEL_DBG=y \ No newline at end of file diff --git a/tests/drivers/counter/counter_basic_api/src/test_counter.c b/tests/drivers/counter/counter_basic_api/src/test_counter.c new file mode 100644 index 0000000000000000000000000000000000000000..250473ce65ebfed3bf7af5429d35c38daadbbb5c --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/src/test_counter.c @@ -0,0 +1,1077 @@ +/* + * Copyright (c) 2018, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/drivers/counter.h> +#include <ztest.h> +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(test); + +static struct k_sem top_cnt_sem; +static volatile uint32_t top_cnt; +static struct k_sem alarm_cnt_sem; +static volatile uint32_t alarm_cnt; + +static void top_handler(const struct device *dev, void *user_data); + +void *exp_user_data = (void *)199; + +#if defined(CONFIG_COUNTER_MCUX_RTC) || defined(CONFIG_COUNTER_RTC_STM32) || \ + defined(CONFIG_COUNTER_MCUX_LPC_RTC) +#define COUNTER_PERIOD_US_VAL (USEC_PER_SEC * 2U) +#else +#define COUNTER_PERIOD_US_VAL 20000 +#endif + +struct counter_alarm_cfg alarm_cfg; +struct counter_alarm_cfg alarm_cfg2; + +#define INST_DT_COMPAT_LABEL(n, compat) DT_LABEL(DT_INST(n, compat)) +/* Generate a list of LABELs for all instances of the "compat" */ +#define LABELS_FOR_DT_COMPAT(compat) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \ + (LISTIFY(DT_NUM_INST_STATUS_OKAY(compat), \ + INST_DT_COMPAT_LABEL, (,), compat),), ()) + +static const char * const devices[] = { + +#ifdef CONFIG_COUNTER_CSK6_DUAL + DT_LABEL(DT_NODELABEL(dual_timer)), +#endif +#ifdef CONFIG_COUNTER_CSK6_GPTCHANNEL + DT_LABEL(DT_NODELABEL(gptchannel0)), + DT_LABEL(DT_NODELABEL(gptchannel1)), + DT_LABEL(DT_NODELABEL(gptchannel2)), + DT_LABEL(DT_NODELABEL(gptchannel3)), + DT_LABEL(DT_NODELABEL(gptchannel4)), + DT_LABEL(DT_NODELABEL(gptchannel5)), + DT_LABEL(DT_NODELABEL(gptchannel6)), + DT_LABEL(DT_NODELABEL(gptchannel7)), +#endif +#ifdef CONFIG_COUNTER_TIMER0 + /* Nordic TIMER0 may be reserved for Bluetooth */ + DT_LABEL(DT_NODELABEL(timer0)), +#endif +#ifdef CONFIG_COUNTER_TIMER1 + DT_LABEL(DT_NODELABEL(timer1)), +#endif +#ifdef CONFIG_COUNTER_TIMER2 + DT_LABEL(DT_NODELABEL(timer2)), +#endif +#ifdef CONFIG_COUNTER_TIMER3 + DT_LABEL(DT_NODELABEL(timer3)), +#endif +#ifdef CONFIG_COUNTER_TIMER4 + DT_LABEL(DT_NODELABEL(timer4)), +#endif +#ifdef CONFIG_COUNTER_RTC0 + /* Nordic RTC0 may be reserved for Bluetooth */ + DT_LABEL(DT_NODELABEL(rtc0)), +#endif + /* Nordic RTC1 is used for the system clock */ +#ifdef CONFIG_COUNTER_RTC2 + DT_LABEL(DT_NODELABEL(rtc2)), +#endif +#ifdef CONFIG_COUNTER_TIMER_STM32 +#define STM32_COUNTER_LABEL(idx) \ + DT_LABEL(DT_INST(idx, st_stm32_counter)), +#define DT_DRV_COMPAT st_stm32_counter + DT_INST_FOREACH_STATUS_OKAY(STM32_COUNTER_LABEL) +#undef DT_DRV_COMPAT +#undef STM32_COUNTER_LABEL +#endif +#ifdef CONFIG_COUNTER_NATIVE_POSIX + DT_LABEL(DT_NODELABEL(counter0)), +#endif + /* NOTE: there is no trailing comma, as the DT_LABELS_FOR_COMPAT + * handles it. + */ + LABELS_FOR_DT_COMPAT(arm_cmsdk_timer) + LABELS_FOR_DT_COMPAT(arm_cmsdk_dtimer) + LABELS_FOR_DT_COMPAT(microchip_xec_timer) + LABELS_FOR_DT_COMPAT(nxp_imx_epit) + LABELS_FOR_DT_COMPAT(nxp_imx_gpt) +#ifdef CONFIG_COUNTER_MCUX_CTIMER + LABELS_FOR_DT_COMPAT(nxp_lpc_ctimer) +#endif +#ifdef CONFIG_COUNTER_MCUX_RTC + LABELS_FOR_DT_COMPAT(nxp_kinetis_rtc) +#endif +#ifdef CONFIG_COUNTER_MCUX_QTMR + LABELS_FOR_DT_COMPAT(nxp_imx_tmr) +#endif +#ifdef CONFIG_COUNTER_MCUX_LPC_RTC + LABELS_FOR_DT_COMPAT(nxp_lpc_rtc) +#endif + LABELS_FOR_DT_COMPAT(silabs_gecko_rtcc) + LABELS_FOR_DT_COMPAT(st_stm32_rtc) +#ifdef CONFIG_COUNTER_MCUX_PIT + LABELS_FOR_DT_COMPAT(nxp_kinetis_pit) +#endif +#ifdef CONFIG_COUNTER_XLNX_AXI_TIMER + LABELS_FOR_DT_COMPAT(xlnx_xps_timer_1_00_a) +#endif +}; + +typedef void (*counter_test_func_t)(const char *dev_name); + +typedef bool (*counter_capability_func_t)(const char *dev_name); + + +static void counter_setup_instance(const char *dev_name) +{ + k_sem_reset(&alarm_cnt_sem); + if (!k_is_user_context()) { + alarm_cnt = 0; + } +} + +static void counter_tear_down_instance(const char *dev_name) +{ + int err; + const struct device *dev; + struct counter_top_cfg top_cfg = { + .callback = NULL, + .user_data = NULL, + .flags = 0 + }; + + dev = device_get_binding(dev_name); + + top_cfg.ticks = counter_get_max_top_value(dev); + err = counter_set_top_value(dev, &top_cfg); + if (err == -ENOTSUP) { + /* If resetting is not support, attempt without reset. */ + top_cfg.flags = COUNTER_TOP_CFG_DONT_RESET; + err = counter_set_top_value(dev, &top_cfg); + + } + zassert_true((err == 0) || (err == -ENOTSUP), + "%s: Setting top value to default failed", dev_name); + + err = counter_stop(dev); + zassert_equal(0, err, "%s: Counter failed to stop", dev_name); + +} + +static void test_all_instances(counter_test_func_t func, + counter_capability_func_t capability_check) +{ + for (int i = 0; i < ARRAY_SIZE(devices); i++) { + counter_setup_instance(devices[i]); + if ((capability_check == NULL) || + capability_check(devices[i])) { + TC_PRINT("Testing %s\n", devices[i]); + func(devices[i]); + } else { + TC_PRINT("Skipped for %s\n", devices[i]); + } + counter_tear_down_instance(devices[i]); + /* Allow logs to be printed. */ + k_sleep(K_MSEC(100)); + } +} + +static bool set_top_value_capable(const char *dev_name) +{ + const struct device *dev = device_get_binding(dev_name); + struct counter_top_cfg cfg = { + .ticks = counter_get_top_value(dev) - 1 + }; + int err; + + err = counter_set_top_value(dev, &cfg); + if (err == -ENOTSUP) { + return false; + } + + cfg.ticks++; + err = counter_set_top_value(dev, &cfg); + if (err == -ENOTSUP) { + return false; + } + + return true; +} + +static void top_handler(const struct device *dev, void *user_data) +{ + zassert_true(user_data == exp_user_data, + "%s: Unexpected callback", dev->name); + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + top_cnt++; + + return; + } + + k_sem_give(&top_cnt_sem); +} + +void test_set_top_value_with_alarm_instance(const char *dev_name) +{ + const struct device *dev; + int err; + uint32_t cnt; + uint32_t top_value; + uint32_t counter_period_us; + uint32_t top_handler_cnt; + struct counter_top_cfg top_cfg = { + .callback = top_handler, + .user_data = exp_user_data, + .flags = 0 + }; + + k_sem_reset(&top_cnt_sem); + top_cnt = 0; + + dev = device_get_binding(dev_name); + if (strcmp(dev_name, "RTC_0") == 0) { + counter_period_us = COUNTER_PERIOD_US_VAL; + } else { + /* if more counter drivers exist other than RTC, + the test value set to 20000 by default */ + counter_period_us = 20000; + } + top_cfg.ticks = counter_us_to_ticks(dev, counter_period_us); + err = counter_start(dev); + zassert_equal(0, err, "%s: Counter failed to start", dev_name); + + k_busy_wait(5000); + + err = counter_get_value(dev, &cnt); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", dev_name, + err); + if (counter_is_counting_up(dev)) { + err = (cnt > 0) ? 0 : 1; + } else { + top_value = counter_get_top_value(dev); + err = (cnt < top_value) ? 0 : 1; + } + zassert_true(err == 0, "%s: Counter should progress", dev_name); + + err = counter_set_top_value(dev, &top_cfg); + zassert_equal(0, err, "%s: Counter failed to set top value (err: %d)", + dev_name, err); + + k_busy_wait(5.2*counter_period_us); + + top_handler_cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + top_cnt : k_sem_count_get(&top_cnt_sem); + zassert_true(top_handler_cnt == 5U, + "%s: Unexpected number of turnarounds (%d).", + dev_name, top_handler_cnt); +} + +void test_set_top_value_with_alarm(void) +{ + test_all_instances(test_set_top_value_with_alarm_instance, + set_top_value_capable); +} + +void test_set_top_value_without_alarm_instance(const char *dev_name) +{ + const struct device *dev; + int err; + uint32_t cnt; + uint32_t top_value; + uint32_t counter_period_us; + struct counter_top_cfg top_cfg = { + .callback = NULL, + .user_data = NULL, + .flags = 0 + }; + + if (strcmp(dev_name, "RTC_0") == 0) { + counter_period_us = COUNTER_PERIOD_US_VAL; + } else { + /* if more counter drivers exist other than RTC, + the test value set to 20000 by default */ + counter_period_us = 20000; + } + dev = device_get_binding(dev_name); + top_cfg.ticks = counter_us_to_ticks(dev, counter_period_us); + err = counter_start(dev); + zassert_equal(0, err, "%s: Counter failed to start", dev_name); + + k_busy_wait(5000); + + err = counter_get_value(dev, &cnt); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", dev_name, + err); + if (counter_is_counting_up(dev)) { + err = (cnt > 0) ? 0 : 1; + } else { + top_value = counter_get_top_value(dev); + err = (cnt < top_value) ? 0 : 1; + } + zassert_true(err == 0, "%s: Counter should progress", dev_name); + + err = counter_set_top_value(dev, &top_cfg); + zassert_equal(0, err, "%s: Counter failed to set top value (err: %d)", + dev_name, err); + + zassert_true(counter_get_top_value(dev) == top_cfg.ticks, + "%s: new top value not in use.", + dev_name); +} + +void test_set_top_value_without_alarm(void) +{ + test_all_instances(test_set_top_value_without_alarm_instance, + set_top_value_capable); +} + +static void alarm_handler(const struct device *dev, uint8_t chan_id, + uint32_t counter, + void *user_data) +{ + /* Arbitrary limit for alarm processing - time between hw expiration + * and read-out from counter in the handler. + */ + static const uint64_t processing_limit_us = 1000; + uint32_t now; + int err; + uint32_t top; + uint32_t diff; + + err = counter_get_value(dev, &now); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", + dev->name, err); + + top = counter_get_top_value(dev); + if (counter_is_counting_up(dev)) { + diff = (now < counter) ? + (now + top - counter) : (now - counter); + } else { + diff = (now > counter) ? + (counter + top - now) : (counter - now); + } + + zassert_true(diff <= counter_us_to_ticks(dev, processing_limit_us), + "Unexpected distance between reported alarm value(%u) " + "and actual counter value (%u), top:%d (processing " + "time limit (%d us) might be exceeded?", + counter, now, top, processing_limit_us); + + if (user_data) { + zassert_true(&alarm_cfg == user_data, + "%s: Unexpected callback", dev->name); + } + + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + alarm_cnt++; + return; + } + zassert_true(k_is_in_isr(), "%s: Expected interrupt context", + dev->name); + k_sem_give(&alarm_cnt_sem); +} + +void test_single_shot_alarm_instance(const char *dev_name, bool set_top) +{ + const struct device *dev; + int err; + uint32_t ticks; + uint32_t cnt; + uint32_t counter_period_us; + struct counter_top_cfg top_cfg = { + .callback = top_handler, + .user_data = exp_user_data, + .flags = 0 + }; + + if (strcmp(dev_name, "RTC_0") == 0) { + counter_period_us = COUNTER_PERIOD_US_VAL; + } else { + /* if more counter drivers exist other than RTC, + the test value set to 20000 by default */ + counter_period_us = 20000; + } + dev = device_get_binding(dev_name); + ticks = counter_us_to_ticks(dev, counter_period_us); + top_cfg.ticks = ticks; + + alarm_cfg.flags = 0; + alarm_cfg.callback = alarm_handler; + alarm_cfg.user_data = &alarm_cfg; + + k_sem_reset(&alarm_cnt_sem); + alarm_cnt = 0; + + if (counter_get_num_of_channels(dev) < 1U) { + /* Counter does not support any alarm */ + return; + } + + err = counter_start(dev); + zassert_equal(0, err, "%s: Counter failed to start", dev_name); + + if (set_top) { + err = counter_set_top_value(dev, &top_cfg); + + zassert_equal(0, err, + "%s: Counter failed to set top value", dev_name); + + alarm_cfg.ticks = ticks + 1; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(-EINVAL, err, + "%s: Counter should return error because ticks" + " exceeded the limit set alarm", dev_name); + alarm_cfg.ticks = ticks - 1; + } + + alarm_cfg.ticks = ticks; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(0, err, "%s: Counter set alarm failed (err: %d)", + dev_name, err); + + k_busy_wait(2*(uint32_t)counter_ticks_to_us(dev, ticks)); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(1, cnt, "%s: Expecting alarm callback", dev_name); + + k_busy_wait(1.5*counter_ticks_to_us(dev, ticks)); + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(1, cnt, "%s: Expecting alarm callback", dev_name); + + err = counter_cancel_channel_alarm(dev, 0); + zassert_equal(0, err, "%s: Counter disabling alarm failed", dev_name); + + top_cfg.ticks = counter_get_max_top_value(dev); + top_cfg.callback = NULL; + top_cfg.user_data = NULL; + err = counter_set_top_value(dev, &top_cfg); + if (err == -ENOTSUP) { + /* If resetting is not support, attempt without reset. */ + top_cfg.flags = COUNTER_TOP_CFG_DONT_RESET; + err = counter_set_top_value(dev, &top_cfg); + + } + zassert_true((err == 0) || (err == -ENOTSUP), + "%s: Setting top value to default failed", dev_name); + + err = counter_stop(dev); + zassert_equal(0, err, "%s: Counter failed to stop", dev_name); +} + +void test_single_shot_alarm_notop_instance(const char *dev_name) +{ + test_single_shot_alarm_instance(dev_name, false); +} + +void test_single_shot_alarm_top_instance(const char *dev_name) +{ + test_single_shot_alarm_instance(dev_name, true); +} + +static bool single_channel_alarm_capable(const char *dev_name) +{ + const struct device *dev = device_get_binding(dev_name); + + return (counter_get_num_of_channels(dev) > 0); +} + +static bool single_channel_alarm_and_custom_top_capable(const char *dev_name) +{ + return single_channel_alarm_capable(dev_name) && + set_top_value_capable(dev_name); +} + +void test_single_shot_alarm_notop(void) +{ + test_all_instances(test_single_shot_alarm_notop_instance, + single_channel_alarm_capable); +} + +void test_single_shot_alarm_top(void) +{ + test_all_instances(test_single_shot_alarm_top_instance, + single_channel_alarm_and_custom_top_capable); +} + +static void *clbk_data[10]; + +static void alarm_handler2(const struct device *dev, uint8_t chan_id, + uint32_t counter, + void *user_data) +{ + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + clbk_data[alarm_cnt] = user_data; + alarm_cnt++; + + return; + } + + clbk_data[k_sem_count_get(&alarm_cnt_sem)] = user_data; + k_sem_give(&alarm_cnt_sem); +} + +/* + * Two alarms set. First alarm is absolute, second relative. Because + * setting of both alarms is delayed it is expected that second alarm + * will expire first (relative to the time called) while first alarm + * will expire after next wrap around. + */ +void test_multiple_alarms_instance(const char *dev_name) +{ + const struct device *dev; + int err; + uint32_t ticks; + uint32_t cnt; + uint32_t counter_period_us; + struct counter_top_cfg top_cfg = { + .callback = top_handler, + .user_data = exp_user_data, + .flags = 0 + }; + + if (strcmp(dev_name, "RTC_0") == 0) { + counter_period_us = COUNTER_PERIOD_US_VAL; + } else { + /* if more counter drivers exist other than RTC, + the test value set to 20000 by default */ + counter_period_us = 20000; + } + dev = device_get_binding(dev_name); + ticks = counter_us_to_ticks(dev, counter_period_us); + + err = counter_get_value(dev, &(top_cfg.ticks)); + zassert_equal(0, err, "%s: Counter get value failed", dev_name); + top_cfg.ticks += ticks; + + alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE; + alarm_cfg.ticks = counter_us_to_ticks(dev, 2000); + alarm_cfg.callback = alarm_handler2; + alarm_cfg.user_data = &alarm_cfg; + + alarm_cfg2.flags = 0; + alarm_cfg2.ticks = counter_us_to_ticks(dev, 2000); + alarm_cfg2.callback = alarm_handler2; + alarm_cfg2.user_data = &alarm_cfg2; + + k_sem_reset(&alarm_cnt_sem); + alarm_cnt = 0; + + if (counter_get_num_of_channels(dev) < 2U) { + /* Counter does not support two alarms */ + return; + } + + err = counter_start(dev); + zassert_equal(0, err, "%s: Counter failed to start", dev_name); + + if (set_top_value_capable(dev_name)) { + err = counter_set_top_value(dev, &top_cfg); + zassert_equal(0, err, "%s: Counter failed to set top value", dev_name); + } else { + /* Counter does not support top value, do not run this test + * as it might take a long time to wrap and trigger the alarm + * resulting in test failures. + */ + return; + } + + k_busy_wait(3*(uint32_t)counter_ticks_to_us(dev, alarm_cfg.ticks)); + + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(0, err, "%s: Counter set alarm failed", dev_name); + + err = counter_set_channel_alarm(dev, 1, &alarm_cfg2); + zassert_equal(0, err, "%s: Counter set alarm failed", dev_name); + + k_busy_wait(1.2 * counter_ticks_to_us(dev, ticks * 2U)); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(2, cnt, + "%s: Invalid number of callbacks %d (expected: %d)", + dev_name, cnt, 2); + + zassert_equal(&alarm_cfg2, clbk_data[0], + "%s: Expected different order or callbacks", + dev_name); + zassert_equal(&alarm_cfg, clbk_data[1], + "%s: Expected different order or callbacks", + dev_name); + + /* tear down */ + err = counter_cancel_channel_alarm(dev, 0); + zassert_equal(0, err, "%s: Counter disabling alarm failed", dev_name); + + err = counter_cancel_channel_alarm(dev, 1); + zassert_equal(0, err, "%s: Counter disabling alarm failed", dev_name); +} + +static bool multiple_channel_alarm_capable(const char *dev_name) +{ + const struct device *dev = device_get_binding(dev_name); + + return (counter_get_num_of_channels(dev) > 1); +} + +void test_multiple_alarms(void) +{ + test_all_instances(test_multiple_alarms_instance, + multiple_channel_alarm_capable); +} + +void test_all_channels_instance(const char *dev_name) +{ + const struct device *dev; + int err; + const int n = 10; + int nchan = 0; + bool limit_reached = false; + struct counter_alarm_cfg alarm_cfgs; + uint32_t ticks; + uint32_t cnt; + uint32_t counter_period_us; + + if (strcmp(dev_name, "RTC_0") == 0) { + counter_period_us = COUNTER_PERIOD_US_VAL; + } else { + counter_period_us = 20000; + } + dev = device_get_binding(dev_name); + ticks = counter_us_to_ticks(dev, counter_period_us); + + alarm_cfgs.flags = 0; + alarm_cfgs.ticks = ticks; + alarm_cfgs.callback = alarm_handler2; + alarm_cfgs.user_data = NULL; + + err = counter_start(dev); + zassert_equal(0, err, "%s: Counter failed to start", dev_name); + + for (int i = 0; i < n; i++) { + err = counter_set_channel_alarm(dev, i, &alarm_cfgs); + if ((err == 0) && !limit_reached) { + nchan++; + } else if (err == -ENOTSUP) { + limit_reached = true; + } else { + zassert_equal(0, 1, + "%s: Unexpected error on setting alarm", dev_name); + } + } + + k_busy_wait(1.5*counter_ticks_to_us(dev, ticks)); + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(nchan, cnt, + "%s: Expecting alarm callback", dev_name); + + for (int i = 0; i < nchan; i++) { + err = counter_cancel_channel_alarm(dev, i); + zassert_equal(0, err, + "%s: Unexpected error on disabling alarm", dev_name); + } + + for (int i = nchan; i < n; i++) { + err = counter_cancel_channel_alarm(dev, i); + zassert_equal(-ENOTSUP, err, + "%s: Unexpected error on disabling alarm", dev_name); + } +} + +void test_all_channels(void) +{ + test_all_instances(test_all_channels_instance, + single_channel_alarm_capable); +} + +/** + * Test validates if alarm set too late (current tick or current tick + 1) + * results in callback being called. + */ +void test_late_alarm_instance(const char *dev_name) +{ + int err; + uint32_t cnt; + const struct device *dev = device_get_binding(dev_name); + uint32_t tick_us = (uint32_t)counter_ticks_to_us(dev, 1); + uint32_t guard = counter_us_to_ticks(dev, 200); + struct counter_alarm_cfg alarm_cfg = { + .callback = alarm_handler, + .flags = COUNTER_ALARM_CFG_ABSOLUTE | + COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE, + .user_data = NULL + }; + + err = counter_set_guard_period(dev, guard, + COUNTER_GUARD_PERIOD_LATE_TO_SET); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + err = counter_start(dev); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + k_busy_wait(2*tick_us); + + alarm_cfg.ticks = 0; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(-ETIME, err, "%s: Unexpected error (%d)", dev_name, err); + + /* wait couple of ticks */ + k_busy_wait(5*tick_us); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(1, cnt, + "%s: Expected %d callbacks, got %d\n", + dev_name, 1, cnt); + + err = counter_get_value(dev, &(alarm_cfg.ticks)); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", dev_name, + err); + + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(-ETIME, err, "%s: Failed to set an alarm (err: %d)", + dev_name, err); + + /* wait to ensure that tick+1 timeout will expire. */ + k_busy_wait(3*tick_us); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(2, cnt, + "%s: Expected %d callbacks, got %d\n", + dev_name, 2, cnt); +} + +void test_late_alarm_error_instance(const char *dev_name) +{ + int err; + const struct device *dev = device_get_binding(dev_name); + uint32_t tick_us = (uint32_t)counter_ticks_to_us(dev, 1); + uint32_t guard = counter_us_to_ticks(dev, 200); + struct counter_alarm_cfg alarm_cfg = { + .callback = alarm_handler, + .flags = COUNTER_ALARM_CFG_ABSOLUTE, + .user_data = NULL + }; + + err = counter_set_guard_period(dev, guard, + COUNTER_GUARD_PERIOD_LATE_TO_SET); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + err = counter_start(dev); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + k_busy_wait(2*tick_us); + + alarm_cfg.ticks = 0; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(-ETIME, err, + "%s: Failed to detect late setting (err: %d)", + dev_name, err); + + err = counter_get_value(dev, &(alarm_cfg.ticks)); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", dev_name, + err); + + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(-ETIME, err, + "%s: Counter failed to detect late setting (err: %d)", + dev_name, err); +} + +static bool late_detection_capable(const char *dev_name) +{ + const struct device *dev = device_get_binding(dev_name); + uint32_t guard = counter_get_guard_period(dev, + COUNTER_GUARD_PERIOD_LATE_TO_SET); + int err = counter_set_guard_period(dev, guard, + COUNTER_GUARD_PERIOD_LATE_TO_SET); + + if (err == -ENOTSUP) { + return false; + } + + if (single_channel_alarm_capable(dev_name) == false) { + return false; + } + + return true; +} + +void test_late_alarm(void) +{ + test_all_instances(test_late_alarm_instance, late_detection_capable); +} + +void test_late_alarm_error(void) +{ + test_all_instances(test_late_alarm_error_instance, + late_detection_capable); +} + +static void test_short_relative_alarm_instance(const char *dev_name) +{ + int err; + uint32_t cnt; + const struct device *dev = device_get_binding(dev_name); + uint32_t tick_us = (uint32_t)counter_ticks_to_us(dev, 1); + struct counter_alarm_cfg alarm_cfg = { + .callback = alarm_handler, + .flags = 0, + .user_data = NULL + }; + + /* for timers with very short ticks, counter_ticks_to_us() returns 0 */ + tick_us = tick_us == 0 ? 1 : tick_us; + + err = counter_start(dev); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + alarm_cfg.ticks = 1; + + for (int i = 0; i < 100; ++i) { + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(0, err, + "%s: Failed to set an alarm (err: %d)", + dev_name, err); + + /* wait to ensure that tick+1 timeout will expire. */ + k_busy_wait(3*tick_us); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(i + 1, cnt, + "%s: Expected %d callbacks, got %d\n", + dev_name, i + 1, cnt); + } +} + +/* Function checks if relative alarm set for 1 tick will expire. If handler is + * not called within near future it indicates that driver do not support it and + * more extensive testing is skipped. + */ +static bool short_relative_capable(const char *dev_name) +{ + const struct device *dev = device_get_binding(dev_name); + struct counter_alarm_cfg alarm_cfg = { + .callback = alarm_handler, + .flags = 0, + .user_data = NULL, + .ticks = 1 + }; + int err; + uint32_t cnt; + bool ret; + + if (single_channel_alarm_capable(dev_name) == false) { + return false; + } + + err = counter_start(dev); + if (err != 0) { + ret = false; + goto end; + } + + k_sem_reset(&alarm_cnt_sem); + alarm_cnt = 0; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + if (err != 0) { + ret = false; + goto end; + } + + k_busy_wait(counter_ticks_to_us(dev, 10)); + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + if (cnt == 1) { + ret = true; + } else { + ret = false; + (void)counter_cancel_channel_alarm(dev, 0); + } + +end: + k_sem_reset(&alarm_cnt_sem); + alarm_cnt = 0; + counter_stop(dev); + k_busy_wait(1000); + + return ret; +} + +static void test_short_relative_alarm(void) +{ + test_all_instances(test_short_relative_alarm_instance, + short_relative_capable); +} + +/* Test checks if cancelled alarm does not get triggered when new alarm is + * configured at the point where previous alarm was about to expire. + */ +static void test_cancelled_alarm_does_not_expire_instance(const char *dev_name) +{ + int err; + uint32_t cnt; + const struct device *dev = device_get_binding(dev_name); + uint32_t us = 1000; + uint32_t ticks = counter_us_to_ticks(dev, us); + uint32_t top = counter_get_top_value(dev); + + us = (uint32_t)counter_ticks_to_us(dev, ticks); + + struct counter_alarm_cfg alarm_cfg = { + .callback = alarm_handler, + .flags = COUNTER_ALARM_CFG_ABSOLUTE, + .user_data = NULL + }; + + err = counter_start(dev); + zassert_equal(0, err, "%s: Unexpected error", dev_name); + + + for (int i = 0; i < us/2; ++i) { + err = counter_get_value(dev, &(alarm_cfg.ticks)); + zassert_true(err == 0, "%s: Counter read failed (err: %d)", + dev_name, err); + + alarm_cfg.ticks += ticks; + alarm_cfg.ticks = alarm_cfg.ticks % top; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(0, err, "%s: Failed to set an alarm (err: %d)", + dev_name, err); + + err = counter_cancel_channel_alarm(dev, 0); + zassert_equal(0, err, "%s: Failed to cancel an alarm (err: %d)", + dev_name, err); + + k_busy_wait(us/2 + i); + + alarm_cfg.ticks = alarm_cfg.ticks + 2*ticks; + alarm_cfg.ticks = alarm_cfg.ticks % top; + err = counter_set_channel_alarm(dev, 0, &alarm_cfg); + zassert_equal(0, err, "%s: Failed to set an alarm (err: %d)", + dev_name, err); + + /* wait to ensure that tick+1 timeout will expire. */ + k_busy_wait(us); + + err = counter_cancel_channel_alarm(dev, 0); + zassert_equal(0, err, "%s: Failed to cancel an alarm (err: %d)", + dev_name, err); + + cnt = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) ? + alarm_cnt : k_sem_count_get(&alarm_cnt_sem); + zassert_equal(0, cnt, + "%s: Expected %d callbacks, got %d (i:%d)\n", + dev_name, 0, cnt, i); + } +} + +static bool reliable_cancel_capable(const char *dev_name) +{ + /* Test performed only for NRF_RTC instances. Other probably will fail. + */ +#ifdef CONFIG_COUNTER_RTC0 + /* Nordic RTC0 may be reserved for Bluetooth */ + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(rtc0))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_RTC2 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(rtc2))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_TIMER0 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer0))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_TIMER1 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer1))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_TIMER2 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer2))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_TIMER3 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer3))) == 0) { + return true; + } +#endif + +#ifdef CONFIG_COUNTER_TIMER4 + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer4))) == 0) { + return true; + } +#endif +#ifdef CONFIG_COUNTER_TIMER_STM32 + if (single_channel_alarm_capable(dev_name)) { + return true; + } +#endif +#ifdef CONFIG_COUNTER_NATIVE_POSIX + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(counter0))) == 0) { + return true; + } +#endif + return false; +} + +void test_cancelled_alarm_does_not_expire(void) +{ + test_all_instances(test_cancelled_alarm_does_not_expire_instance, + reliable_cancel_capable); +} + +void test_main(void) +{ + const struct device *dev; + int i; + + /* Give required clocks some time to stabilize. In particular, nRF SoCs + * need such delay for the Xtal LF clock source to start and for this + * test to use the correct timing. + */ + k_busy_wait(USEC_PER_MSEC * 300); + + k_sem_init(&top_cnt_sem, 0, UINT_MAX); + k_object_access_grant(&top_cnt_sem, k_current_get()); + + k_sem_init(&alarm_cnt_sem, 0, UINT_MAX); + k_object_access_grant(&alarm_cnt_sem, k_current_get()); + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + dev = device_get_binding(devices[i]); + zassert_not_null(dev, "Unable to get counter device %s", + devices[i]); + k_object_access_grant(dev, k_current_get()); + } + + ztest_test_suite(test_counter, + /* Uses callbacks, run in supervisor mode */ + ztest_unit_test(test_set_top_value_with_alarm), + ztest_unit_test(test_single_shot_alarm_notop), + ztest_unit_test(test_single_shot_alarm_top), + ztest_unit_test(test_multiple_alarms), + ztest_unit_test(test_all_channels), + ztest_unit_test(test_late_alarm), + ztest_unit_test(test_late_alarm_error), + ztest_unit_test(test_short_relative_alarm), + ztest_unit_test(test_cancelled_alarm_does_not_expire), + + /* No callbacks, run in usermode */ + ztest_user_unit_test(test_set_top_value_without_alarm) + ); + ztest_run_test_suite(test_counter); +} diff --git a/tests/drivers/counter/counter_basic_api/testcase.yaml b/tests/drivers/counter/counter_basic_api/testcase.yaml new file mode 100644 index 0000000000000000000000000000000000000000..083cf3afdd829a300d43c528fda29258ac63c689 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/testcase.yaml @@ -0,0 +1,14 @@ +tests: + drivers.counter.basic_api: + tags: drivers + depends_on: counter + min_ram: 16 + platform_exclude: nucleo_f302r8 + timeout: 600 + drivers.counter.basic_api.nrf_zli: + tags: drivers + depends_on: counter + platform_allow: nrf52840dk_nrf52840 + timeout: 400 + extra_args: > + OVERLAY_CONFIG="zli.conf;boards/nrf52840dk_nrf52840_zli.conf" diff --git a/tests/drivers/counter/counter_basic_api/zli.conf b/tests/drivers/counter/counter_basic_api/zli.conf new file mode 100644 index 0000000000000000000000000000000000000000..d44b04ae8468e9e85a901607b6ea55c58c141d13 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/zli.conf @@ -0,0 +1 @@ +CONFIG_ZERO_LATENCY_IRQS=y