| /* |
| * 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); |