|  | /* | 
|  | * Copyright (c) 2020 Friedt Professional Engineering Services, Inc | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT zephyr_gpio_emul | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/gpio/gpio_emul.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/zephyr.h> | 
|  | #include <zephyr/pm/device.h> | 
|  |  | 
|  | #include "gpio_utils.h" | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(gpio_emul); | 
|  |  | 
|  | #define GPIO_EMUL_INT_BITMASK						\ | 
|  | (GPIO_INT_DISABLE | GPIO_INT_ENABLE | GPIO_INT_LEVELS_LOGICAL |	\ | 
|  | GPIO_INT_EDGE | GPIO_INT_LOW_0 | GPIO_INT_HIGH_1) | 
|  |  | 
|  | /** | 
|  | * @brief GPIO Emulator interrupt capabilities | 
|  | * | 
|  | * These enumerations are used as a bitmask and allow the GPIO Emulator to | 
|  | * model GPIO interrupt controllers with varying interrupt trigger support. | 
|  | * | 
|  | * For example, some controllers to not support level interrupts, | 
|  | * some controllers do not support rising and falling edge simultaneously, | 
|  | * etc. | 
|  | * | 
|  | * This primarily affects the behaviour of @ref gpio_pin_interrupt_configure. | 
|  | */ | 
|  | enum gpio_emul_interrupt_cap { | 
|  | GPIO_EMUL_INT_CAP_EDGE_RISING = 1, | 
|  | GPIO_EMUL_INT_CAP_EDGE_FALLING = 2, | 
|  | GPIO_EMUL_INT_CAP_LEVEL_HIGH = 16, | 
|  | GPIO_EMUL_INT_CAP_LEVEL_LOW = 32, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * @brief Emulated GPIO controller configuration data | 
|  | * | 
|  | * This structure contains all of the state for a given emulated GPIO | 
|  | * controller as well as all of the pins associated with it. | 
|  | * | 
|  | * The @a flags member is a pointer to an array which is @a num_pins in size. | 
|  | * | 
|  | * @a num_pins must be in the range [1, @ref GPIO_MAX_PINS_PER_PORT]. | 
|  | * | 
|  | * Pin direction as well as other pin properties are set using | 
|  | * specific bits in @a flags. For more details, see @ref gpio_interface. | 
|  | * | 
|  | * Changes are synchronized using @ref gpio_emul_data.mu. | 
|  | */ | 
|  | struct gpio_emul_config { | 
|  | /** Common @ref gpio_driver_config */ | 
|  | const struct gpio_driver_config common; | 
|  | /** Number of pins available in the given GPIO controller instance */ | 
|  | const gpio_pin_t num_pins; | 
|  | /** Supported interrupts */ | 
|  | const enum gpio_emul_interrupt_cap interrupt_caps; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * @brief Emulated GPIO controller data | 
|  | * | 
|  | * This structure contains data structures used by a emulated GPIO | 
|  | * controller. | 
|  | * | 
|  | * If the application wishes to specify a "wiring" for the emulated | 
|  | * GPIO, then a @ref gpio_callback_handler_t should be registered using | 
|  | * @ref gpio_add_callback. | 
|  | * | 
|  | * Changes are to @ref gpio_emul_data and @ref gpio_emul_config are | 
|  | * synchronized using @a mu. | 
|  | */ | 
|  | struct gpio_emul_data { | 
|  | /** Common @ref gpio_driver_data */ | 
|  | struct gpio_driver_data common; | 
|  | /** Pointer to an array of flags is @a num_pins in size */ | 
|  | gpio_flags_t *flags; | 
|  | /** Input values for each pin */ | 
|  | gpio_port_value_t input_vals; | 
|  | /** Output values for each pin */ | 
|  | gpio_port_value_t output_vals; | 
|  | /** Interrupt status for each pin */ | 
|  | gpio_port_pins_t interrupts; | 
|  | /** Mutex to synchronize accesses to driver data and config */ | 
|  | struct k_mutex mu; | 
|  | /** Singly-linked list of callbacks associated with the controller */ | 
|  | sys_slist_t callbacks; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * @brief Obtain a mask of pins that match all of the provided @p flags | 
|  | * | 
|  | * Use this function to see which pins match the current GPIO configuration. | 
|  | * | 
|  | * The caller must ensure that @ref gpio_emul_data.mu is locked. | 
|  | * | 
|  | * @param port The emulated GPIO device pointer | 
|  | * @param mask A mask of flags to match | 
|  | * @param flags The flags to match | 
|  | * | 
|  | * @return a mask of the pins with matching @p flags | 
|  | */ | 
|  | static gpio_port_pins_t | 
|  | get_pins_with_flags(const struct device *port, gpio_port_pins_t mask, | 
|  | gpio_flags_t flags) | 
|  | { | 
|  | size_t i; | 
|  | gpio_port_pins_t matched = 0; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | for (i = 0; i < config->num_pins; ++i) { | 
|  | if ((drv_data->flags[i] & mask) == flags) { | 
|  | matched |= BIT(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | return matched; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief Obtain a mask of pins that are configured as @ref GPIO_INPUT | 
|  | * | 
|  | * The caller must ensure that @ref gpio_emul_data.mu is locked. | 
|  | * | 
|  | * @param port The emulated GPIO device pointer | 
|  | * | 
|  | * @return a mask of pins that are configured as @ref GPIO_INPUT | 
|  | */ | 
|  | static inline gpio_port_pins_t get_input_pins(const struct device *port) | 
|  | { | 
|  | return get_pins_with_flags(port, GPIO_INPUT, GPIO_INPUT); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief Obtain a mask of pins that are configured as @ref GPIO_OUTPUT | 
|  | * | 
|  | * The caller must ensure that @ref gpio_emul_data.mu is locked. | 
|  | * | 
|  | * @param port The emulated GPIO device pointer | 
|  | * | 
|  | * @return a mask of pins that are configured as @ref GPIO_OUTPUT | 
|  | */ | 
|  | static inline gpio_port_pins_t get_output_pins(const struct device *port) | 
|  | { | 
|  | return get_pins_with_flags(port, GPIO_OUTPUT, GPIO_OUTPUT); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Check if @p port has capabilities specified in @p caps | 
|  | * | 
|  | * @param port The emulated GPIO device pointer | 
|  | * @param caps A bitmask of @ref gpio_emul_interrupt_cap | 
|  | * | 
|  | * @return true if all @p caps are present, otherwise false | 
|  | */ | 
|  | static inline bool gpio_emul_config_has_caps(const struct device *port, | 
|  | int caps) { | 
|  |  | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | return (caps & config->interrupt_caps) == caps; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * GPIO backend API (for setting input pin values) | 
|  | */ | 
|  | static void gpio_emul_gen_interrupt_bits(const struct device *port, | 
|  | gpio_port_pins_t mask, | 
|  | gpio_port_value_t prev_values, | 
|  | gpio_port_value_t values, | 
|  | gpio_port_pins_t *interrupts, | 
|  | bool detect_edge) | 
|  | { | 
|  | size_t i; | 
|  | bool bit; | 
|  | bool prev_bit; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | for (i = 0, *interrupts = 0; mask && i < config->num_pins; | 
|  | ++i, mask >>= 1, prev_values >>= 1, values >>= 1) { | 
|  | if ((mask & 1) == 0) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | prev_bit = ((prev_values & 1) != 0); | 
|  | bit = ((values & 1) != 0); | 
|  |  | 
|  | switch (drv_data->flags[i] & GPIO_EMUL_INT_BITMASK) { | 
|  | case GPIO_INT_EDGE_RISING: | 
|  | if (gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_EDGE_RISING)) { | 
|  | if (detect_edge && !prev_bit && bit) { | 
|  | *interrupts |= BIT(i); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GPIO_INT_EDGE_FALLING: | 
|  | if (gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_EDGE_FALLING)) { | 
|  | if (detect_edge && prev_bit && !bit) { | 
|  | *interrupts |= BIT(i); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GPIO_INT_EDGE_BOTH: | 
|  | if (gpio_emul_config_has_caps(port, | 
|  | GPIO_EMUL_INT_CAP_EDGE_RISING | GPIO_EMUL_INT_CAP_EDGE_FALLING)) { | 
|  | if (detect_edge && prev_bit != bit) { | 
|  | *interrupts |= BIT(i); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GPIO_INT_LEVEL_LOW: | 
|  | if (gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_LEVEL_LOW)) { | 
|  | if (!bit) { | 
|  | *interrupts |= BIT(i); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GPIO_INT_LEVEL_HIGH: | 
|  | if (gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_LEVEL_HIGH)) { | 
|  | if (bit) { | 
|  | *interrupts |= BIT(i); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case 0: | 
|  | case GPIO_INT_DISABLE: | 
|  | break; | 
|  | default: | 
|  | LOG_DBG("unhandled case %u", | 
|  | drv_data->flags[i] & GPIO_EMUL_INT_BITMASK); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief Trigger possible interrupt events after an input pin has changed | 
|  | * | 
|  | * For more information, see @ref gpio_interface. | 
|  | * | 
|  | * The caller must ensure that @ref gpio_emul_data.mu is locked. | 
|  | * | 
|  | * @param port The emulated GPIO port | 
|  | * @param mask The mask of pins that have changed | 
|  | * @param prev_values Previous pin values | 
|  | * @param values Current pin values | 
|  | */ | 
|  | static void gpio_emul_pend_interrupt(const struct device *port, gpio_port_pins_t mask, | 
|  | gpio_port_value_t prev_values, | 
|  | gpio_port_value_t values) | 
|  | { | 
|  | gpio_port_pins_t interrupts; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  |  | 
|  | gpio_emul_gen_interrupt_bits(port, mask, prev_values, values, | 
|  | &interrupts, true); | 
|  | while (interrupts != 0) { | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, interrupts); | 
|  | gpio_emul_gen_interrupt_bits(port, mask, prev_values, values, | 
|  | &interrupts, false); | 
|  | } | 
|  | } | 
|  |  | 
|  | int gpio_emul_input_set_masked_pend(const struct device *port, gpio_port_pins_t mask, | 
|  | gpio_port_value_t values, bool pend) | 
|  | { | 
|  | int ret; | 
|  | gpio_port_pins_t input_mask; | 
|  | gpio_port_pins_t prev_values; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | if (mask == 0) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (~config->common.port_pin_mask & mask) { | 
|  | LOG_ERR("Pin not supported port_pin_mask=%x mask=%x", | 
|  | config->common.port_pin_mask, mask); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  |  | 
|  | input_mask = get_input_pins(port); | 
|  | if (~input_mask & mask) { | 
|  | LOG_ERR("Not input pin input_mask=%x mask=%x", input_mask, | 
|  | mask); | 
|  | ret = -EINVAL; | 
|  | goto unlock; | 
|  | } | 
|  |  | 
|  | prev_values = drv_data->input_vals; | 
|  | drv_data->input_vals &= ~mask; | 
|  | drv_data->input_vals |= values & mask; | 
|  | values = drv_data->input_vals; | 
|  |  | 
|  | if (pend) { | 
|  | gpio_emul_pend_interrupt(port, mask, prev_values, values); | 
|  | } | 
|  | ret = 0; | 
|  |  | 
|  | unlock: | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* documented in drivers/gpio/gpio_emul.h */ | 
|  | int gpio_emul_input_set_masked(const struct device *port, gpio_port_pins_t mask, | 
|  | gpio_port_value_t values) | 
|  | { | 
|  | return gpio_emul_input_set_masked_pend(port, mask, values, true); | 
|  | } | 
|  |  | 
|  | /* documented in drivers/gpio/gpio_emul.h */ | 
|  | int gpio_emul_output_get_masked(const struct device *port, gpio_port_pins_t mask, | 
|  | gpio_port_value_t *values) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | if (mask == 0) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (~config->common.port_pin_mask & mask) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | *values = drv_data->output_vals & get_output_pins(port); | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* documented in drivers/gpio/gpio_emul.h */ | 
|  | int gpio_emul_flags_get(const struct device *port, gpio_pin_t pin, gpio_flags_t *flags) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | if (flags == NULL) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if ((config->common.port_pin_mask & BIT(pin)) == 0) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | *flags = drv_data->flags[pin]; | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * GPIO Driver API | 
|  | * | 
|  | * API is documented at drivers/gpio.h | 
|  | */ | 
|  |  | 
|  | static int gpio_emul_pin_configure(const struct device *port, gpio_pin_t pin, | 
|  | gpio_flags_t flags) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  | int rv; | 
|  |  | 
|  | if (flags & GPIO_OPEN_DRAIN) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if (flags & GPIO_OPEN_SOURCE) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if ((config->common.port_pin_mask & BIT(pin)) == 0) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | drv_data->flags[pin] = flags; | 
|  | if (flags & GPIO_OUTPUT) { | 
|  | if (flags & GPIO_OUTPUT_INIT_LOW) { | 
|  | drv_data->output_vals &= ~BIT(pin); | 
|  | if (flags & GPIO_INPUT) { | 
|  | /* for push-pull mode to generate interrupts */ | 
|  | rv = gpio_emul_input_set_masked_pend( | 
|  | port, BIT(pin), drv_data->output_vals, | 
|  | false); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | } | 
|  | } else if (flags & GPIO_OUTPUT_INIT_HIGH) { | 
|  | drv_data->output_vals |= BIT(pin); | 
|  | if (flags & GPIO_INPUT) { | 
|  | /* for push-pull mode to generate interrupts */ | 
|  | rv = gpio_emul_input_set_masked_pend( | 
|  | port, BIT(pin), drv_data->output_vals, | 
|  | false); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | } | 
|  | } | 
|  | } else if (flags & GPIO_INPUT) { | 
|  | if (flags & GPIO_PULL_UP) { | 
|  | rv = gpio_emul_input_set_masked_pend(port, BIT(pin), BIT(pin), | 
|  | false); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | } else if (flags & GPIO_PULL_DOWN) { | 
|  | rv = gpio_emul_input_set_masked_pend( | 
|  | port, BIT(pin), 0, false); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, BIT(pin)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_GPIO_GET_CONFIG | 
|  | static int gpio_emul_pin_get_config(const struct device *port, gpio_pin_t pin, | 
|  | gpio_flags_t *out_flags) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  |  | 
|  | *out_flags = drv_data->flags[pin] & | 
|  | ~(GPIO_OUTPUT_INIT_LOW | GPIO_OUTPUT_INIT_HIGH | 
|  | | GPIO_OUTPUT_INIT_LOGICAL); | 
|  | if (drv_data->flags[pin] & GPIO_OUTPUT) { | 
|  | if (drv_data->output_vals & BIT(pin)) { | 
|  | *out_flags |= GPIO_OUTPUT_HIGH; | 
|  | } else { | 
|  | *out_flags |= GPIO_OUTPUT_LOW; | 
|  | } | 
|  | } | 
|  |  | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static int gpio_emul_port_get_raw(const struct device *port, gpio_port_value_t *values) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  |  | 
|  | if (values == NULL) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | *values = drv_data->input_vals & get_input_pins(port); | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_emul_port_set_masked_raw(const struct device *port, | 
|  | gpio_port_pins_t mask, | 
|  | gpio_port_value_t values) | 
|  | { | 
|  | gpio_port_pins_t output_mask; | 
|  | gpio_port_pins_t prev_values; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | int rv; | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | output_mask = get_output_pins(port); | 
|  | mask &= output_mask; | 
|  | prev_values = drv_data->output_vals; | 
|  | prev_values &= output_mask; | 
|  | values &= mask; | 
|  | drv_data->output_vals &= ~mask; | 
|  | drv_data->output_vals |= values; | 
|  | /* in push-pull, set input values & fire interrupts */ | 
|  | rv = gpio_emul_input_set_masked(port, mask & get_input_pins(port), | 
|  | drv_data->output_vals); | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  |  | 
|  | /* for output-wiring, so the user can take action based on output */ | 
|  | if (prev_values ^ values) { | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, mask & ~get_input_pins(port)); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_emul_port_set_bits_raw(const struct device *port, | 
|  | gpio_port_pins_t pins) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | int rv; | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | pins &= get_output_pins(port); | 
|  | drv_data->output_vals |= pins; | 
|  | /* in push-pull, set input values & fire interrupts */ | 
|  | rv = gpio_emul_input_set_masked(port, pins & get_input_pins(port), | 
|  | drv_data->output_vals); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  |  | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  | /* for output-wiring, so the user can take action based on output */ | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, pins & ~get_input_pins(port)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_emul_port_clear_bits_raw(const struct device *port, | 
|  | gpio_port_pins_t pins) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | int rv; | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | pins &= get_output_pins(port); | 
|  | drv_data->output_vals &= ~pins; | 
|  | /* in push-pull, set input values & fire interrupts */ | 
|  | rv = gpio_emul_input_set_masked(port, pins & get_input_pins(port), drv_data->output_vals); | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | /* for output-wiring, so the user can take action based on output */ | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, pins & ~get_input_pins(port)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_emul_port_toggle_bits(const struct device *port, gpio_port_pins_t pins) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | int rv; | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  | drv_data->output_vals ^= (pins & get_output_pins(port)); | 
|  | /* in push-pull, set input values but do not fire interrupts (yet) */ | 
|  | rv = gpio_emul_input_set_masked_pend(port, pins & get_input_pins(port), | 
|  | drv_data->output_vals, false); | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  | __ASSERT_NO_MSG(rv == 0); | 
|  | /* for output-wiring, so the user can take action based on output */ | 
|  | gpio_fire_callbacks(&drv_data->callbacks, port, pins); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static bool gpio_emul_level_trigger_supported(const struct device *port, | 
|  | enum gpio_int_trig trig) | 
|  | { | 
|  | switch (trig) { | 
|  | case GPIO_INT_TRIG_LOW: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_LEVEL_LOW); | 
|  | case GPIO_INT_TRIG_HIGH: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_LEVEL_HIGH); | 
|  | case GPIO_INT_TRIG_BOTH: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_LEVEL_LOW | 
|  | | GPIO_EMUL_INT_CAP_LEVEL_HIGH); | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool gpio_emul_edge_trigger_supported(const struct device *port, | 
|  | enum gpio_int_trig trig) | 
|  | { | 
|  | switch (trig) { | 
|  | case GPIO_INT_TRIG_LOW: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_EDGE_FALLING); | 
|  | case GPIO_INT_TRIG_HIGH: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_EDGE_RISING); | 
|  | case GPIO_INT_TRIG_BOTH: | 
|  | return gpio_emul_config_has_caps(port, GPIO_EMUL_INT_CAP_EDGE_FALLING | 
|  | | GPIO_EMUL_INT_CAP_EDGE_RISING); | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int gpio_emul_pin_interrupt_configure(const struct device *port, gpio_pin_t pin, | 
|  | enum gpio_int_mode mode, | 
|  | enum gpio_int_trig trig) | 
|  | { | 
|  | int ret; | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = | 
|  | (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | if ((BIT(pin) & config->common.port_pin_mask) == 0) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (mode != GPIO_INT_MODE_DISABLED) { | 
|  | switch (trig) { | 
|  | case GPIO_INT_TRIG_LOW: | 
|  | case GPIO_INT_TRIG_HIGH: | 
|  | case GPIO_INT_TRIG_BOTH: | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mode == GPIO_INT_MODE_LEVEL) { | 
|  | if (!gpio_emul_level_trigger_supported(port, trig)) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mode == GPIO_INT_MODE_EDGE) { | 
|  | if (!gpio_emul_edge_trigger_supported(port, trig)) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | k_mutex_lock(&drv_data->mu, K_FOREVER); | 
|  |  | 
|  | switch (mode) { | 
|  | case GPIO_INT_MODE_DISABLED: | 
|  | drv_data->flags[pin] &= ~GPIO_EMUL_INT_BITMASK; | 
|  | drv_data->flags[pin] |= GPIO_INT_DISABLE; | 
|  | break; | 
|  | case GPIO_INT_MODE_LEVEL: | 
|  | case GPIO_INT_MODE_EDGE: | 
|  | drv_data->flags[pin] &= ~GPIO_EMUL_INT_BITMASK; | 
|  | drv_data->flags[pin] |= (mode | trig); | 
|  | break; | 
|  | default: | 
|  | ret = -EINVAL; | 
|  | goto unlock; | 
|  | } | 
|  |  | 
|  | ret = 0; | 
|  |  | 
|  | unlock: | 
|  | k_mutex_unlock(&drv_data->mu); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int gpio_emul_manage_callback(const struct device *port, | 
|  | struct gpio_callback *cb, bool set) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)port->data; | 
|  |  | 
|  | return gpio_manage_callback(&drv_data->callbacks, cb, set); | 
|  | } | 
|  |  | 
|  | static gpio_port_pins_t gpio_emul_get_pending_int(const struct device *dev) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)dev->data; | 
|  |  | 
|  | return drv_data->interrupts; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_GPIO_GET_DIRECTION | 
|  | static int gpio_emul_port_get_direction(const struct device *port, gpio_port_pins_t map, | 
|  | gpio_port_pins_t *inputs, gpio_port_pins_t *outputs) | 
|  | { | 
|  | int i; | 
|  | gpio_port_pins_t ip = 0; | 
|  | gpio_port_pins_t op = 0; | 
|  | struct gpio_emul_data *const drv_data = (struct gpio_emul_data *)port->data; | 
|  | const struct gpio_emul_config *config = (const struct gpio_emul_config *)port->config; | 
|  |  | 
|  | map &= config->common.port_pin_mask; | 
|  |  | 
|  | if (inputs != NULL) { | 
|  | for (i = find_lsb_set(map) - 1; map; | 
|  | map &= ~BIT(i), i = find_lsb_set(map) - 1) { | 
|  | ip |= !!(drv_data->flags[i] & GPIO_INPUT) * BIT(i); | 
|  | } | 
|  |  | 
|  | *inputs = ip; | 
|  | } | 
|  |  | 
|  | if (outputs != NULL) { | 
|  | for (i = find_lsb_set(map) - 1; map; | 
|  | map &= ~BIT(i), i = find_lsb_set(map) - 1) { | 
|  | op |= !!(drv_data->flags[i] & GPIO_OUTPUT) * BIT(i); | 
|  | } | 
|  |  | 
|  | *outputs = op; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif /* CONFIG_GPIO_GET_DIRECTION */ | 
|  |  | 
|  | static const struct gpio_driver_api gpio_emul_driver = { | 
|  | .pin_configure = gpio_emul_pin_configure, | 
|  | #ifdef CONFIG_GPIO_GET_CONFIG | 
|  | .pin_get_config = gpio_emul_pin_get_config, | 
|  | #endif | 
|  | .port_get_raw = gpio_emul_port_get_raw, | 
|  | .port_set_masked_raw = gpio_emul_port_set_masked_raw, | 
|  | .port_set_bits_raw = gpio_emul_port_set_bits_raw, | 
|  | .port_clear_bits_raw = gpio_emul_port_clear_bits_raw, | 
|  | .port_toggle_bits = gpio_emul_port_toggle_bits, | 
|  | .pin_interrupt_configure = gpio_emul_pin_interrupt_configure, | 
|  | .manage_callback = gpio_emul_manage_callback, | 
|  | .get_pending_int = gpio_emul_get_pending_int, | 
|  | #ifdef CONFIG_GPIO_GET_DIRECTION | 
|  | .port_get_direction = gpio_emul_port_get_direction, | 
|  | #endif /* CONFIG_GPIO_GET_DIRECTION */ | 
|  | }; | 
|  |  | 
|  | static int gpio_emul_init(const struct device *dev) | 
|  | { | 
|  | struct gpio_emul_data *drv_data = | 
|  | (struct gpio_emul_data *)dev->data; | 
|  |  | 
|  | sys_slist_init(&drv_data->callbacks); | 
|  | return k_mutex_init(&drv_data->mu); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PM_DEVICE | 
|  | static int gpio_emul_pm_device_pm_action(const struct device *dev, | 
|  | enum pm_device_action action) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  | ARG_UNUSED(action); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Device Initialization | 
|  | */ | 
|  |  | 
|  | #define GPIO_EMUL_INT_CAPS(_num) (0					\ | 
|  | + DT_INST_PROP(_num, rising_edge)				\ | 
|  | * GPIO_EMUL_INT_CAP_EDGE_RISING				\ | 
|  | + DT_INST_PROP(_num, falling_edge)				\ | 
|  | * GPIO_EMUL_INT_CAP_EDGE_FALLING			\ | 
|  | + DT_INST_PROP(_num, high_level)				\ | 
|  | * GPIO_EMUL_INT_CAP_LEVEL_HIGH				\ | 
|  | + DT_INST_PROP(_num, low_level)					\ | 
|  | * GPIO_EMUL_INT_CAP_LEVEL_LOW				\ | 
|  | ) | 
|  |  | 
|  | #define DEFINE_GPIO_EMUL(_num)						\ | 
|  | \ | 
|  | static gpio_flags_t						\ | 
|  | gpio_emul_flags_##_num[DT_INST_PROP(_num, ngpios)];	\ | 
|  | \ | 
|  | static const struct gpio_emul_config gpio_emul_config_##_num = {\ | 
|  | .common = {						\ | 
|  | .port_pin_mask =				\ | 
|  | GPIO_PORT_PIN_MASK_FROM_DT_INST(_num),	\ | 
|  | },							\ | 
|  | .num_pins = DT_INST_PROP(_num, ngpios),			\ | 
|  | .interrupt_caps = GPIO_EMUL_INT_CAPS(_num)		\ | 
|  | };								\ | 
|  | \ | 
|  | static struct gpio_emul_data gpio_emul_data_##_num = {		\ | 
|  | .flags = gpio_emul_flags_##_num,			\ | 
|  | };								\ | 
|  | \ | 
|  | PM_DEVICE_DT_INST_DEFINE(_num, gpio_emul_pm_device_pm_action);	\ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(_num, gpio_emul_init,			\ | 
|  | PM_DEVICE_DT_INST_GET(_num),		\ | 
|  | &gpio_emul_data_##_num,			\ | 
|  | &gpio_emul_config_##_num, POST_KERNEL,	\ | 
|  | CONFIG_GPIO_INIT_PRIORITY,			\ | 
|  | &gpio_emul_driver); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(DEFINE_GPIO_EMUL) |