blob: 468fe1a7b2f07a83194e7d170841043267f3673a [file] [log] [blame]
/*
* Copyright (c) 2019 Piotr Mienkowski
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <limits.h>
#include <zephyr/sys/util.h>
#include "test_gpio_api.h"
struct gpio_callback gpio_cb;
static int cb_count;
static int cb_count_target;
static void callback_edge(const struct device *port, struct gpio_callback *cb,
gpio_port_pins_t pins)
{
zassert_equal(pins, BIT(TEST_PIN),
"Detected interrupt on an invalid pin");
cb_count++;
}
static void callback_level(const struct device *port,
struct gpio_callback *cb,
gpio_port_pins_t pins)
{
int ret;
zassert_equal(pins, BIT(TEST_PIN),
"Detected interrupt on an invalid pin");
cb_count++;
if (cb_count % cb_count_target == 0) {
ret = gpio_pin_interrupt_configure(port, TEST_PIN, GPIO_INT_DISABLE);
zassert_equal(ret, 0,
"Failed to disable pin interrupt in the callback");
}
}
static void pin_set_and_verify(const struct device *port, unsigned int pin,
int val,
int idx)
{
zassert_equal(gpio_pin_set(port, pin, val), 0,
"Test point %d: failed to set logical pin value", idx);
k_busy_wait(TEST_GPIO_MAX_RISE_FALL_TIME_US);
}
void test_gpio_pin_interrupt_edge(unsigned int cfg_flags,
unsigned int int_flags)
{
const struct device *port;
int cb_count_expected;
unsigned int cfg_out_flag;
int ret;
port = DEVICE_DT_GET(TEST_NODE);
zassert_true(device_is_ready(port), "GPIO dev is not ready");
TC_PRINT("Running test on port=%s, pin=%d\n", port->name, TEST_PIN);
ret = gpio_pin_configure(port, TEST_PIN, GPIO_INPUT | GPIO_OUTPUT);
if (ret == -ENOTSUP) {
TC_PRINT("Simultaneous pin in/out mode is not supported.\n");
ztest_test_skip();
return;
}
zassert_equal(ret, 0, "Failed to configure the pin");
if ((cfg_flags & GPIO_ACTIVE_LOW) != 0) {
cfg_out_flag = GPIO_OUTPUT_HIGH;
} else {
cfg_out_flag = GPIO_OUTPUT_LOW;
}
ret = gpio_pin_configure(port, TEST_PIN, GPIO_INPUT | cfg_out_flag |
cfg_flags);
zassert_equal(ret, 0, "Failed to configure the pin");
cb_count = 0;
cb_count_expected = 0;
gpio_init_callback(&gpio_cb, callback_edge, BIT(TEST_PIN));
ret = gpio_add_callback(port, &gpio_cb);
ret = gpio_pin_interrupt_configure(port, TEST_PIN, int_flags);
if (ret == -ENOTSUP) {
TC_PRINT("Pin interrupt is not supported.\n");
ztest_test_skip();
return;
}
zassert_equal(ret, 0, "Failed to configure pin interrupt");
for (int i = 0; i < 6; i++) {
pin_set_and_verify(port, TEST_PIN, 1, i);
if (int_flags & GPIO_INT_HIGH_1) {
cb_count_expected++;
}
zassert_equal(cb_count, cb_count_expected,
"Test point %d: Pin interrupt triggered invalid "
"number of times on rising/to active edge", i);
pin_set_and_verify(port, TEST_PIN, 0, i);
if (int_flags & GPIO_INT_LOW_0) {
cb_count_expected++;
}
zassert_equal(cb_count, cb_count_expected,
"Test point %d: Pin interrupt triggered invalid "
"number of times on falling/to inactive edge", i);
}
ret = gpio_pin_interrupt_configure(port, TEST_PIN, GPIO_INT_DISABLE);
zassert_equal(ret, 0, "Failed to disable pin interrupt");
for (int i = 0; i < 6; i++) {
pin_set_and_verify(port, TEST_PIN, 1, i);
pin_set_and_verify(port, TEST_PIN, 0, i);
zassert_equal(cb_count, cb_count_expected,
"Pin interrupt triggered when disabled");
}
}
void test_gpio_pin_interrupt_level(unsigned int cfg_flags,
unsigned int int_flags, unsigned int interrupt_calls)
{
const struct device *port;
int cb_count_expected;
unsigned int cfg_out_flag;
int pin_out_val;
int ret;
port = DEVICE_DT_GET(TEST_NODE);
zassert_true(device_is_ready(port), "GPIO dev is not ready");
TC_PRINT("Running test on port=%s, pin=%d\n", port->name, TEST_PIN);
ret = gpio_pin_configure(port, TEST_PIN, GPIO_INPUT | GPIO_OUTPUT);
if (ret == -ENOTSUP) {
TC_PRINT("Simultaneous pin in/out mode is not supported.\n");
ztest_test_skip();
return;
}
zassert_equal(ret, 0, "Failed to configure the pin");
pin_out_val = ((int_flags & GPIO_INT_HIGH_1) != 0) ? 0 : 1;
if ((cfg_flags & GPIO_ACTIVE_LOW) != 0) {
cfg_out_flag = (pin_out_val != 0) ? GPIO_OUTPUT_LOW :
GPIO_OUTPUT_HIGH;
} else {
cfg_out_flag = (pin_out_val != 0) ? GPIO_OUTPUT_HIGH :
GPIO_OUTPUT_LOW;
}
ret = gpio_pin_configure(port, TEST_PIN, GPIO_INPUT | cfg_out_flag |
cfg_flags);
zassert_equal(ret, 0, "Failed to configure the pin");
cb_count = 0;
cb_count_expected = 0;
cb_count_target = interrupt_calls;
gpio_init_callback(&gpio_cb, callback_level, BIT(TEST_PIN));
ret = gpio_add_callback(port, &gpio_cb);
ret = gpio_pin_interrupt_configure(port, TEST_PIN, int_flags);
if (ret == -ENOTSUP) {
TC_PRINT("Pin interrupt is not supported.\n");
ztest_test_skip();
return;
}
zassert_equal(ret, 0, "Failed to configure pin interrupt");
zassert_equal(cb_count, cb_count_expected,
"Pin interrupt triggered on level %d", pin_out_val);
for (int i = 0; i < 6; i++) {
pin_out_val = (pin_out_val != 0) ? 0 : 1;
pin_set_and_verify(port, TEST_PIN, pin_out_val, i);
cb_count_expected += interrupt_calls;
zassert_equal(cb_count, cb_count_expected,
"Test point %d: Pin interrupt triggered invalid "
"number of times on level %d", i, pin_out_val);
pin_out_val = (pin_out_val != 0) ? 0 : 1;
pin_set_and_verify(port, TEST_PIN, pin_out_val, i);
zassert_equal(cb_count, cb_count_expected,
"Test point %d: Pin interrupt triggered invalid "
"number of times on level %d", i, pin_out_val);
/* Re-enable pin level interrupt */
ret = gpio_pin_interrupt_configure(port, TEST_PIN, int_flags);
zassert_equal(ret, 0,
"Failed to re-enable pin level interrupt");
}
ret = gpio_pin_interrupt_configure(port, TEST_PIN, GPIO_INT_DISABLE);
zassert_equal(ret, 0, "Failed to disable pin interrupt");
for (int i = 0; i < 6; i++) {
pin_set_and_verify(port, TEST_PIN, 1, i);
pin_set_and_verify(port, TEST_PIN, 0, i);
zassert_equal(cb_count, cb_count_expected,
"Pin interrupt triggered when disabled");
}
}
/** @brief Verify GPIO_INT_EDGE_RISING flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_edge_rising)
{
test_gpio_pin_interrupt_edge(0, GPIO_INT_EDGE_RISING);
}
/** @brief Verify GPIO_INT_EDGE_FALLING flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_edge_falling)
{
test_gpio_pin_interrupt_edge(0, GPIO_INT_EDGE_FALLING);
}
/** @brief Verify GPIO_INT_EDGE_BOTH flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_edge_both)
{
test_gpio_pin_interrupt_edge(0, GPIO_INT_EDGE_BOTH);
}
/** @brief Verify GPIO_INT_EDGE_TO_ACTIVE flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_edge_to_active)
{
TC_PRINT("Step 1: Configure pin as active high\n");
test_gpio_pin_interrupt_edge(GPIO_ACTIVE_HIGH, GPIO_INT_EDGE_TO_ACTIVE);
TC_PRINT("Step 2: Configure pin as active low\n");
test_gpio_pin_interrupt_edge(GPIO_ACTIVE_LOW, GPIO_INT_EDGE_TO_ACTIVE);
}
/** @brief Verify GPIO_INT_EDGE_TO_INACTIVE flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_edge_to_inactive)
{
TC_PRINT("Step 1: Configure pin as active high\n");
test_gpio_pin_interrupt_edge(GPIO_ACTIVE_HIGH, GPIO_INT_EDGE_TO_INACTIVE);
TC_PRINT("Step 2: Configure pin as active low\n");
test_gpio_pin_interrupt_edge(GPIO_ACTIVE_LOW, GPIO_INT_EDGE_TO_INACTIVE);
}
/** @brief Verify GPIO_INT_LEVEL_HIGH flag with 1 interrupt call */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_high_interrupt_count_1)
{
test_gpio_pin_interrupt_level(0, GPIO_INT_LEVEL_HIGH, 1);
}
/** @brief Verify GPIO_INT_LEVEL_HIGH flag with 5 interrupt call */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_high_interrupt_count_5)
{
test_gpio_pin_interrupt_level(0, GPIO_INT_LEVEL_HIGH, 5);
}
/** @brief Verify GPIO_INT_LEVEL_LOW flag with 1 interrupt call */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_low_interrupt_count_1)
{
test_gpio_pin_interrupt_level(0, GPIO_INT_LEVEL_LOW, 1);
}
/** @brief Verify GPIO_INT_LEVEL_LOW flag with 5 interrupt call */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_low_interrupt_count_5)
{
test_gpio_pin_interrupt_level(0, GPIO_INT_LEVEL_LOW, 5);
}
/** @brief Verify GPIO_INT_LEVEL_ACTIVE flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_active)
{
TC_PRINT("Step 1: Configure pin as active high\n");
test_gpio_pin_interrupt_level(GPIO_ACTIVE_HIGH, GPIO_INT_LEVEL_ACTIVE, 1);
TC_PRINT("Step 2: Configure pin as active low\n");
test_gpio_pin_interrupt_level(GPIO_ACTIVE_LOW, GPIO_INT_LEVEL_ACTIVE, 1);
}
/** @brief Verify GPIO_INT_LEVEL_INACTIVE flag. */
ZTEST(gpio_api_1pin_int, test_gpio_int_level_inactive)
{
TC_PRINT("Step 1: Configure pin as active high\n");
test_gpio_pin_interrupt_level(GPIO_ACTIVE_HIGH, GPIO_INT_LEVEL_INACTIVE, 1);
TC_PRINT("Step 2: Configure pin as active low\n");
test_gpio_pin_interrupt_level(GPIO_ACTIVE_LOW, GPIO_INT_LEVEL_INACTIVE, 1);
}
ZTEST_SUITE(gpio_api_1pin_int, NULL, NULL, NULL, NULL, NULL);