diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index cc7cb9a1411..a77bbf643c0 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -1156,6 +1156,23 @@ Release Notes: tests: - drivers.clock +"Drivers: Comparator": + status: maintained + maintainers: + - bjarki-andreasen + files: + - drivers/comparator/ + - dts/bindings/comparator/ + - include/zephyr/drivers/comparator.h + - include/zephyr/drivers/comparator/ + - tests/drivers/build_all/comparator/ + - tests/drivers/comparator/ + - doc/hardware/peripherals/comparator.rst + labels: + - "area: Comparator" + tests: + - drivers.comparator + "Drivers: Console": status: odd fixes files: diff --git a/doc/hardware/peripherals/comparator.rst b/doc/hardware/peripherals/comparator.rst new file mode 100644 index 00000000000..e0e63407661 --- /dev/null +++ b/doc/hardware/peripherals/comparator.rst @@ -0,0 +1,69 @@ +.. _comparator_api: + +Comparator +########## + +Overview +******** + +An analog comparator compares the voltages of two analog signals connected to its negative and +positive inputs. If the voltage at the positive input is higher than the negative input, the +comparator's output will be high, otherwise, it will be low. + +Comparators can typically set a trigger which triggers on output changes. This trigger can +either invoke a callback, or its status can be polled. + +Related configuration options: + +* :kconfig:option:`CONFIG_COMPARATOR` + +Configuration +************* + +Embedded comparators can typically be configured at runtime. When enabled, an initial +configuration must be provided using the devicetree. At runtime, comparators can have their +configuration updated using device driver specific APIs. The configuration will be applied +when the comparator is resumed. + +Power management +**************** + +Comparators are enabled using power management. When resumed, the comparator will actively +compare its inputs, producing an output and detecting edges. When suspended, the comparator +will be inactive. + +Comparator shell +**************** + +The comparator shell provides the ``comp`` command with a set of subcommands for the +:ref:`shell ` module. + +The ``comp`` shell command provides the following subcommands: + +* ``get_output`` See :c:func:`comparator_get_output` +* ``set_trigger`` See :c:func:`comparator_set_trigger` +* ``await_trigger`` Awaits trigger using the following flow: + * Set trigger callback using :c:func:`comparator_set_trigger_callback` + * Await callback or time out after default or optionally provided timeout + * Clear trigger callback using :c:func:`comparator_set_trigger_callback` +* ``trigger_is_pending`` See :c:func:`comparator_trigger_is_pending` + +Related configuration options: + +* :kconfig:option:`CONFIG_SHELL` +* :kconfig:option:`CONFIG_COMPARATOR_SHELL` +* :kconfig:option:`CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_DEFAULT_TIMEOUT` +* :kconfig:option:`CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT` + +.. note:: + The power management shell can optionally be enabled alongside the comparator shell. + + Related configuration options: + + * :kconfig:option:`CONFIG_PM_DEVICE` + * :kconfig:option:`CONFIG_PM_DEVICE_SHELL` + +API Reference +************* + +.. doxygengroup:: comparator_interface diff --git a/doc/hardware/peripherals/index.rst b/doc/hardware/peripherals/index.rst index e2574e75993..2a7405aced2 100644 --- a/doc/hardware/peripherals/index.rst +++ b/doc/hardware/peripherals/index.rst @@ -18,6 +18,7 @@ Peripherals clock_control.rst can/index.rst charger.rst + comparator.rst coredump.rst counter.rst dac.rst diff --git a/doc/releases/migration-guide-4.0.rst b/doc/releases/migration-guide-4.0.rst index 3699a7fedcb..3581625f830 100644 --- a/doc/releases/migration-guide-4.0.rst +++ b/doc/releases/migration-guide-4.0.rst @@ -150,6 +150,11 @@ Sensors to support all JEDEC JC 42.4 compatible temperature sensors. It now uses the :dtcompatible:`jedec,jc-42.4-temp` compatible string instead to the ``microchip,mcp9808`` string. +* The ``nxp,`` prefixed properties in :dtcompatible:`nxp,kinetis-acmp` have been deprecated in favor + of properties without the prefix. The sensor based driver for the :dtcompatible:`nxp,kinetis-acmp` + has been updated to support both the new and deprecated property names. Uses of the deprecated + property names should be updated to the new property names. + Serial ====== diff --git a/doc/releases/release-notes-4.0.rst b/doc/releases/release-notes-4.0.rst index 70ed64dd770..85c2523ab00 100644 --- a/doc/releases/release-notes-4.0.rst +++ b/doc/releases/release-notes-4.0.rst @@ -145,6 +145,14 @@ Drivers and Sensors * Clock control +* Comparator + + * Introduced comparator device driver subsystem selected with :kconfig:option:`CONFIG_COMPARATOR` + * Introduced comparator shell commands selected with :kconfig:option:`CONFIG_COMPARATOR_SHELL` + * Added support for Nordic nRF COMP (:dtcompatible:`nordic,nrf-comp`) + * Added support for Nordic nRF LPCOMP (:dtcompatible:`nordic,nrf-lpcomp`) + * Added support for NXP Kinetis ACMP (:dtcompatible:`nxp,kinetis-acmp`) + * Counter * DAC diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index acf20d2867c..05be8c3fd01 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory_ifdef(CONFIG_CACHE_MANAGEMENT cache) add_subdirectory_ifdef(CONFIG_CAN can) add_subdirectory_ifdef(CONFIG_CHARGER charger) add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control) +add_subdirectory_ifdef(CONFIG_COMPARATOR comparator) add_subdirectory_ifdef(CONFIG_CONSOLE console) add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump) add_subdirectory_ifdef(CONFIG_COUNTER counter) diff --git a/drivers/Kconfig b/drivers/Kconfig index ec9e0c77b6d..db80ba39a66 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -15,6 +15,7 @@ source "drivers/cache/Kconfig" source "drivers/can/Kconfig" source "drivers/charger/Kconfig" source "drivers/clock_control/Kconfig" +source "drivers/comparator/Kconfig" source "drivers/console/Kconfig" source "drivers/coredump/Kconfig" source "drivers/counter/Kconfig" diff --git a/drivers/comparator/CMakeLists.txt b/drivers/comparator/CMakeLists.txt new file mode 100644 index 00000000000..43462d64288 --- /dev/null +++ b/drivers/comparator/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/comparator.h) + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_USERSPACE comparator_handlers.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_FAKE_COMP comparator_fake_comp.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_MCUX_ACMP comparator_mcux_acmp.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_SHELL comparator_shell.c) diff --git a/drivers/comparator/Kconfig b/drivers/comparator/Kconfig new file mode 100644 index 00000000000..978adc03251 --- /dev/null +++ b/drivers/comparator/Kconfig @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +menuconfig COMPARATOR + bool "Comparator drivers" + help + Enable comparator driver configuration. + +if COMPARATOR + +module = COMPARATOR +module-str = comparator +source "subsys/logging/Kconfig.template.log_config" + +config COMPARATOR_INIT_PRIORITY + int "COMPARATOR init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + Comparator device driver initialization priority. + +rsource "Kconfig.fake_comp" +rsource "Kconfig.mcux_acmp" +rsource "Kconfig.nrf_comp" +rsource "Kconfig.nrf_lpcomp" +rsource "Kconfig.shell" + +endif # COMPARATOR diff --git a/drivers/comparator/Kconfig.fake_comp b/drivers/comparator/Kconfig.fake_comp new file mode 100644 index 00000000000..5fca8db1878 --- /dev/null +++ b/drivers/comparator/Kconfig.fake_comp @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_FAKE_COMP + bool "Fake comparator driver" + default y + depends on DT_HAS_ZEPHYR_FAKE_COMP_ENABLED + depends on ZTEST diff --git a/drivers/comparator/Kconfig.mcux_acmp b/drivers/comparator/Kconfig.mcux_acmp new file mode 100644 index 00000000000..8109cb1cdd2 --- /dev/null +++ b/drivers/comparator/Kconfig.mcux_acmp @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_MCUX_ACMP + bool "NXP MCUX ACMP comparator driver" + default y + depends on DT_HAS_NXP_KINETIS_ACMP_ENABLED + select PINCTRL + select MCUX_ACMP diff --git a/drivers/comparator/Kconfig.nrf_comp b/drivers/comparator/Kconfig.nrf_comp new file mode 100644 index 00000000000..0e0c889f3ee --- /dev/null +++ b/drivers/comparator/Kconfig.nrf_comp @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_NRF_COMP + bool "Nordic COMP comparator driver" + default y + depends on DT_HAS_NORDIC_NRF_COMP_ENABLED + select NRFX_COMP diff --git a/drivers/comparator/Kconfig.nrf_lpcomp b/drivers/comparator/Kconfig.nrf_lpcomp new file mode 100644 index 00000000000..f33de46f15d --- /dev/null +++ b/drivers/comparator/Kconfig.nrf_lpcomp @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_NRF_LPCOMP + bool "Nordic LPCOMP comparator driver" + default y + depends on DT_HAS_NORDIC_NRF_LPCOMP_ENABLED + select NRFX_LPCOMP diff --git a/drivers/comparator/Kconfig.shell b/drivers/comparator/Kconfig.shell new file mode 100644 index 00000000000..de244619be3 --- /dev/null +++ b/drivers/comparator/Kconfig.shell @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_SHELL + bool "Comparator shell" + default y + depends on SHELL + help + Comparator device driver shell. + +if COMPARATOR_SHELL + +config COMPARATOR_SHELL_AWAIT_TRIGGER_DEFAULT_TIMEOUT + int "Default timeout for await_trigger command in seconds" + default 10 + +config COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT + int "Max timeout for await_trigger command in seconds" + default 60 + +endif # COMPARATOR_SHELL diff --git a/drivers/comparator/comparator_fake_comp.c b/drivers/comparator/comparator_fake_comp.c new file mode 100644 index 00000000000..85abbad2174 --- /dev/null +++ b/drivers/comparator/comparator_fake_comp.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifdef CONFIG_ZTEST +#include +#endif + +#define DT_DRV_COMPAT zephyr_fake_comp + +DEFINE_FAKE_VALUE_FUNC(int, + comp_fake_comp_get_output, + const struct device *); + +DEFINE_FAKE_VALUE_FUNC(int, + comp_fake_comp_set_trigger, + const struct device *, + enum comparator_trigger); + +DEFINE_FAKE_VALUE_FUNC(int, + comp_fake_comp_set_trigger_callback, + const struct device *, + comparator_callback_t, + void *); + +DEFINE_FAKE_VALUE_FUNC(int, + comp_fake_comp_trigger_is_pending, + const struct device *); + +static const struct comparator_driver_api fake_comp_api = { + .get_output = comp_fake_comp_get_output, + .set_trigger = comp_fake_comp_set_trigger, + .set_trigger_callback = comp_fake_comp_set_trigger_callback, + .trigger_is_pending = comp_fake_comp_trigger_is_pending, +}; + +#ifdef CONFIG_ZTEST +static void fake_comp_reset_rule_before(const struct ztest_unit_test *test, void *fixture) +{ + ARG_UNUSED(test); + ARG_UNUSED(fixture); + + RESET_FAKE(comp_fake_comp_get_output); + RESET_FAKE(comp_fake_comp_set_trigger); + RESET_FAKE(comp_fake_comp_set_trigger_callback); + RESET_FAKE(comp_fake_comp_trigger_is_pending); +} + +ZTEST_RULE(comp_fake_comp_reset_rule, fake_comp_reset_rule_before, NULL); +#endif + +DEVICE_DT_INST_DEFINE( + 0, + NULL, + NULL, + NULL, + NULL, + POST_KERNEL, + CONFIG_COMPARATOR_INIT_PRIORITY, + &fake_comp_api +); diff --git a/drivers/comparator/comparator_handlers.c b/drivers/comparator/comparator_handlers.c new file mode 100644 index 00000000000..fba43a9ce8e --- /dev/null +++ b/drivers/comparator/comparator_handlers.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static inline int z_vrfy_comparator_get_output(const struct device *dev) +{ + K_OOPS(K_SYSCALL_DRIVER_COMPARATOR(dev, get_output)); + return z_impl_comparator_get_output(dev); +} +#include + +static inline int z_vrfy_comparator_set_trigger(const struct device *dev, + enum comparator_trigger trigger) +{ + K_OOPS(K_SYSCALL_DRIVER_COMPARATOR(dev, set_trigger)); + return z_impl_comparator_set_trigger(dev, trigger); +} +#include + +static inline int z_vrfy_comparator_trigger_is_pending(const struct device *dev) +{ + K_OOPS(K_SYSCALL_DRIVER_COMPARATOR(dev, trigger_is_pending)); + return z_impl_comparator_trigger_is_pending(dev); +} +#include diff --git a/drivers/comparator/comparator_mcux_acmp.c b/drivers/comparator/comparator_mcux_acmp.c new file mode 100644 index 00000000000..4d76f105e34 --- /dev/null +++ b/drivers/comparator/comparator_mcux_acmp.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2020 Vestas Wind Systems A/S + * Copyright (c) 2022 NXP + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(nxp_kinetis_acmp, CONFIG_COMPARATOR_LOG_LEVEL); + +#define DT_DRV_COMPAT nxp_kinetis_acmp + +/* + * DAC is a register defined in the MCUX HAL. We don't need it here and it conflicts + * with the COMP_MCUX_ACMP_PORT_INPUT_DAC definition so undef it here. + */ +#ifdef DAC +#undef DAC +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT) && (FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_OFFSET 1 +#else +#define COMP_MCUX_ACMP_HAS_OFFSET 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT) && (FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_HYSTERESIS 1 +#else +#define COMP_MCUX_ACMP_HAS_HYSTERESIS 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_INPSEL 1 +#else +#define COMP_MCUX_ACMP_HAS_INPSEL 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_INNSEL 1 +#else +#define COMP_MCUX_ACMP_HAS_INNSEL 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 1 +#else +#define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT == 1U) +#define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 1 +#else +#define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 0 +#endif + +#if defined(FSL_FEATURE_ACMP_HAS_C3_REG) && (FSL_FEATURE_ACMP_HAS_C3_REG != 0U) +#define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 1 +#else +#define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 0 +#endif + +#if !(defined(FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE) && (FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE == 1U)) +#define COMP_MCUX_ACMP_HAS_WINDOW_MODE 1 +#else +#define COMP_MCUX_ACMP_HAS_WINDOW_MODE 0 +#endif + +#define MCUX_ACMP_ENUM(name, value) \ + _CONCAT_4(COMP_MCUX_ACMP_, name, _, value) + +#define MCUX_ACMP_DT_INST_ENUM(inst, name, prop) \ + MCUX_ACMP_ENUM(name, DT_INST_STRING_TOKEN(inst, prop)) + +#define MCUX_ACMP_DT_INST_ENUM_OR(inst, name, prop, or) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prop), \ + (MCUX_ACMP_DT_INST_ENUM(inst, name, prop)), \ + (MCUX_ACMP_ENUM(name, or))) + +#define MCUX_ACMP_DT_INST_OFFSET_MODE(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, OFFSET_MODE, offset_mode, LEVEL0) + +#define MCUX_ACMP_DT_INST_HYST_MODE(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, HYSTERESIS_MODE, hysteresis_mode, LEVEL0) + +#define MCUX_ACMP_DT_INST_EN_HS_MODE(inst) \ + DT_INST_PROP(inst, enable_high_speed_mode) + +#define MCUX_ACMP_DT_INST_INV_OUT(inst) \ + DT_INST_PROP(inst, invert_output) + +#define MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst) \ + DT_INST_PROP(inst, use_unfiltered_output) + +#define MCUX_ACMP_DT_INST_EN_PIN_OUT(inst) \ + DT_INST_PROP(inst, enable_pin_out) + +#define MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst) \ + { \ + .offset_mode = MCUX_ACMP_DT_INST_OFFSET_MODE(inst), \ + .hysteresis_mode = MCUX_ACMP_DT_INST_HYST_MODE(inst), \ + .enable_high_speed_mode = MCUX_ACMP_DT_INST_EN_HS_MODE(inst), \ + .invert_output = MCUX_ACMP_DT_INST_INV_OUT(inst), \ + .use_unfiltered_output = MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst), \ + .enable_pin_output = MCUX_ACMP_DT_INST_EN_PIN_OUT(inst), \ + } + +#define MCUX_ACMP_DT_INST_P_MUX_IN(inst) \ + MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, positive_mux_input) + +#define MCUX_ACMP_DT_INST_N_MUX_IN(inst) \ + MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, negative_mux_input) + +#define MCUX_ACMP_DT_INST_P_PORT_IN(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, positive_port_input, MUX) + +#define MCUX_ACMP_DT_INST_N_PORT_IN(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, negative_port_input, MUX) + +#define MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst) \ + { \ + .positive_mux_input = MCUX_ACMP_DT_INST_P_MUX_IN(inst), \ + .negative_mux_input = MCUX_ACMP_DT_INST_N_MUX_IN(inst), \ + .positive_port_input = MCUX_ACMP_DT_INST_P_PORT_IN(inst), \ + .negative_port_input = MCUX_ACMP_DT_INST_N_PORT_IN(inst), \ + } + +#define MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst) \ + DT_INST_PROP(inst, filter_enable_sample) + +#define MCUX_ACMP_DT_INST_FILTER_COUNT(inst) \ + DT_INST_PROP_OR(inst, filter_count, 0) + +#define MCUX_ACMP_DT_INST_FILTER_PERIOD(inst) \ + DT_INST_PROP_OR(inst, filter_period, 0) + +#define MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst) \ + { \ + .enable_sample = MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst), \ + .filter_count = MCUX_ACMP_DT_INST_FILTER_COUNT(inst), \ + .filter_period = MCUX_ACMP_DT_INST_FILTER_PERIOD(inst), \ + } + +#define MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, DAC_VREF_SOURCE, dac_vref_source, VIN1) + +#define MCUX_ACMP_DT_INST_DAC_VALUE(inst) \ + DT_INST_PROP_OR(inst, dac_value, 0) + +#define MCUX_ACMP_DT_INST_DAC_EN(inst) \ + DT_INST_PROP(inst, dac_enable) + +#define MCUX_ACMP_DT_INST_DAC_EN_HS(inst) \ + DT_INST_PROP(inst, dac_enable_high_speed) + +#define MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst) \ + { \ + .vref_source = MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst), \ + .value = MCUX_ACMP_DT_INST_DAC_VALUE(inst), \ + .enable_output = MCUX_ACMP_DT_INST_DAC_EN(inst), \ + .enable_high_speed_mode = MCUX_ACMP_DT_INST_DAC_EN_HS(inst), \ + } + +#define MCUX_ACMP_DT_INST_DM_EN_P_CH(inst) \ + DT_INST_PROP(inst, discrete_mode_enable_positive_channel) + +#define MCUX_ACMP_DT_INST_DM_EN_N_CH(inst) \ + DT_INST_PROP(inst, discrete_mode_enable_negative_channel) + +#define MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst) \ + DT_INST_PROP(inst, discrete_mode_enable_resistor_divider) + +#define MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_CLOCK, discrete_mode_clock_source, SLOW) + +#define MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_SAMPLE_TIME, discrete_mode_sample_time, T1) + +#define MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase1_time, ALT0) + +#define MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst) \ + MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase2_time, ALT0) + +#define MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst) \ + { \ + .enable_positive_channel = MCUX_ACMP_DT_INST_DM_EN_P_CH(inst), \ + .enable_negative_channel = MCUX_ACMP_DT_INST_DM_EN_N_CH(inst), \ + .enable_resistor_divider = MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst), \ + .clock_source = MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst), \ + .sample_time = MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst), \ + .phase1_time = MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst), \ + .phase2_time = MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst), \ + } + +#define MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst) \ + DT_INST_PROP(inst, enable_window_mode) + +struct mcux_acmp_config { + CMP_Type *base; + const struct pinctrl_dev_config *pincfg; + void (*irq_init)(void); + const struct comp_mcux_acmp_mode_config mode_config; + const struct comp_mcux_acmp_input_config input_config; + const struct comp_mcux_acmp_filter_config filter_config; + const struct comp_mcux_acmp_dac_config dac_config; +#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE + const struct comp_mcux_acmp_dm_config dm_config; +#endif +#if COMP_MCUX_ACMP_HAS_WINDOW_MODE + bool enable_window_mode; +#endif +}; + +#if MCUX_ACMP_HAS_OFFSET +BUILD_ASSERT((int)kACMP_OffsetLevel0 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL0); +BUILD_ASSERT((int)kACMP_OffsetLevel1 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL1); +#endif + +#if COMP_MCUX_ACMP_HAS_HYSTERESIS +BUILD_ASSERT((int)kACMP_HysteresisLevel0 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL0); +BUILD_ASSERT((int)kACMP_HysteresisLevel1 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL1); +BUILD_ASSERT((int)kACMP_HysteresisLevel2 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL2); +BUILD_ASSERT((int)kACMP_HysteresisLevel3 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL3); +#endif + +BUILD_ASSERT((int)kACMP_VrefSourceVin1 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN1); +BUILD_ASSERT((int)kACMP_VrefSourceVin2 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN2); + +#if MCUX_ACMP_HAS_INPSEL || MCUX_ACMP_HAS_INNSEL +BUILD_ASSERT((int)kACMP_PortInputFromDAC == (int)COMP_MCUX_ACMP_PORT_INPUT_DAC); +BUILD_ASSERT((int)kACMP_PortInputFromMux == (int)COMP_MCUX_ACMP_PORT_INPUT_MUX); +#endif + +#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE +BUILD_ASSERT((int)kACMP_DiscreteClockSlow == (int)COMP_MCUX_ACMP_DM_CLOCK_SLOW); +BUILD_ASSERT((int)kACMP_DiscreteClockFast == (int)COMP_MCUX_ACMP_DM_CLOCK_FAST); + +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs1T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T1); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs2T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T2); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs4T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T4); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs8T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T8); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs16T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T16); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs32T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T32); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs64T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T64); +BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs256T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T256); + +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt0 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT0); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt1 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT1); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt2 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT2); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt3 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT3); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt4 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT4); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt5 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT5); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt6 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT6); +BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt7 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT7); +#endif + +struct mcux_acmp_data { + uint32_t interrupt_mask; + comparator_callback_t callback; + void *user_data; +}; + +#if CONFIG_PM_DEVICE +static bool mcux_acmp_is_resumed(const struct device *dev) +{ + enum pm_device_state state; + + (void)pm_device_state_get(dev, &state); + return state == PM_DEVICE_STATE_ACTIVE; +} +#else +static bool mcux_acmp_is_resumed(const struct device *dev) +{ + ARG_UNUSED(dev); + return true; +} +#endif + +static int mcux_acmp_get_output(const struct device *dev) +{ + const struct mcux_acmp_config *config = dev->config; + uint32_t status; + + status = ACMP_GetStatusFlags(config->base); + return (status & kACMP_OutputAssertEventFlag) ? 1 : 0; +} + +static int mcux_acmp_set_trigger(const struct device *dev, + enum comparator_trigger trigger) +{ + const struct mcux_acmp_config *config = dev->config; + struct mcux_acmp_data *data = dev->data; + + ACMP_DisableInterrupts(config->base, UINT32_MAX); + + switch (trigger) { + case COMPARATOR_TRIGGER_NONE: + data->interrupt_mask = 0; + break; + + case COMPARATOR_TRIGGER_RISING_EDGE: + data->interrupt_mask = kACMP_OutputRisingInterruptEnable; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + data->interrupt_mask = kACMP_OutputFallingInterruptEnable; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + data->interrupt_mask = kACMP_OutputFallingInterruptEnable | + kACMP_OutputRisingInterruptEnable; + break; + } + + if (data->interrupt_mask && data->callback != NULL) { + ACMP_EnableInterrupts(config->base, data->interrupt_mask); + } + + return 0; +} + +static int mcux_acmp_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + const struct mcux_acmp_config *config = dev->config; + struct mcux_acmp_data *data = dev->data; + + ACMP_DisableInterrupts(config->base, UINT32_MAX); + + data->callback = callback; + data->user_data = user_data; + + if (data->callback == NULL) { + return 0; + } + + if (data->interrupt_mask) { + ACMP_EnableInterrupts(config->base, data->interrupt_mask); + } + + return 0; +} + +static int mcux_acmp_trigger_is_pending(const struct device *dev) +{ + const struct mcux_acmp_config *config = dev->config; + struct mcux_acmp_data *data = dev->data; + uint32_t status_flags; + + status_flags = ACMP_GetStatusFlags(config->base); + ACMP_ClearStatusFlags(config->base, UINT32_MAX); + + if ((data->interrupt_mask & kACMP_OutputRisingInterruptEnable) && + (status_flags & kACMP_OutputRisingEventFlag)) { + return 1; + } + + if ((data->interrupt_mask & kACMP_OutputFallingInterruptEnable) && + (status_flags & kACMP_OutputFallingEventFlag)) { + return 1; + } + + return 0; +} + +static const struct comparator_driver_api mcux_acmp_comp_api = { + .get_output = mcux_acmp_get_output, + .set_trigger = mcux_acmp_set_trigger, + .set_trigger_callback = mcux_acmp_set_trigger_callback, + .trigger_is_pending = mcux_acmp_trigger_is_pending, +}; + +static void comp_mcux_acmp_init_mode_config(const struct device *dev, + const struct comp_mcux_acmp_mode_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + acmp_config_t acmp_config; + +#if COMP_MCUX_ACMP_HAS_OFFSET + acmp_config.offsetMode = (acmp_offset_mode_t)config->offset_mode; +#endif + +#if COMP_MCUX_ACMP_HAS_HYSTERESIS + acmp_config.hysteresisMode = (acmp_hysteresis_mode_t)config->hysteresis_mode; +#endif + + acmp_config.enableHighSpeed = config->enable_high_speed_mode; + acmp_config.enableInvertOutput = config->invert_output; + acmp_config.useUnfilteredOutput = config->use_unfiltered_output; + acmp_config.enablePinOut = config->enable_pin_output; + + ACMP_Init(dev_config->base, &acmp_config); +} + +int comp_mcux_acmp_set_mode_config(const struct device *dev, + const struct comp_mcux_acmp_mode_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + + comp_mcux_acmp_init_mode_config(dev, config); + + if (mcux_acmp_is_resumed(dev)) { + ACMP_Enable(dev_config->base, true); + } + + return 0; +} + +int comp_mcux_acmp_set_input_config(const struct device *dev, + const struct comp_mcux_acmp_input_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + acmp_channel_config_t acmp_channel_config; + +#if COMP_MCUX_ACMP_HAS_INPSEL + acmp_channel_config.positivePortInput = (acmp_port_input_t)config->positive_port_input; +#endif + + acmp_channel_config.plusMuxInput = (uint32_t)config->positive_mux_input; + +#if COMP_MCUX_ACMP_HAS_INNSEL + acmp_channel_config.negativePortInput = (acmp_port_input_t)config->negative_port_input; +#endif + + acmp_channel_config.minusMuxInput = (uint32_t)config->negative_mux_input; + + ACMP_SetChannelConfig(dev_config->base, &acmp_channel_config); + return 0; +} + +int comp_mcux_acmp_set_filter_config(const struct device *dev, + const struct comp_mcux_acmp_filter_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + acmp_filter_config_t acmp_filter_config; + + if (config->enable_sample && config->filter_count == 0) { + return -EINVAL; + } + + if (config->filter_count > 7) { + return -EINVAL; + } + + acmp_filter_config.enableSample = config->enable_sample; + acmp_filter_config.filterCount = config->filter_count; + acmp_filter_config.filterPeriod = config->filter_period; + + ACMP_SetFilterConfig(dev_config->base, &acmp_filter_config); + return 0; +} + +int comp_mcux_acmp_set_dac_config(const struct device *dev, + const struct comp_mcux_acmp_dac_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + acmp_dac_config_t acmp_dac_config; + + acmp_dac_config.referenceVoltageSource = + (acmp_reference_voltage_source_t)config->vref_source; + + acmp_dac_config.DACValue = config->value; + +#if COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE + acmp_dac_config.enableOutput = config->enable_output; +#endif + +#if COMP_MCUX_ACMP_HAS_DAC_WORK_MODE + acmp_dac_config.workMode = config->enable_high_speed_mode + ? kACMP_DACWorkHighSpeedMode + : kACMP_DACWorkLowSpeedMode; +#endif + + ACMP_SetDACConfig(dev_config->base, &acmp_dac_config); + return 0; +} + +#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE +int comp_mcux_acmp_set_dm_config(const struct device *dev, + const struct comp_mcux_acmp_dm_config *config) +{ + const struct mcux_acmp_config *dev_config = dev->config; + acmp_discrete_mode_config_t acmp_dm_config; + + acmp_dm_config.enablePositiveChannelDiscreteMode = config->enable_positive_channel; + acmp_dm_config.enableNegativeChannelDiscreteMode = config->enable_negative_channel; + acmp_dm_config.enableResistorDivider = config->enable_resistor_divider; + acmp_dm_config.clockSource = (acmp_discrete_clock_source_t)config->clock_source; + acmp_dm_config.sampleTime = (acmp_discrete_sample_time_t)config->sample_time; + acmp_dm_config.phase1Time = (acmp_discrete_phase_time_t)config->phase1_time; + acmp_dm_config.phase2Time = (acmp_discrete_phase_time_t)config->phase2_time; + + ACMP_SetDiscreteModeConfig(dev_config->base, &acmp_dm_config); + return 0; +} +#endif + +#if COMP_MCUX_ACMP_HAS_WINDOW_MODE +int comp_mcux_acmp_set_window_mode(const struct device *dev, bool enable) +{ + const struct mcux_acmp_config *config = dev->config; + + ACMP_EnableWindowMode(config->base, enable); + return 0; +} +#endif + +static int mcux_acmp_pm_callback(const struct device *dev, enum pm_device_action action) +{ + const struct mcux_acmp_config *config = dev->config; + + if (action == PM_DEVICE_ACTION_RESUME) { + ACMP_Enable(config->base, true); + } + +#if CONFIG_PM_DEVICE + if (action == PM_DEVICE_ACTION_SUSPEND) { + ACMP_Enable(config->base, false); + } +#endif + + return 0; +} + +static void mcux_acmp_irq_handler(const struct device *dev) +{ + const struct mcux_acmp_config *config = dev->config; + struct mcux_acmp_data *data = dev->data; + + ACMP_ClearStatusFlags(config->base, UINT32_MAX); + + if (data->callback == NULL) { + return; + } + + data->callback(dev, data->user_data); +} + +static int mcux_acmp_init(const struct device *dev) +{ + const struct mcux_acmp_config *config = dev->config; + int ret; + + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret) { + LOG_ERR("failed to set %s", "pincfg"); + return ret; + } + + comp_mcux_acmp_init_mode_config(dev, &config->mode_config); + + ret = comp_mcux_acmp_set_input_config(dev, &config->input_config); + if (ret) { + LOG_ERR("failed to set %s", "input config"); + return ret; + } + + ret = comp_mcux_acmp_set_filter_config(dev, &config->filter_config); + if (ret) { + LOG_ERR("failed to set %s", "filter config"); + return ret; + } + + ret = comp_mcux_acmp_set_dac_config(dev, &config->dac_config); + if (ret) { + LOG_ERR("failed to set %s", "dac config"); + return ret; + } + +#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE + ret = comp_mcux_acmp_set_dm_config(dev, &config->dm_config); + if (ret) { + LOG_ERR("failed to set %s", "discrete mode config"); + return ret; + } +#endif + +#if COMP_MCUX_ACMP_HAS_WINDOW_MODE + ret = comp_mcux_acmp_set_window_mode(dev, config->enable_window_mode); + if (ret) { + LOG_ERR("failed to set %s", "window mode"); + return ret; + } +#endif + + ACMP_DisableInterrupts(config->base, UINT32_MAX); + config->irq_init(); + + return pm_device_driver_init(dev, mcux_acmp_pm_callback); +} + +#define MCUX_ACMP_IRQ_HANDLER_SYM(inst) \ + _CONCAT(mcux_acmp_irq_init, inst) + +#define MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \ + static void MCUX_ACMP_IRQ_HANDLER_SYM(inst)(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), \ + DT_INST_IRQ(inst, priority), \ + mcux_acmp_irq_handler, \ + DEVICE_DT_INST_GET(inst), \ + 0); \ + \ + irq_enable(DT_INST_IRQN(inst)); \ + } + +#define MCUX_ACMP_DEVICE(inst) \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static struct mcux_acmp_data _CONCAT(data, inst); \ + \ + MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \ + \ + static const struct mcux_acmp_config _CONCAT(config, inst) = { \ + .base = (CMP_Type *)DT_INST_REG_ADDR(inst), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .irq_init = MCUX_ACMP_IRQ_HANDLER_SYM(inst), \ + .mode_config = MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst), \ + .input_config = MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst), \ + .filter_config = MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst), \ + .dac_config = MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst), \ + IF_ENABLED(COMP_MCUX_ACMP_HAS_DISCRETE_MODE, \ + (.dm_config = MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst),)) \ + IF_ENABLED(COMP_MCUX_ACMP_HAS_WINDOW_MODE, \ + (.enable_window_mode = MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst),)) \ + }; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, mcux_acmp_pm_callback); \ + \ + DEVICE_DT_INST_DEFINE(inst, \ + mcux_acmp_init, \ + PM_DEVICE_DT_INST_GET(inst), \ + &_CONCAT(data, inst), \ + &_CONCAT(config, inst), \ + POST_KERNEL, \ + CONFIG_COMPARATOR_INIT_PRIORITY, \ + &mcux_acmp_comp_api); + +DT_INST_FOREACH_STATUS_OKAY(MCUX_ACMP_DEVICE) diff --git a/drivers/comparator/comparator_nrf_comp.c b/drivers/comparator/comparator_nrf_comp.c new file mode 100644 index 00000000000..339d24f8dcd --- /dev/null +++ b/drivers/comparator/comparator_nrf_comp.c @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#define DT_DRV_COMPAT nordic_nrf_comp + +#define SHIM_NRF_COMP_DT_INST_REFSEL(inst) \ + _CONCAT(COMP_NRF_COMP_REFSEL_, DT_INST_STRING_TOKEN(inst, refsel)) + +#define SHIM_NRF_COMP_DT_INST_REFSEL_IS_AREF(inst) \ + DT_INST_ENUM_HAS_VALUE(inst, refsel, AREF) + +#define SHIM_NRF_COMP_DT_INST_EXTREFSEL(inst) \ + _CONCAT(COMP_NRF_COMP_EXTREFSEL_, DT_INST_STRING_TOKEN(inst, extrefsel)) + +#define SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_SE(inst) \ + DT_INST_ENUM_HAS_VALUE(inst, main_mode, SE) + +#define SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_DIFF(inst) \ + DT_INST_ENUM_HAS_VALUE(inst, main_mode, DIFF) + +#define SHIM_NRF_COMP_DT_INST_TH_DOWN(inst) \ + DT_INST_PROP(inst, th_down) + +#define SHIM_NRF_COMP_DT_INST_TH_UP(inst) \ + DT_INST_PROP(inst, th_up) + +#define SHIM_NRF_COMP_DT_INST_SP_MODE(inst) \ + _CONCAT(COMP_NRF_COMP_SP_MODE_, DT_INST_STRING_TOKEN(inst, sp_mode)) + +#define SHIM_NRF_COMP_DT_INST_ENABLE_HYST(inst) \ + DT_INST_PROP(inst, enable_hyst) + +#define SHIM_NRF_COMP_DT_INST_ISOURCE(inst) \ + _CONCAT(COMP_NRF_COMP_ISOURCE_, DT_INST_STRING_TOKEN(inst, isource)) + +#define SHIM_NRF_COMP_DT_INST_PSEL(inst) \ + _CONCAT(COMP_NRF_COMP_PSEL_, DT_INST_STRING_TOKEN(inst, psel)) + +#if defined(COMP_HYST_HYST_Hyst40mV) +#define NRF_COMP_HYST_ENABLED NRF_COMP_HYST_40MV +#elif defined(COMP_HYST_HYST_Hyst50mV) +#define NRF_COMP_HYST_ENABLED NRF_COMP_HYST_50MV +#endif + +#define NRF_COMP_HYST_DISABLED NRF_COMP_HYST_NO_HYST + +#if defined(NRF_COMP_HYST_ENABLED) +#define NRF_COMP_HAS_HYST 1 +#else +#define NRF_COMP_HAS_HYST 0 +#endif + +struct shim_nrf_comp_data { + uint32_t event_mask; + bool started; + atomic_t triggered; + comparator_callback_t callback; + void *user_data; +}; + +#if (NRF_COMP_HAS_AIN_AS_PIN) +static const uint32_t shim_nrf_comp_ain_map[] = { +#if defined(CONFIG_SOC_NRF54H20) || defined(CONFIG_SOC_NRF9280) + NRF_PIN_PORT_TO_PIN_NUMBER(0U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(1U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(2U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(3U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1), +#elif defined(CONFIG_SOC_NRF54L15) + NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(11U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(12U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(13U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(14U, 1), +#endif +}; +#endif + +#if SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_SE(0) +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_TH_DOWN(0) < 64); +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_TH_UP(0) < 64); +#endif + +#if NRF_COMP_HAS_AIN_AS_PIN +BUILD_ASSERT((COMP_NRF_COMP_PSEL_AIN0 == 0)); +BUILD_ASSERT((COMP_NRF_COMP_PSEL_AIN7 == 7)); +BUILD_ASSERT((COMP_NRF_COMP_EXTREFSEL_AIN0 == 0)); +BUILD_ASSERT((COMP_NRF_COMP_EXTREFSEL_AIN7 == 7)); +#else +#ifndef COMP_PSEL_PSEL_AnalogInput4 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_AIN4); +#endif + +#ifndef COMP_PSEL_PSEL_AnalogInput5 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_AIN5); +#endif + +#ifndef COMP_PSEL_PSEL_AnalogInput6 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_AIN6); +#endif + +#ifndef COMP_PSEL_PSEL_AnalogInput7 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_AIN7); +#endif +#endif + +#ifndef COMP_PSEL_PSEL_VddDiv2 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_VDD_DIV2); +#endif + +#ifndef COMP_PSEL_PSEL_VddhDiv5 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_PSEL(0) != COMP_NRF_COMP_PSEL_VDDH_DIV5); +#endif + +#ifndef COMP_MODE_SP_Normal +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_SP_MODE(0) != COMP_NRF_COMP_SP_MODE_NORMAL); +#endif + +#if NRF_COMP_HAS_ISOURCE +#ifndef COMP_ISOURCE_ISOURCE_Ien2uA5 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_ISOURCE(0) != COMP_NRF_COMP_ISOURCE_2UA5); +#endif + +#ifndef COMP_ISOURCE_ISOURCE_Ien5uA +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_ISOURCE(0) != COMP_NRF_COMP_ISOURCE_5UA); +#endif + +#ifndef COMP_ISOURCE_ISOURCE_Ien10uA +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_ISOURCE(0) != COMP_NRF_COMP_ISOURCE_10UA); +#endif +#endif + +#if SHIM_NRF_COMP_DT_INST_REFSEL_IS_AREF(0) +#ifndef COMP_EXTREFSEL_EXTREFSEL_AnalogReference4 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_EXTREFSEL(0) != COMP_NRF_COMP_EXTREFSEL_AIN4); +#endif + +#ifndef COMP_EXTREFSEL_EXTREFSEL_AnalogReference5 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_EXTREFSEL(0) != COMP_NRF_COMP_EXTREFSEL_AIN5); +#endif + +#ifndef COMP_EXTREFSEL_EXTREFSEL_AnalogReference6 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_EXTREFSEL(0) != COMP_NRF_COMP_EXTREFSEL_AIN6); +#endif + +#ifndef COMP_EXTREFSEL_EXTREFSEL_AnalogReference7 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_EXTREFSEL(0) != COMP_NRF_COMP_EXTREFSEL_AIN7); +#endif +#endif + +#if SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_SE(0) +#ifndef COMP_REFSEL_REFSEL_Int1V8 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_REFSEL(0) != COMP_NRF_COMP_REFSEL_INT_1V8); +#endif + +#ifndef COMP_REFSEL_REFSEL_Int2V4 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_REFSEL(0) != COMP_NRF_COMP_REFSEL_INT_2V4); +#endif + +#ifndef COMP_REFSEL_REFSEL_AVDDAO1V8 +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_REFSEL(0) != COMP_NRF_COMP_REFSEL_AVDDAO1V8); +#endif + +#ifndef COMP_REFSEL_REFSEL_VDD +BUILD_ASSERT(SHIM_NRF_COMP_DT_INST_REFSEL(0) != COMP_NRF_COMP_REFSEL_VDD); +#endif +#endif + +#if SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_DIFF(0) +#if SHIM_NRF_COMP_DT_INST_ENABLE_HYST(0) +BUILD_ASSERT(NRF_COMP_HAS_HYST); +#endif +#endif + +#if SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_SE(0) +static const struct comp_nrf_comp_se_config shim_nrf_comp_config0 = { + .psel = SHIM_NRF_COMP_DT_INST_PSEL(0), + .sp_mode = SHIM_NRF_COMP_DT_INST_SP_MODE(0), + .isource = SHIM_NRF_COMP_DT_INST_ISOURCE(0), +#if SHIM_NRF_COMP_DT_INST_REFSEL_IS_AREF(0) + .extrefsel = SHIM_NRF_COMP_DT_INST_EXTREFSEL(0), +#endif + .refsel = SHIM_NRF_COMP_DT_INST_REFSEL(0), + .th_down = SHIM_NRF_COMP_DT_INST_TH_DOWN(0), + .th_up = SHIM_NRF_COMP_DT_INST_TH_UP(0), +}; +#else +static const struct comp_nrf_comp_diff_config shim_nrf_comp_config0 = { + .psel = SHIM_NRF_COMP_DT_INST_PSEL(0), + .sp_mode = SHIM_NRF_COMP_DT_INST_SP_MODE(0), + .isource = SHIM_NRF_COMP_DT_INST_ISOURCE(0), + .extrefsel = SHIM_NRF_COMP_DT_INST_EXTREFSEL(0), + .enable_hyst = SHIM_NRF_COMP_DT_INST_ENABLE_HYST(0), +}; +#endif + +static struct shim_nrf_comp_data shim_nrf_comp_data0; + +#if CONFIG_PM_DEVICE +static bool shim_nrf_comp_is_resumed(void) +{ + enum pm_device_state state; + + (void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state); + return state == PM_DEVICE_STATE_ACTIVE; +} +#else +static bool shim_nrf_comp_is_resumed(void) +{ + return true; +} +#endif + +static void shim_nrf_comp_start(void) +{ + if (shim_nrf_comp_data0.started) { + return; + } + + nrfx_comp_start(shim_nrf_comp_data0.event_mask, 0); + shim_nrf_comp_data0.started = true; +} + +static void shim_nrf_comp_stop(void) +{ + if (!shim_nrf_comp_data0.started) { + return; + } + + nrfx_comp_stop(); + shim_nrf_comp_data0.started = false; +} + +static int shim_nrf_comp_pm_callback(const struct device *dev, enum pm_device_action action) +{ + ARG_UNUSED(dev); + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + shim_nrf_comp_start(); + break; + +#if CONFIG_PM_DEVICE + case PM_DEVICE_ACTION_SUSPEND: + shim_nrf_comp_stop(); + break; +#endif + + default: + return -ENOTSUP; + } + + return 0; +} + +#if (NRF_COMP_HAS_AIN_AS_PIN) +static int shim_nrf_comp_psel_to_nrf(enum comp_nrf_comp_psel shim, + nrf_comp_input_t *nrf) +{ + if (shim >= ARRAY_SIZE(shim_nrf_comp_ain_map)) { + return -EINVAL; + } + + *nrf = shim_nrf_comp_ain_map[(uint32_t)shim]; + return 0; +} +#else +static int shim_nrf_comp_psel_to_nrf(enum comp_nrf_comp_psel shim, + nrf_comp_input_t *nrf) +{ + switch (shim) { + case COMP_NRF_COMP_PSEL_AIN0: + *nrf = NRF_COMP_INPUT_0; + break; + + case COMP_NRF_COMP_PSEL_AIN1: + *nrf = NRF_COMP_INPUT_1; + break; + + case COMP_NRF_COMP_PSEL_AIN2: + *nrf = NRF_COMP_INPUT_2; + break; + + case COMP_NRF_COMP_PSEL_AIN3: + *nrf = NRF_COMP_INPUT_3; + break; + +#if defined(COMP_PSEL_PSEL_AnalogInput4) + case COMP_NRF_COMP_PSEL_AIN4: + *nrf = NRF_COMP_INPUT_4; + break; +#endif + +#if defined(COMP_PSEL_PSEL_AnalogInput5) + case COMP_NRF_COMP_PSEL_AIN5: + *nrf = NRF_COMP_INPUT_5; + break; +#endif + +#if defined(COMP_PSEL_PSEL_AnalogInput6) + case COMP_NRF_COMP_PSEL_AIN6: + *nrf = NRF_COMP_INPUT_6; + break; +#endif + +#if defined(COMP_PSEL_PSEL_AnalogInput7) + case COMP_NRF_COMP_PSEL_AIN7: + *nrf = NRF_COMP_INPUT_7; + break; +#endif + +#if defined(COMP_PSEL_PSEL_VddDiv2) + case COMP_NRF_COMP_PSEL_VDD_DIV2: + *nrf = NRF_COMP_VDD_DIV2; + break; +#endif + +#if defined(COMP_PSEL_PSEL_VddhDiv5) + case COMP_NRF_COMP_PSEL_VDDH_DIV5: + *nrf = NRF_COMP_VDDH_DIV5; + break; +#endif + + default: + return -EINVAL; + } + + return 0; +} +#endif + +static int shim_nrf_comp_sp_mode_to_nrf(enum comp_nrf_comp_sp_mode shim, + nrf_comp_sp_mode_t *nrf) +{ + switch (shim) { + case COMP_NRF_COMP_SP_MODE_LOW: + *nrf = NRF_COMP_SP_MODE_LOW; + break; + +#if defined(COMP_MODE_SP_Normal) + case COMP_NRF_COMP_SP_MODE_NORMAL: + *nrf = NRF_COMP_SP_MODE_NORMAL; + break; +#endif + + case COMP_NRF_COMP_SP_MODE_HIGH: + *nrf = NRF_COMP_SP_MODE_HIGH; + break; + + default: + return -EINVAL; + } + + return 0; +} + +#if NRF_COMP_HAS_ISOURCE +static int shim_nrf_comp_isource_to_nrf(enum comp_nrf_comp_isource shim, + nrf_isource_t *nrf) +{ + switch (shim) { + case COMP_NRF_COMP_ISOURCE_DISABLED: + *nrf = NRF_COMP_ISOURCE_OFF; + break; + +#if defined(COMP_ISOURCE_ISOURCE_Ien2uA5) + case COMP_NRF_COMP_ISOURCE_2UA5: + *nrf = NRF_COMP_ISOURCE_IEN_2UA5; + break; +#endif + +#if defined(COMP_ISOURCE_ISOURCE_Ien5uA) + case COMP_NRF_COMP_ISOURCE_5UA: + *nrf = NRF_COMP_ISOURCE_IEN_5UA; + break; +#endif + +#if defined(COMP_ISOURCE_ISOURCE_Ien10uA) + case COMP_NRF_COMP_ISOURCE_10UA: + *nrf = NRF_COMP_ISOURCE_IEN_10UA; + break; +#endif + + default: + return -EINVAL; + } + + return 0; +} +#endif + +#if (NRF_COMP_HAS_AIN_AS_PIN) +static int shim_nrf_comp_extrefsel_to_nrf(enum comp_nrf_comp_extrefsel shim, + nrf_comp_ext_ref_t *nrf) +{ + if (shim >= ARRAY_SIZE(shim_nrf_comp_ain_map)) { + return -EINVAL; + } + + *nrf = shim_nrf_comp_ain_map[(uint32_t)shim]; + return 0; +} +#else +static int shim_nrf_comp_extrefsel_to_nrf(enum comp_nrf_comp_extrefsel shim, + nrf_comp_ext_ref_t *nrf) +{ + switch (shim) { + case COMP_NRF_COMP_EXTREFSEL_AIN0: + *nrf = NRF_COMP_EXT_REF_0; + break; + + case COMP_NRF_COMP_EXTREFSEL_AIN1: + *nrf = NRF_COMP_EXT_REF_1; + break; + + case COMP_NRF_COMP_EXTREFSEL_AIN2: + *nrf = NRF_COMP_EXT_REF_2; + break; + + case COMP_NRF_COMP_EXTREFSEL_AIN3: + *nrf = NRF_COMP_EXT_REF_3; + break; + +#if defined(COMP_EXTREFSEL_EXTREFSEL_AnalogReference4) + case COMP_NRF_COMP_EXTREFSEL_AIN4: + *nrf = NRF_COMP_EXT_REF_4; + break; +#endif + +#if defined(COMP_EXTREFSEL_EXTREFSEL_AnalogReference5) + case COMP_NRF_COMP_EXTREFSEL_AIN5: + *nrf = NRF_COMP_EXT_REF_5; + break; +#endif + +#if defined(COMP_EXTREFSEL_EXTREFSEL_AnalogReference6) + case COMP_NRF_COMP_EXTREFSEL_AIN6: + *nrf = NRF_COMP_EXT_REF_6; + break; +#endif + +#if defined(COMP_EXTREFSEL_EXTREFSEL_AnalogReference7) + case COMP_NRF_COMP_EXTREFSEL_AIN7: + *nrf = NRF_COMP_EXT_REF_7; + break; +#endif + + default: + return -EINVAL; + } + + return 0; +} +#endif + +static int shim_nrf_comp_refsel_to_nrf(enum comp_nrf_comp_refsel shim, + nrf_comp_ref_t *nrf) +{ + switch (shim) { + case COMP_NRF_COMP_REFSEL_INT_1V2: + *nrf = NRF_COMP_REF_INT_1V2; + break; + +#if defined(COMP_REFSEL_REFSEL_Int1V8) + case COMP_NRF_COMP_REFSEL_INT_1V8: + *nrf = NRF_COMP_REF_INT_1V8; + break; +#endif + +#if defined(COMP_REFSEL_REFSEL_Int2V4) + case COMP_NRF_COMP_REFSEL_INT_2V4: + *nrf = NRF_COMP_REF_INT_2V4; + break; +#endif + +#if defined(COMP_REFSEL_REFSEL_AVDDAO1V8) + case COMP_NRF_COMP_REFSEL_AVDDAO1V8: + *nrf = NRF_COMP_REF_AVDDAO1V8; + break; +#endif + +#if defined(COMP_REFSEL_REFSEL_VDD) + case COMP_NRF_COMP_REFSEL_VDD: + *nrf = NRF_COMP_REF_VDD; + break; +#endif + + case COMP_NRF_COMP_REFSEL_AREF: + *nrf = NRF_COMP_REF_AREF; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int shim_nrf_comp_se_config_to_nrf(const struct comp_nrf_comp_se_config *shim, + nrfx_comp_config_t *nrf) +{ + if (shim_nrf_comp_refsel_to_nrf(shim->refsel, &nrf->reference)) { + return -EINVAL; + } + + if (shim_nrf_comp_extrefsel_to_nrf(shim->extrefsel, &nrf->ext_ref)) { + return -EINVAL; + } + + nrf->main_mode = NRF_COMP_MAIN_MODE_SE; + + if (shim->th_down > 63 || shim->th_up > 63) { + return -EINVAL; + } + + nrf->threshold.th_down = shim->th_down; + nrf->threshold.th_up = shim->th_up; + + if (shim_nrf_comp_sp_mode_to_nrf(shim->sp_mode, &nrf->speed_mode)) { + return -EINVAL; + } + + nrf->hyst = NRF_COMP_HYST_NO_HYST; + +#if NRF_COMP_HAS_ISOURCE + if (shim_nrf_comp_isource_to_nrf(shim->isource, &nrf->isource)) { + return -EINVAL; + } +#else + if (shim->isource != COMP_NRF_COMP_ISOURCE_DISABLED) { + return -EINVAL; + } +#endif + + if (shim_nrf_comp_psel_to_nrf(shim->psel, &nrf->input)) { + return -EINVAL; + } + + nrf->interrupt_priority = 0; + return 0; +} + +static int shim_nrf_comp_diff_config_to_nrf(const struct comp_nrf_comp_diff_config *shim, + nrfx_comp_config_t *nrf) +{ + nrf->reference = NRF_COMP_REF_AREF; + + if (shim_nrf_comp_extrefsel_to_nrf(shim->extrefsel, &nrf->ext_ref)) { + return -EINVAL; + } + + nrf->main_mode = NRF_COMP_MAIN_MODE_DIFF; + nrf->threshold.th_down = 0; + nrf->threshold.th_up = 0; + + if (shim_nrf_comp_sp_mode_to_nrf(shim->sp_mode, &nrf->speed_mode)) { + return -EINVAL; + } + +#if NRF_COMP_HAS_HYST + if (shim->enable_hyst) { + nrf->hyst = NRF_COMP_HYST_ENABLED; + } else { + nrf->hyst = NRF_COMP_HYST_DISABLED; + } +#else + if (shim->enable_hyst) { + return -EINVAL; + } +#endif + +#if NRF_COMP_HAS_ISOURCE + if (shim_nrf_comp_isource_to_nrf(shim->isource, &nrf->isource)) { + return -EINVAL; + } +#else + if (shim->isource != COMP_NRF_COMP_ISOURCE_DISABLED) { + return -EINVAL; + } +#endif + + if (shim_nrf_comp_psel_to_nrf(shim->psel, &nrf->input)) { + return -EINVAL; + } + + nrf->interrupt_priority = 0; + return 0; +} + +static int shim_nrf_comp_get_output(const struct device *dev) +{ + ARG_UNUSED(dev); + + return nrfx_comp_sample(); +} + +static int shim_nrf_comp_set_trigger(const struct device *dev, + enum comparator_trigger trigger) +{ + shim_nrf_comp_stop(); + + switch (trigger) { + case COMPARATOR_TRIGGER_NONE: + shim_nrf_comp_data0.event_mask = 0; + break; + + case COMPARATOR_TRIGGER_RISING_EDGE: + shim_nrf_comp_data0.event_mask = NRF_COMP_INT_UP_MASK; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + shim_nrf_comp_data0.event_mask = NRF_COMP_INT_DOWN_MASK; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + shim_nrf_comp_data0.event_mask = NRF_COMP_INT_CROSS_MASK; + break; + } + + if (shim_nrf_comp_is_resumed()) { + shim_nrf_comp_start(); + } + + return 0; +} + +static int shim_nrf_comp_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + shim_nrf_comp_stop(); + + shim_nrf_comp_data0.callback = callback; + shim_nrf_comp_data0.user_data = user_data; + + if (callback != NULL && atomic_test_and_clear_bit(&shim_nrf_comp_data0.triggered, 0)) { + callback(dev, user_data); + } + + if (shim_nrf_comp_is_resumed()) { + shim_nrf_comp_start(); + } + + return 0; +} + +static int shim_nrf_comp_trigger_is_pending(const struct device *dev) +{ + ARG_UNUSED(dev); + + return atomic_test_and_clear_bit(&shim_nrf_comp_data0.triggered, 0); +} + +static const struct comparator_driver_api shim_nrf_comp_api = { + .get_output = shim_nrf_comp_get_output, + .set_trigger = shim_nrf_comp_set_trigger, + .set_trigger_callback = shim_nrf_comp_set_trigger_callback, + .trigger_is_pending = shim_nrf_comp_trigger_is_pending, +}; + +static int shim_nrf_comp_reconfigure(const nrfx_comp_config_t *nrf) +{ + shim_nrf_comp_stop(); + + (void)nrfx_comp_reconfigure(nrf); + + if (shim_nrf_comp_is_resumed()) { + shim_nrf_comp_start(); + } + + return 0; +} + +int comp_nrf_comp_configure_se(const struct device *dev, + const struct comp_nrf_comp_se_config *config) +{ + nrfx_comp_config_t nrf = {}; + + ARG_UNUSED(dev); + + if (shim_nrf_comp_se_config_to_nrf(config, &nrf)) { + return -EINVAL; + } + + return shim_nrf_comp_reconfigure(&nrf); +} + +int comp_nrf_comp_configure_diff(const struct device *dev, + const struct comp_nrf_comp_diff_config *config) +{ + nrfx_comp_config_t nrf = {}; + + ARG_UNUSED(dev); + + if (shim_nrf_comp_diff_config_to_nrf(config, &nrf)) { + return -EINVAL; + } + + return shim_nrf_comp_reconfigure(&nrf); +} + +static void shim_nrf_comp_event_handler(nrf_comp_event_t event) +{ + ARG_UNUSED(event); + + if (shim_nrf_comp_data0.callback == NULL) { + atomic_set_bit(&shim_nrf_comp_data0.triggered, 0); + return; + } + + shim_nrf_comp_data0.callback(DEVICE_DT_INST_GET(0), shim_nrf_comp_data0.user_data); + atomic_clear_bit(&shim_nrf_comp_data0.triggered, 0); +} + +static int shim_nrf_comp_init(const struct device *dev) +{ + nrfx_comp_config_t nrf = {}; + + IRQ_CONNECT(DT_INST_IRQN(0), + DT_INST_IRQ(0, priority), + nrfx_isr, + nrfx_comp_irq_handler, + 0); + + irq_enable(DT_INST_IRQN(0)); + +#if SHIM_NRF_COMP_DT_INST_MAIN_MODE_IS_SE(0) + (void)shim_nrf_comp_se_config_to_nrf(&shim_nrf_comp_config0, &nrf); +#else + (void)shim_nrf_comp_diff_config_to_nrf(&shim_nrf_comp_config0, &nrf); +#endif + + if (nrfx_comp_init(&nrf, shim_nrf_comp_event_handler) != NRFX_SUCCESS) { + return -ENODEV; + } + + return pm_device_driver_init(dev, shim_nrf_comp_pm_callback); +} + +PM_DEVICE_DT_INST_DEFINE(0, shim_nrf_comp_pm_callback); + +DEVICE_DT_INST_DEFINE(0, + shim_nrf_comp_init, + PM_DEVICE_DT_INST_GET(0), + NULL, + NULL, + POST_KERNEL, + CONFIG_COMPARATOR_INIT_PRIORITY, + &shim_nrf_comp_api); diff --git a/drivers/comparator/comparator_nrf_lpcomp.c b/drivers/comparator/comparator_nrf_lpcomp.c new file mode 100644 index 00000000000..6c6710d337d --- /dev/null +++ b/drivers/comparator/comparator_nrf_lpcomp.c @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include + +#define DT_DRV_COMPAT nordic_nrf_lpcomp + +#define SHIM_NRF_LPCOMP_DT_INST_REFSEL(inst) \ + _CONCAT(COMP_NRF_LPCOMP_REFSEL_, DT_INST_STRING_TOKEN(inst, refsel)) + +#define SHIM_NRF_LPCOMP_DT_INST_REFSEL_IS_AREF(inst) \ + DT_INST_ENUM_HAS_VALUE(inst, refsel, AREF) + +#define SHIM_NRF_LPCOMP_DT_INST_EXTREFSEL(inst) \ + _CONCAT(COMP_NRF_LPCOMP_EXTREFSEL_, DT_INST_STRING_TOKEN(inst, extrefsel)) + +#define SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(inst) \ + DT_INST_PROP(inst, enable_hyst) + +#define SHIM_NRF_LPCOMP_DT_INST_PSEL(inst) \ + _CONCAT(COMP_NRF_LPCOMP_PSEL_, DT_INST_STRING_TOKEN(inst, psel)) + +struct shim_nrf_lpcomp_data { + nrfx_lpcomp_config_t config; + uint32_t event_mask; + bool started; + atomic_t triggered; + comparator_callback_t callback; + void *user_data; +}; + +#if (NRF_LPCOMP_HAS_AIN_AS_PIN) +static const uint32_t shim_nrf_lpcomp_ain_map[] = { +#if defined(CONFIG_SOC_NRF54H20) || defined(CONFIG_SOC_NRF9280) + NRF_PIN_PORT_TO_PIN_NUMBER(0U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(1U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(2U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(3U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1), +#elif defined(CONFIG_SOC_NRF54L15) + NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(11U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(12U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(13U, 1), + NRF_PIN_PORT_TO_PIN_NUMBER(14U, 1), +#endif +}; +#endif + +#if (NRF_LPCOMP_HAS_AIN_AS_PIN) +BUILD_ASSERT(COMP_NRF_LPCOMP_PSEL_AIN0 == 0); +BUILD_ASSERT(COMP_NRF_LPCOMP_PSEL_AIN7 == 7); +BUILD_ASSERT(COMP_NRF_LPCOMP_EXTREFSEL_AIN0 == 0); +BUILD_ASSERT(COMP_NRF_LPCOMP_EXTREFSEL_AIN1 == 1); +#endif + +#if (LPCOMP_REFSEL_RESOLUTION == 8) +BUILD_ASSERT((SHIM_NRF_LPCOMP_DT_INST_REFSEL(0) < COMP_NRF_LPCOMP_REFSEL_VDD_1_16) || + (SHIM_NRF_LPCOMP_DT_INST_REFSEL(0) > COMP_NRF_LPCOMP_REFSEL_VDD_15_16)); +#endif + +#if SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(0) +BUILD_ASSERT(NRF_LPCOMP_HAS_HYST); +#endif + +static struct shim_nrf_lpcomp_data shim_nrf_lpcomp_data0; + +static const struct comp_nrf_lpcomp_config shim_nrf_lpcomp_config0 = { + .psel = SHIM_NRF_LPCOMP_DT_INST_PSEL(0), +#if SHIM_NRF_LPCOMP_DT_INST_REFSEL_IS_AREF(0) + .extrefsel = SHIM_NRF_LPCOMP_DT_INST_EXTREFSEL(0), +#endif + .refsel = SHIM_NRF_LPCOMP_DT_INST_REFSEL(0), + .enable_hyst = SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(0), +}; + +#if CONFIG_PM_DEVICE +static bool shim_nrf_lpcomp_is_resumed(void) +{ + enum pm_device_state state; + + (void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state); + return state == PM_DEVICE_STATE_ACTIVE; +} +#else +static bool shim_nrf_lpcomp_is_resumed(void) +{ + return true; +} +#endif + +static void shim_nrf_lpcomp_start(void) +{ + if (shim_nrf_lpcomp_data0.started) { + return; + } + + nrfx_lpcomp_start(shim_nrf_lpcomp_data0.event_mask, 0); + shim_nrf_lpcomp_data0.started = true; +} + +static void shim_nrf_lpcomp_stop(void) +{ + if (!shim_nrf_lpcomp_data0.started) { + return; + } + + nrfx_lpcomp_stop(); + shim_nrf_lpcomp_data0.started = false; +} + +static int shim_nrf_lpcomp_pm_callback(const struct device *dev, enum pm_device_action action) +{ + ARG_UNUSED(dev); + + ARG_UNUSED(dev); + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + shim_nrf_lpcomp_start(); + break; + +#if CONFIG_PM_DEVICE + case PM_DEVICE_ACTION_SUSPEND: + shim_nrf_lpcomp_stop(); + break; +#endif + + default: + return -ENOTSUP; + } + + return 0; +} + +#if (NRF_LPCOMP_HAS_AIN_AS_PIN) +static int shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim, + nrf_lpcomp_input_t *nrf) +{ + if (shim >= ARRAY_SIZE(shim_nrf_lpcomp_ain_map)) { + return -EINVAL; + } + + *nrf = shim_nrf_lpcomp_ain_map[(uint32_t)shim]; + return 0; +} +#else +static int shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim, + nrf_lpcomp_input_t *nrf) +{ + switch (shim) { + case COMP_NRF_LPCOMP_PSEL_AIN0: + *nrf = NRF_LPCOMP_INPUT_0; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN1: + *nrf = NRF_LPCOMP_INPUT_1; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN2: + *nrf = NRF_LPCOMP_INPUT_2; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN3: + *nrf = NRF_LPCOMP_INPUT_3; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN4: + *nrf = NRF_LPCOMP_INPUT_4; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN5: + *nrf = NRF_LPCOMP_INPUT_5; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN6: + *nrf = NRF_LPCOMP_INPUT_6; + break; + + case COMP_NRF_LPCOMP_PSEL_AIN7: + *nrf = NRF_LPCOMP_INPUT_7; + break; + + default: + return -EINVAL; + } + + return 0; +} +#endif + +#if (NRF_LPCOMP_HAS_AIN_AS_PIN) +static int shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim, + nrf_lpcomp_ext_ref_t *nrf) +{ + if (shim >= ARRAY_SIZE(shim_nrf_lpcomp_ain_map)) { + return -EINVAL; + } + + *nrf = shim_nrf_lpcomp_ain_map[shim]; + return 0; +} +#else +static int shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim, + nrf_lpcomp_ext_ref_t *nrf) +{ + switch (shim) { + case COMP_NRF_LPCOMP_EXTREFSEL_AIN0: + *nrf = NRF_LPCOMP_EXT_REF_REF0; + break; + + case COMP_NRF_LPCOMP_EXTREFSEL_AIN1: + *nrf = NRF_LPCOMP_EXT_REF_REF1; + break; + + default: + return -EINVAL; + } + + return 0; +} +#endif + +static int shim_nrf_lpcomp_refsel_to_nrf(enum comp_nrf_lpcomp_refsel shim, + nrf_lpcomp_ref_t *nrf) +{ + switch (shim) { + case COMP_NRF_LPCOMP_REFSEL_VDD_1_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_1_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_2_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_2_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_3_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_3_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_4_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_4_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_5_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_5_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_6_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_6_8; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_7_8: + *nrf = NRF_LPCOMP_REF_SUPPLY_7_8; + break; + +#if (LPCOMP_REFSEL_RESOLUTION == 16) + case COMP_NRF_LPCOMP_REFSEL_VDD_1_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_1_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_3_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_3_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_5_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_5_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_7_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_7_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_9_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_9_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_11_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_11_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_13_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_13_16; + break; + + case COMP_NRF_LPCOMP_REFSEL_VDD_15_16: + *nrf = NRF_LPCOMP_REF_SUPPLY_15_16; + break; +#endif + + case COMP_NRF_LPCOMP_REFSEL_AREF: + *nrf = NRF_LPCOMP_REF_EXT_REF; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int shim_nrf_lpcomp_config_to_nrf(const struct comp_nrf_lpcomp_config *shim, + nrfx_lpcomp_config_t *nrf) +{ + if (shim_nrf_lpcomp_refsel_to_nrf(shim->refsel, &nrf->reference)) { + return -EINVAL; + } + + if (shim_nrf_lpcomp_extrefsel_to_nrf(shim->extrefsel, &nrf->ext_ref)) { + return -EINVAL; + } + +#if NRF_LPCOMP_HAS_HYST + if (shim->enable_hyst) { + nrf->hyst = NRF_LPCOMP_HYST_ENABLED; + } else { + nrf->hyst = NRF_LPCOMP_HYST_NOHYST; + } +#else + if (shim->enable_hyst) { + return -EINVAL; + } +#endif + + if (shim_nrf_lpcomp_psel_to_nrf(shim->psel, &nrf->input)) { + return -EINVAL; + } + + return 0; +} + +static void shim_nrf_lpcomp_reconfigure(void) +{ + (void)nrfx_lpcomp_reconfigure(&shim_nrf_lpcomp_data0.config); +} + +static int shim_nrf_lpcomp_get_output(const struct device *dev) +{ + ARG_UNUSED(dev); + + return nrfx_lpcomp_sample(); +} + +static int shim_nrf_lpcomp_set_trigger(const struct device *dev, + enum comparator_trigger trigger) +{ + shim_nrf_lpcomp_stop(); + + switch (trigger) { + case COMPARATOR_TRIGGER_NONE: + shim_nrf_lpcomp_data0.event_mask = 0; + shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_CROSS; + break; + + case COMPARATOR_TRIGGER_RISING_EDGE: + shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_UP_MASK; + shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_UP; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_DOWN_MASK; + shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_DOWN; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_CROSS_MASK; + shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_CROSS; + break; + } + + shim_nrf_lpcomp_reconfigure(); + + if (shim_nrf_lpcomp_is_resumed()) { + shim_nrf_lpcomp_start(); + } + + return 0; +} + +static int shim_nrf_lpcomp_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + shim_nrf_lpcomp_stop(); + + shim_nrf_lpcomp_data0.callback = callback; + shim_nrf_lpcomp_data0.user_data = user_data; + + if (callback != NULL && atomic_test_and_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0)) { + callback(dev, user_data); + } + + if (shim_nrf_lpcomp_is_resumed()) { + shim_nrf_lpcomp_start(); + } + + return 0; +} + +static int shim_nrf_lpcomp_trigger_is_pending(const struct device *dev) +{ + ARG_UNUSED(dev); + + return atomic_test_and_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0); +} + +static const struct comparator_driver_api shim_nrf_lpcomp_api = { + .get_output = shim_nrf_lpcomp_get_output, + .set_trigger = shim_nrf_lpcomp_set_trigger, + .set_trigger_callback = shim_nrf_lpcomp_set_trigger_callback, + .trigger_is_pending = shim_nrf_lpcomp_trigger_is_pending, +}; + +int comp_nrf_lpcomp_configure(const struct device *dev, + const struct comp_nrf_lpcomp_config *config) +{ + nrfx_lpcomp_config_t nrf = {}; + + if (shim_nrf_lpcomp_config_to_nrf(config, &nrf)) { + return -EINVAL; + } + + memcpy(&shim_nrf_lpcomp_data0.config, &nrf, sizeof(shim_nrf_lpcomp_data0.config)); + + shim_nrf_lpcomp_stop(); + shim_nrf_lpcomp_reconfigure(); + if (shim_nrf_lpcomp_is_resumed()) { + shim_nrf_lpcomp_start(); + } + + return 0; +} + +static void shim_nrf_lpcomp_event_handler(nrf_lpcomp_event_t event) +{ + ARG_UNUSED(event); + + if (shim_nrf_lpcomp_data0.callback == NULL) { + atomic_set_bit(&shim_nrf_lpcomp_data0.triggered, 0); + return; + } + + shim_nrf_lpcomp_data0.callback(DEVICE_DT_INST_GET(0), shim_nrf_lpcomp_data0.user_data); + atomic_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0); +} + +static int shim_nrf_lpcomp_init(const struct device *dev) +{ + IRQ_CONNECT(DT_INST_IRQN(0), + DT_INST_IRQ(0, priority), + nrfx_isr, + nrfx_lpcomp_irq_handler, + 0); + + irq_enable(DT_INST_IRQN(0)); + + (void)shim_nrf_lpcomp_config_to_nrf(&shim_nrf_lpcomp_config0, + &shim_nrf_lpcomp_data0.config); + + if (nrfx_lpcomp_init(&shim_nrf_lpcomp_data0.config, + shim_nrf_lpcomp_event_handler) != NRFX_SUCCESS) { + return -ENODEV; + } + + return pm_device_driver_init(dev, shim_nrf_lpcomp_pm_callback); +} + +PM_DEVICE_DT_INST_DEFINE(0, shim_nrf_lpcomp_pm_callback); + +DEVICE_DT_INST_DEFINE(0, + shim_nrf_lpcomp_init, + PM_DEVICE_DT_INST_GET(0), + NULL, + NULL, + POST_KERNEL, + CONFIG_COMPARATOR_INIT_PRIORITY, + &shim_nrf_lpcomp_api); diff --git a/drivers/comparator/comparator_shell.c b/drivers/comparator/comparator_shell.c new file mode 100644 index 00000000000..2d747530871 --- /dev/null +++ b/drivers/comparator/comparator_shell.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#define AWAIT_TRIGGER_DEFAULT_TIMEOUT \ + CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_DEFAULT_TIMEOUT + +#define AWAIT_TRIGGER_MAX_TIMEOUT \ + CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT + +/* Mapped 1-1 to enum comparator_trigger */ +static const char *const trigger_lookup[] = { + "NONE", + "RISING_EDGE", + "FALLING_EDGE", + "BOTH_EDGES", +}; + +static K_SEM_DEFINE(triggered_sem, 0, 1); + +static int get_device_from_str(const struct shell *sh, + const char *dev_str, + const struct device **dev) +{ + *dev = device_get_binding(dev_str); + + if (*dev == NULL) { + shell_error(sh, "%s not %s", dev_str, "found"); + return -ENODEV; + } + + if (!device_is_ready(*dev)) { + shell_error(sh, "%s not %s", dev_str, "ready"); + return -ENODEV; + } + + return 0; +} + +static int cmd_get_output(const struct shell *sh, size_t argc, char **argv) +{ + int ret; + const char *dev_str; + const struct device *dev; + + ARG_UNUSED(argc); + + dev_str = argv[1]; + ret = get_device_from_str(sh, dev_str, &dev); + if (ret < 0) { + return ret; + } + + ret = comparator_get_output(dev); + if (ret < 0) { + shell_error(sh, "failed to %s %s", "get", "output"); + return -EIO; + } + + shell_print(sh, "%i", ret); + return 0; +} + +static int get_trigger_from_str(const struct shell *sh, + const char *trigger_str, + enum comparator_trigger *trigger) +{ + ARRAY_FOR_EACH(trigger_lookup, i) { + if (strcmp(trigger_lookup[i], trigger_str) == 0) { + *trigger = (enum comparator_trigger)i; + return 0; + } + } + + shell_error(sh, "%s not %s", trigger_str, "valid"); + return -EINVAL; +} + +static int cmd_set_trigger(const struct shell *sh, size_t argc, char **argv) +{ + const char *dev_str; + const char *trigger_str; + int ret; + const struct device *dev; + enum comparator_trigger trigger; + + ARG_UNUSED(argc); + + dev_str = argv[1]; + ret = get_device_from_str(sh, dev_str, &dev); + if (ret < 0) { + return ret; + } + + trigger_str = argv[2]; + ret = get_trigger_from_str(sh, trigger_str, &trigger); + if (ret < 0) { + return ret; + } + + ret = comparator_set_trigger(dev, trigger); + if (ret < 0) { + shell_error(sh, "failed to %s %s", "set", "trigger"); + return -EIO; + } + + return 0; +} + +static int get_timeout_from_str(const struct shell *sh, + const char *timeout_str, + k_timeout_t *timeout) +{ + long seconds; + char *end; + + seconds = strtol(timeout_str, &end, 10); + if ((*end != '\0') || + (seconds < 1) || + (seconds > AWAIT_TRIGGER_MAX_TIMEOUT)) { + shell_error(sh, "%s not %s", timeout_str, "valid"); + return -EINVAL; + } + + *timeout = K_SECONDS(seconds); + return 0; +} + +static void trigger_cb(const struct device *dev, void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(user_data); + + k_sem_give(&triggered_sem); +} + +static int cmd_await_trigger(const struct shell *sh, size_t argc, char **argv) +{ + const char *dev_str; + const char *timeout_str; + int ret; + const struct device *dev; + k_timeout_t timeout; + + dev_str = argv[1]; + ret = get_device_from_str(sh, dev_str, &dev); + if (ret < 0) { + return ret; + } + + if (argc == 3) { + timeout_str = argv[2]; + ret = get_timeout_from_str(sh, timeout_str, &timeout); + if (ret < 0) { + return ret; + } + } else { + timeout = K_SECONDS(AWAIT_TRIGGER_DEFAULT_TIMEOUT); + } + + k_sem_reset(&triggered_sem); + + ret = comparator_set_trigger_callback(dev, trigger_cb, NULL); + if (ret < 0) { + shell_error(sh, "failed to %s %s", "set", "trigger callback"); + return -EIO; + } + + ret = k_sem_take(&triggered_sem, timeout); + if (ret == 0) { + shell_print(sh, "triggered"); + } else if (ret == -EAGAIN) { + shell_print(sh, "timed out"); + } else { + shell_error(sh, "internal error"); + } + + ret = comparator_set_trigger_callback(dev, NULL, NULL); + if (ret < 0) { + shell_error(sh, "failed to %s %s", "clear", "trigger callback"); + return -EIO; + } + + return 0; +} + +static int cmd_trigger_is_pending(const struct shell *sh, size_t argc, char **argv) +{ + int ret; + const char *dev_str; + const struct device *dev; + + ARG_UNUSED(argc); + + dev_str = argv[1]; + ret = get_device_from_str(sh, dev_str, &dev); + if (ret < 0) { + return ret; + } + + ret = comparator_trigger_is_pending(dev); + if (ret < 0) { + shell_error(sh, "failed to %s %s", "get", "trigger status"); + return -EIO; + } + + shell_print(sh, "%i", ret); + return 0; +} + +static void dsub_set_trigger_lookup_1(size_t idx, struct shell_static_entry *entry) +{ + entry->syntax = (idx < ARRAY_SIZE(trigger_lookup)) ? trigger_lookup[idx] : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_set_trigger_1, dsub_set_trigger_lookup_1); + +static void dsub_set_trigger_lookup_0(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, NULL); + + entry->syntax = dev != NULL ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = &dsub_set_trigger_1; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_set_trigger_0, dsub_set_trigger_lookup_0); + +static void dsub_device_lookup_0(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, NULL); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_device_0, dsub_device_lookup_0); + +#define GET_OUTPUT_HELP \ + ("comp get_output ") + +#define SET_TRIGGER_HELP \ + ("comp set_trigger ") + +#define AWAIT_TRIGGER_HELP \ + ("comp await_trigger [timeout] (default " \ + STRINGIFY(AWAIT_TRIGGER_DEFAULT_TIMEOUT) \ + "s, max " \ + STRINGIFY(AWAIT_TRIGGER_MAX_TIMEOUT) \ + "s)") + +#define TRIGGER_PENDING_HELP \ + ("comp trigger_is_pending ") + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_comp, + SHELL_CMD_ARG(get_output, &dsub_device_0, GET_OUTPUT_HELP, cmd_get_output, 2, 0), + SHELL_CMD_ARG(set_trigger, &dsub_set_trigger_0, SET_TRIGGER_HELP, cmd_set_trigger, 3, 0), + SHELL_CMD_ARG(await_trigger, &dsub_device_0, AWAIT_TRIGGER_HELP, cmd_await_trigger, 2, 1), + SHELL_CMD_ARG(trigger_is_pending, &dsub_device_0, TRIGGER_PENDING_HELP, + cmd_trigger_is_pending, 2, 1), + SHELL_SUBCMD_SET_END +); + +SHELL_CMD_REGISTER(comp, &sub_comp, "Comparator device commands", NULL); diff --git a/drivers/sensor/nxp/CMakeLists.txt b/drivers/sensor/nxp/CMakeLists.txt index 73dc2cb274b..36e39e002bf 100644 --- a/drivers/sensor/nxp/CMakeLists.txt +++ b/drivers/sensor/nxp/CMakeLists.txt @@ -5,10 +5,10 @@ add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002) add_subdirectory_ifdef(CONFIG_FXLS8974 fxls8974) add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700) -add_subdirectory_ifdef(CONFIG_MCUX_ACMP mcux_acmp) add_subdirectory_ifdef(CONFIG_MCUX_LPCMP mcux_lpcmp) add_subdirectory_ifdef(CONFIG_NXP_TEMPMON nxp_tempmon) add_subdirectory_ifdef(CONFIG_QDEC_MCUX qdec_mcux) add_subdirectory_ifdef(CONFIG_QDEC_NXP_S32 qdec_nxp_s32) +add_subdirectory_ifdef(CONFIG_SENSOR_MCUX_ACMP mcux_acmp) add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/nxp/mcux_acmp/Kconfig b/drivers/sensor/nxp/mcux_acmp/Kconfig index 0f976c65e21..1934e4d93f7 100644 --- a/drivers/sensor/nxp/mcux_acmp/Kconfig +++ b/drivers/sensor/nxp/mcux_acmp/Kconfig @@ -4,17 +4,18 @@ # Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 -config MCUX_ACMP +config SENSOR_MCUX_ACMP bool "NXP MCUX Analog Comparator (ACMP)" default y depends on DT_HAS_NXP_KINETIS_ACMP_ENABLED select PINCTRL + select MCUX_ACMP help Enable driver for the NXP MCUX Analog Comparator (ACMP). -config MCUX_ACMP_TRIGGER +config SENSOR_MCUX_ACMP_TRIGGER bool "Trigger support" - depends on MCUX_ACMP + depends on SENSOR_MCUX_ACMP help Enable trigger support for the NXP MCUX Analog Comparator (ACMP). diff --git a/drivers/sensor/nxp/mcux_acmp/mcux_acmp.c b/drivers/sensor/nxp/mcux_acmp/mcux_acmp.c index 30339f3e48d..90dde17e83c 100644 --- a/drivers/sensor/nxp/mcux_acmp/mcux_acmp.c +++ b/drivers/sensor/nxp/mcux_acmp/mcux_acmp.c @@ -41,13 +41,56 @@ BUILD_ASSERT(kACMP_PortInputFromDAC == 0); BUILD_ASSERT(kACMP_PortInputFromMux == 1); #endif /* MCUX_ACMP_HAS_INPSEL || MCUX_ACMP_HAS_INNSEL */ +/* + * prop New property name + * depr Deprecated property name + */ +#define MCUX_ACMP_DT_INST_PROP(inst, prop, depr) \ + COND_CODE_1( \ + DT_INST_NODE_HAS_PROP(inst, prop), \ + (DT_INST_PROP(inst, prop)), \ + (DT_INST_PROP(inst, depr)) \ + ) + +/* + * prop New property name + * depr Deprecated property name + */ +#define MCUX_ACMP_DT_INST_PROP_OR(inst, prop, depr, default_value) \ + COND_CODE_1( \ + DT_INST_NODE_HAS_PROP(inst, prop) || DT_INST_NODE_HAS_PROP(inst, depr), \ + (MCUX_ACMP_DT_INST_PROP(inst, prop, depr)), \ + (default_value) \ + ) + +#define MCUX_ACMP_DT_INST_ENABLE_SAMPLE(inst) \ + MCUX_ACMP_DT_INST_PROP(inst, filter_enable_sample, nxp_enable_sample) + +#define MCUX_ACMP_DT_INST_FILTER_COUNT(inst) \ + MCUX_ACMP_DT_INST_PROP_OR(inst, filter_count, nxp_filter_count, 0) + +#define MCUX_ACMP_DT_INST_FILTER_PERIOD(inst) \ + MCUX_ACMP_DT_INST_PROP_OR(inst, filter_period, nxp_filter_period, 0) + +#define MCUX_ACMP_DT_INST_HIGH_SPEED(inst) \ + MCUX_ACMP_DT_INST_PROP(inst, enable_high_speed_mode, nxp_high_speed_mode) + +#define MCUX_ACMP_DT_INST_USE_UNFILTERED_MODE(inst) \ + MCUX_ACMP_DT_INST_PROP(inst, use_unfiltered_output, nxp_use_unfiltered_output) + +#define MCUX_ACMP_DT_INST_USE_ENABLE_PIN_OUT(inst) \ + MCUX_ACMP_DT_INST_PROP(inst, enable_pin_out, nxp_enable_output_pin) + +#define MCUX_ACMP_DT_INST_ENABLE_WINDOW_MODE(inst) \ + MCUX_ACMP_DT_INST_PROP(inst, enable_window_mode, nxp_window_mode) + struct mcux_acmp_config { CMP_Type *base; acmp_filter_config_t filter; const struct pinctrl_dev_config *pincfg; -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER void (*irq_config_func)(const struct device *dev); -#endif /* CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ bool high_speed : 1; bool unfiltered : 1; bool output : 1; @@ -61,7 +104,7 @@ struct mcux_acmp_data { #if MCUX_ACMP_HAS_DISCRETE_MODE acmp_discrete_mode_config_t discrete_config; #endif -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER const struct device *dev; sensor_trigger_handler_t rising_handler; const struct sensor_trigger *rising_trigger; @@ -69,7 +112,7 @@ struct mcux_acmp_data { const struct sensor_trigger *falling_trigger; struct k_work work; volatile uint32_t status; -#endif /* CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ bool cout; }; @@ -370,7 +413,7 @@ static int mcux_acmp_channel_get(const struct device *dev, return 0; } -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER static int mcux_acmp_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) @@ -431,7 +474,7 @@ static void mcux_acmp_isr(const struct device *dev) k_work_submit(&data->work); } -#endif /* CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ static int mcux_acmp_init(const struct device *dev) { @@ -462,7 +505,7 @@ static int mcux_acmp_init(const struct device *dev) /* Disable DAC */ ACMP_SetDACConfig(config->base, NULL); -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER data->dev = dev; k_work_init(&data->work, mcux_acmp_trigger_work_handler); @@ -470,7 +513,7 @@ static int mcux_acmp_init(const struct device *dev) ACMP_EnableInterrupts(config->base, kACMP_OutputRisingInterruptEnable | kACMP_OutputFallingInterruptEnable); -#endif /* CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ ACMP_Enable(config->base, true); @@ -480,30 +523,30 @@ static int mcux_acmp_init(const struct device *dev) static const struct sensor_driver_api mcux_acmp_driver_api = { .attr_set = mcux_acmp_attr_set, .attr_get = mcux_acmp_attr_get, -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER .trigger_set = mcux_acmp_trigger_set, -#endif /* CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ .sample_fetch = mcux_acmp_sample_fetch, .channel_get = mcux_acmp_channel_get, }; -#define MCUX_ACMP_DECLARE_CONFIG(n, config_func_init) \ +#define MCUX_ACMP_DECLARE_CONFIG(n, config_func_init) \ static const struct mcux_acmp_config mcux_acmp_config_##n = { \ .base = (CMP_Type *)DT_INST_REG_ADDR(n), \ .filter = { \ - .enableSample = DT_INST_PROP(n, nxp_enable_sample), \ - .filterCount = DT_INST_PROP_OR(n, nxp_filter_count, 0), \ - .filterPeriod = DT_INST_PROP_OR(n, nxp_filter_period, 0), \ + .enableSample = MCUX_ACMP_DT_INST_ENABLE_SAMPLE(n), \ + .filterCount = MCUX_ACMP_DT_INST_FILTER_COUNT(n), \ + .filterPeriod = MCUX_ACMP_DT_INST_FILTER_PERIOD(n), \ }, \ - .high_speed = DT_INST_PROP(n, nxp_high_speed_mode), \ - .unfiltered = DT_INST_PROP(n, nxp_use_unfiltered_output), \ - .output = DT_INST_PROP(n, nxp_enable_output_pin), \ - .window = DT_INST_PROP(n, nxp_window_mode), \ + .high_speed = MCUX_ACMP_DT_INST_HIGH_SPEED(n), \ + .unfiltered = MCUX_ACMP_DT_INST_USE_UNFILTERED_MODE(n), \ + .output = MCUX_ACMP_DT_INST_USE_ENABLE_PIN_OUT(n), \ + .window = MCUX_ACMP_DT_INST_ENABLE_WINDOW_MODE(n), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ config_func_init \ } -#ifdef CONFIG_MCUX_ACMP_TRIGGER +#ifdef CONFIG_SENSOR_MCUX_ACMP_TRIGGER #define MCUX_ACMP_CONFIG_FUNC(n) \ static void mcux_acmp_config_func_##n(const struct device *dev) \ { \ @@ -517,12 +560,12 @@ static const struct mcux_acmp_config mcux_acmp_config_##n = { \ .irq_config_func = mcux_acmp_config_func_##n #define MCUX_ACMP_INIT_CONFIG(n) \ MCUX_ACMP_DECLARE_CONFIG(n, MCUX_ACMP_CONFIG_FUNC_INIT(n)) -#else /* !CONFIG_MCUX_ACMP_TRIGGER */ +#else /* !CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ #define MCUX_ACMP_CONFIG_FUNC(n) #define MCUX_ACMP_CONFIG_FUNC_INIT #define MCUX_ACMP_INIT_CONFIG(n) \ MCUX_ACMP_DECLARE_CONFIG(n, MCUX_ACMP_CONFIG_FUNC_INIT) -#endif /* !CONFIG_MCUX_ACMP_TRIGGER */ +#endif /* !CONFIG_SENSOR_MCUX_ACMP_TRIGGER */ #define MCUX_ACMP_INIT(n) \ static struct mcux_acmp_data mcux_acmp_data_##n; \ diff --git a/dts/arm/nordic/nrf52810.dtsi b/dts/arm/nordic/nrf52810.dtsi index 8758a6354b8..a67d677e7a2 100644 --- a/dts/arm/nordic/nrf52810.dtsi +++ b/dts/arm/nordic/nrf52810.dtsi @@ -269,7 +269,6 @@ reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf52811.dtsi b/dts/arm/nordic/nrf52811.dtsi index b14df8b567a..41391b7eb3d 100644 --- a/dts/arm/nordic/nrf52811.dtsi +++ b/dts/arm/nordic/nrf52811.dtsi @@ -301,7 +301,6 @@ reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf52820.dtsi b/dts/arm/nordic/nrf52820.dtsi index 601a4f8c7ed..4d05bc741a0 100644 --- a/dts/arm/nordic/nrf52820.dtsi +++ b/dts/arm/nordic/nrf52820.dtsi @@ -313,7 +313,6 @@ reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf52832.dtsi b/dts/arm/nordic/nrf52832.dtsi index b722d183da8..32245608a80 100644 --- a/dts/arm/nordic/nrf52832.dtsi +++ b/dts/arm/nordic/nrf52832.dtsi @@ -310,16 +310,13 @@ comp: comparator@40013000 { /* - * This comparator node can be COMP or LPCOMP, - * for the user to pick: - * compatible = "nordic,nrf-comp" or - * "nordic,nrf-lpcomp". + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP */ compatible = "nordic,nrf-comp"; reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf52833.dtsi b/dts/arm/nordic/nrf52833.dtsi index d81a8953fda..b576401803a 100644 --- a/dts/arm/nordic/nrf52833.dtsi +++ b/dts/arm/nordic/nrf52833.dtsi @@ -325,16 +325,13 @@ comp: comparator@40013000 { /* - * This comparator node can be COMP or LPCOMP, - * for the user to pick: - * compatible = "nordic,nrf-comp" or - * "nordic,nrf-lpcomp". + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP */ compatible = "nordic,nrf-comp"; reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf52840.dtsi b/dts/arm/nordic/nrf52840.dtsi index fd7c1aeaa87..3965c4f3b11 100644 --- a/dts/arm/nordic/nrf52840.dtsi +++ b/dts/arm/nordic/nrf52840.dtsi @@ -312,16 +312,13 @@ comp: comparator@40013000 { /* - * This comparator node can be COMP or LPCOMP, - * for the user to pick: - * compatible = "nordic,nrf-comp" or - * "nordic,nrf-lpcomp". + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP */ compatible = "nordic,nrf-comp"; reg = <0x40013000 0x1000>; interrupts = <19 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: swi0: egu@40014000 { diff --git a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi index 7d79b227be2..50842fb3801 100644 --- a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi @@ -350,16 +350,13 @@ wdt1: watchdog@19000 { comp: comparator@1a000 { /* - * This comparator node can be COMP or LPCOMP, - * for the user to pick: - * compatible = "nordic,nrf-comp" or - * "nordic,nrf-lpcomp". + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP */ compatible = "nordic,nrf-comp"; reg = <0x1a000 0x1000>; interrupts = <26 NRF_DEFAULT_IRQ_PRIORITY>; status = "disabled"; - #io-channel-cells = <1>; }; egu0: egu@1b000 { diff --git a/dts/arm/nxp/nxp_ke1xf.dtsi b/dts/arm/nxp/nxp_ke1xf.dtsi index 6aff4315889..8c880c06daf 100644 --- a/dts/arm/nxp/nxp_ke1xf.dtsi +++ b/dts/arm/nxp/nxp_ke1xf.dtsi @@ -575,7 +575,6 @@ interrupts = <40 0>; clocks = <&scg KINETIS_SCG_BUS_CLK>; status = "disabled"; - #io-channel-cells = <2>; }; cmp1: cmp@40074000 { @@ -584,7 +583,6 @@ interrupts = <41 0>; clocks = <&scg KINETIS_SCG_BUS_CLK>; status = "disabled"; - #io-channel-cells = <2>; }; cmp2: cmp@40075000 { @@ -593,7 +591,6 @@ interrupts = <70 0>; clocks = <&scg KINETIS_SCG_BUS_CLK>; status = "disabled"; - #io-channel-cells = <2>; }; flexio1: flexio@4005a000 { diff --git a/dts/arm/nxp/nxp_ke1xz.dtsi b/dts/arm/nxp/nxp_ke1xz.dtsi index 577f0e894fe..8e34d564845 100644 --- a/dts/arm/nxp/nxp_ke1xz.dtsi +++ b/dts/arm/nxp/nxp_ke1xz.dtsi @@ -419,7 +419,6 @@ interrupts = <16 0>; clocks = <&scg KINETIS_SCG_BUS_CLK>; status = "disabled"; - #io-channel-cells = <2>; }; lpspi0: spi@4002c000 { diff --git a/dts/arm/nxp/nxp_rt118x.dtsi b/dts/arm/nxp/nxp_rt118x.dtsi index b0f7c5e078a..4bb645e8979 100644 --- a/dts/arm/nxp/nxp_rt118x.dtsi +++ b/dts/arm/nxp/nxp_rt118x.dtsi @@ -299,7 +299,6 @@ reg = <0x2dc0000 0x4000>; interrupts = <200 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp2: cmp@2dd0000 { @@ -307,7 +306,6 @@ reg = <0x2dd0000 0x4000>; interrupts = <201 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp3: cmp@2de0000 { @@ -315,7 +313,6 @@ reg = <0x2de0000 0x4000>; interrupts = <202 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp4: cmp@2df0000 { @@ -323,7 +320,6 @@ reg = <0x2df0000 0x4000>; interrupts = <203 0>; status = "disabled"; - #io-channel-cells = <2>; }; }; diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 6e3644f58fa..5dd41ed398b 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -1011,7 +1011,6 @@ reg = <0x401a4000 0x4000>; interrupts = <157 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp2: cmp@401a8000 { @@ -1019,7 +1018,6 @@ reg = <0x401a8000 0x4000>; interrupts = <158 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp3: cmp@401ac000 { @@ -1027,7 +1025,6 @@ reg = <0x401ac000 0x4000>; interrupts = <159 0>; status = "disabled"; - #io-channel-cells = <2>; }; acmp4: cmp@401b0000 { @@ -1035,7 +1032,6 @@ reg = <0x401b0000 0x4000>; interrupts = <160 0>; status = "disabled"; - #io-channel-cells = <2>; }; anatop: anatop@40c84000 { diff --git a/dts/bindings/comparator/nordic,nrf-comp.yaml b/dts/bindings/comparator/nordic,nrf-comp.yaml new file mode 100644 index 00000000000..a6f7f32db5f --- /dev/null +++ b/dts/bindings/comparator/nordic,nrf-comp.yaml @@ -0,0 +1,128 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + Nordic nRF COMP (analog COMParator) + + This comparator has varying configurations which require varying + properties be set in the devicetree. + + The following example displays the minimum node layout: + + comp: comp@deadbeef { + compatible = "nordic,nrf-comp"; + reg = <0xdeadbeef 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + + Enabling the comparator node requires setting the default + configuration of the comparator. + + The following example displays enabling the comparator in + single-ended mode, selecting an internal reference: + + &comp { + status = "okay"; + main-mode = "SE"; + psel = "AIN0"; + refsel = "INT_1V2"; + sp-mode = "NORMAL"; + th-up = <36>; + th-down = <30>; + isource = "OFF"; + }; + + To select an external reference, select the "AREF" + reference and add the external reference: + + &comp { + ... + refsel = "AREF"; + extrefsel = "AIN1"; + ... + }; + + The following example displays enabling the comparator + in differential mode: + + &comp { + status = "okay"; + main-mode = "DIFF"; + psel = "AIN0"; + extrefsel = "AIN1"; + sp-mode = "NORMAL"; + hyst = "50MV"; + isource = "OFF"; + }; + +compatible: "nordic,nrf-comp" + +include: base.yaml + +properties: + main-mode: + type: string + enum: + - "SE" + - "DIFF" + + psel: + type: string + enum: + - "AIN0" + - "AIN1" + - "AIN2" + - "AIN3" + - "AIN4" + - "AIN5" + - "AIN6" + - "AIN7" + - "VDD_DIV2" + - "VDDH_DIV5" + + extrefsel: + type: string + enum: + - "AIN0" + - "AIN1" + - "AIN2" + - "AIN3" + - "AIN4" + - "AIN5" + - "AIN6" + - "AIN7" + + refsel: + type: string + enum: + - "INT_1V2" + - "INT_1V8" + - "INT_2V4" + - "AVDDAO1V8" + - "VDD" + - "AREF" + + enable-hyst: + type: boolean + + sp-mode: + type: string + enum: + - "LOW" + - "NORMAL" + - "HIGH" + + th-up: + type: int + + th-down: + type: int + + isource: + type: string + enum: + - "DISABLED" + - "2UA5" + - "5UA" + - "10UA" diff --git a/dts/bindings/comparator/nordic,nrf-lpcomp.yaml b/dts/bindings/comparator/nordic,nrf-lpcomp.yaml new file mode 100644 index 00000000000..8f9cd91a6b7 --- /dev/null +++ b/dts/bindings/comparator/nordic,nrf-lpcomp.yaml @@ -0,0 +1,83 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + Nordic nRF LPCOMP (analog Low-Power COMParator) + + The following example displays the minimum node layout: + + comp: comp@deadbeef { + compatible = "nordic,nrf-lpcomp"; + reg = <0xdeadbeef 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + + Enabling the comparator node requires setting the default + configuration of the comparator. + + The following example displays enabling the comparator + using an internal reference: + + &comp { + status = "okay"; + psel = "AIN0"; + refsel = "VDD_4_8"; + hyst = "ENABLED"; + }; + + To select an external reference, select the "AREF" + reference and add the external reference: + + &comp { + ... + refsel = "AREF"; + extrefsel = "AIN1"; + ... + }; + +compatible: "nordic,nrf-lpcomp" + +include: base.yaml + +properties: + psel: + type: string + enum: + - "AIN0" + - "AIN1" + - "AIN2" + - "AIN3" + - "AIN4" + - "AIN5" + - "AIN6" + - "AIN7" + + extrefsel: + type: string + enum: + - "AIN0" + - "AIN1" + + refsel: + type: string + enum: + - "VDD_1_8" + - "VDD_2_8" + - "VDD_3_8" + - "VDD_4_8" + - "VDD_5_8" + - "VDD_6_8" + - "VDD_7_8" + - "VDD_1_16" + - "VDD_3_16" + - "VDD_5_16" + - "VDD_7_16" + - "VDD_9_16" + - "VDD_11_16" + - "VDD_13_16" + - "VDD_15_16" + - "AREF" + + enable-hyst: + type: boolean diff --git a/dts/bindings/comparator/nxp,kinetis-acmp.yaml b/dts/bindings/comparator/nxp,kinetis-acmp.yaml new file mode 100644 index 00000000000..21db936c2bf --- /dev/null +++ b/dts/bindings/comparator/nxp,kinetis-acmp.yaml @@ -0,0 +1,227 @@ +# Copyright (c) 2020 Vestas Wind Systems A/S +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + NXP Kinetis ACMP (Analog CoMParator) + + The following example displays the minimum node layout: + + acmp0: acmp@deadbeef { + compatible = "nxp,kinetis-acmp"; + reg = <0xdeadbeef 0x1000>; + interrupts = <0 0>; + clocks = <&scg KINETIS_SCG_BUS_CLK>; + status = "disabled"; + }; + + Enabling the comparator node requires setting the minimum default + configuration of the comparator. This includes selecting the + positive and negative inputs, and routing them using pinctrl: + + &pinctrl { + acmp0_default: acmp0_default { + group0 { + ... + }; + }; + }; + + &acmp0 { + status = "okay"; + pinctrl-0 = <&acmp0_default>; + pinctrl-names = "default"; + + positive-mux-input = "IN0"; + negative-mux-input = "IN1"; + }; + +compatible: "nxp,kinetis-acmp" + +include: + - base.yaml + - pinctrl-device.yaml + +properties: + interrupts: + required: true + + reg: + required: true + + nxp,enable-output-pin: + type: boolean + deprecated: true + description: Deprecated. Please use enable-pin-out instead + + nxp,use-unfiltered-output: + type: boolean + deprecated: true + description: Deprecated. Please use use-unfiltered-output instead + + nxp,high-speed-mode: + type: boolean + deprecated: true + description: Deprecated. Please use enable-high-speed-mode instead + + nxp,enable-sample: + type: boolean + deprecated: true + description: Deprecated. Please use filter-enable-sample instead + + nxp,filter-count: + type: int + deprecated: true + description: Deprecated. Please use filter-count instead + + nxp,filter-period: + type: int + deprecated: true + description: Deprecated. Please use filter-period instead + + nxp,window-mode: + type: boolean + deprecated: true + description: Deprecated. Please use enable-window-mode instead + + offset-mode: + type: string + enum: + - "LEVEL0" + - "LEVEL1" + + hysteresis-mode: + type: string + enum: + - "LEVEL0" + - "LEVEL1" + - "LEVEL2" + - "LEVEL3" + + enable-high-speed-mode: + type: boolean + + invert-output: + type: boolean + + use-unfiltered-output: + type: boolean + + enable-pin-out: + type: boolean + + enable-window-mode: + type: boolean + + positive-mux-input: + type: string + enum: + - IN0 + - IN1 + - IN2 + - IN3 + - IN4 + - IN5 + - IN6 + - IN7 + + negative-mux-input: + type: string + enum: + - IN0 + - IN1 + - IN2 + - IN3 + - IN4 + - IN5 + - IN6 + - IN7 + + positive-port-input: + type: string + enum: + - DAC + - MUX + + negative-port-input: + type: string + enum: + - DAC + - MUX + + filter-enable-sample: + type: boolean + + filter-count: + type: int + description: Filter sample count (0 to 7). + + filter-period: + type: int + description: Filter sample period in bus clock cycles (0 to 255). + + dac-vref-source: + type: string + enum: + - "VIN1" + - "VIN2" + + dac-value: + type: int + + dac-enable: + type: boolean + + dac-enable-high-speed: + type: boolean + + discrete-mode-enable-positive-channel: + type: boolean + + discrete-mode-enable-negative-channel: + type: boolean + + discrete-mode-enable-resistor-divider: + type: boolean + + discrete-mode-clock-source: + type: string + enum: + - "SLOW" + - "FAST" + + discrete-mode-sample-time: + type: string + enum: + - "T1" + - "T2" + - "T4" + - "T8" + - "T16" + - "T32" + - "T64" + - "T256" + + discrete-mode-phase1-time: + type: string + enum: + - "ALT0" + - "ALT1" + - "ALT2" + - "ALT3" + - "ALT4" + - "ALT5" + - "ALT6" + - "ALT7" + + discrete-mode-phase2-time: + type: string + enum: + - "ALT0" + - "ALT1" + - "ALT2" + - "ALT3" + - "ALT4" + - "ALT5" + - "ALT6" + - "ALT7" diff --git a/dts/bindings/comparator/zephyr,comp-fake.yaml b/dts/bindings/comparator/zephyr,comp-fake.yaml new file mode 100644 index 00000000000..ac344f4639c --- /dev/null +++ b/dts/bindings/comparator/zephyr,comp-fake.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Fake comparator device used as stub or mock for testing + +compatible: "zephyr,fake-comp" + +include: base.yaml diff --git a/dts/bindings/sensor/nordic,nrf-comp.yaml b/dts/bindings/sensor/nordic,nrf-comp.yaml deleted file mode 100644 index cc964015ba1..00000000000 --- a/dts/bindings/sensor/nordic,nrf-comp.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2022, Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 - -description: Nordic nRF family COMP (Comparator) - -compatible: "nordic,nrf-comp" - -include: base.yaml - -properties: - reg: - required: true - - interrupts: - required: true - - "#io-channel-cells": - type: int - const: 1 - required: true - -io-channel-cells: - - input diff --git a/dts/bindings/sensor/nordic,nrf-lpcomp.yaml b/dts/bindings/sensor/nordic,nrf-lpcomp.yaml deleted file mode 100644 index 132b0980ac5..00000000000 --- a/dts/bindings/sensor/nordic,nrf-lpcomp.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2022, Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 - -description: Nordic nRF family LPCOMP (Low-power Comparator) - -compatible: "nordic,nrf-lpcomp" - -include: base.yaml - -properties: - reg: - required: true - - interrupts: - required: true - - "#io-channel-cells": - type: int - const: 1 - required: true - -io-channel-cells: - - input diff --git a/dts/bindings/sensor/nxp,kinetis-acmp.yaml b/dts/bindings/sensor/nxp,kinetis-acmp.yaml deleted file mode 100644 index 9608f9305c9..00000000000 --- a/dts/bindings/sensor/nxp,kinetis-acmp.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2020 Vestas Wind Systems A/S -# SPDX-License-Identifier: Apache-2.0 - -description: NXP Kinetis Analog Comparator (ACMP) - -compatible: "nxp,kinetis-acmp" - -include: [sensor-device.yaml, pinctrl-device.yaml] - -properties: - interrupts: - required: true - - reg: - required: true - - nxp,enable-output-pin: - type: boolean - description: | - Make the comparator output (CMP0) available on a packaged pin. - - nxp,use-unfiltered-output: - type: boolean - description: | - Use the unfiltered comparator output for CMP0. - - nxp,high-speed-mode: - type: boolean - description: | - Enable high speed comparison mode. - - nxp,enable-sample: - type: boolean - description: | - Enable external sample signal as clock input. - - nxp,filter-count: - type: int - description: | - Filter sample count (0 to 7). - - nxp,filter-period: - type: int - description: | - Filter sample period in bus clock cycles (0 to 255). - - nxp,window-mode: - type: boolean - description: | - Enable windowing mode. - - "#io-channel-cells": - type: int - const: 2 - -io-channel-cells: - - positive - - negative diff --git a/dts/common/nordic/nrf54h20.dtsi b/dts/common/nordic/nrf54h20.dtsi index 9af1488fd1b..0ec483859c5 100644 --- a/dts/common/nordic/nrf54h20.dtsi +++ b/dts/common/nordic/nrf54h20.dtsi @@ -838,11 +838,14 @@ }; comp: comparator@983000 { + /* + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP + */ compatible = "nordic,nrf-comp"; reg = <0x983000 0x1000>; status = "disabled"; interrupts = <387 NRF_DEFAULT_IRQ_PRIORITY>; - #io-channel-cells = <1>; }; temp: temperature-sensor@984000 { diff --git a/dts/common/nordic/nrf54l15.dtsi b/dts/common/nordic/nrf54l15.dtsi index 5851bfd71e3..81b22c9f859 100644 --- a/dts/common/nordic/nrf54l15.dtsi +++ b/dts/common/nordic/nrf54l15.dtsi @@ -563,6 +563,17 @@ frame-timeout-supported; }; + comp: comparator@106000 { + /* + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP + */ + compatible = "nordic,nrf-comp"; + reg = <0x106000 0x1000>; + status = "disabled"; + interrupts = <262 NRF_DEFAULT_IRQ_PRIORITY>; + }; + #ifdef USE_NON_SECURE_ADDRESS_MAP /* intentionally empty because WDT30 is hardware fixed to Secure */ #else diff --git a/dts/common/nordic/nrf9280.dtsi b/dts/common/nordic/nrf9280.dtsi index 9949f6c9f1c..e202132f89a 100644 --- a/dts/common/nordic/nrf9280.dtsi +++ b/dts/common/nordic/nrf9280.dtsi @@ -737,11 +737,14 @@ }; comp: comparator@983000 { + /* + * Use compatible "nordic,nrf-comp" to configure as COMP + * Use compatible "nordic,nrf-lpcomp" to configure as LPCOMP + */ compatible = "nordic,nrf-comp"; reg = <0x983000 0x1000>; status = "disabled"; interrupts = <387 NRF_DEFAULT_IRQ_PRIORITY>; - #io-channel-cells = <1>; }; temp: temperature-sensor@984000 { diff --git a/include/zephyr/drivers/comparator.h b/include/zephyr/drivers/comparator.h new file mode 100644 index 00000000000..3d246fea5ff --- /dev/null +++ b/include/zephyr/drivers/comparator.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_H_ +#define ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_H_ + +/** + * @brief Comparator Interface + * @defgroup comparator_interface Comparator Interface + * @since 3.7 + * @version 0.1.0 + * @ingroup io_interfaces + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Comparator trigger enumerations */ +enum comparator_trigger { + /** No trigger */ + COMPARATOR_TRIGGER_NONE = 0, + /** Trigger on rising edge of comparator output */ + COMPARATOR_TRIGGER_RISING_EDGE, + /** Trigger on falling edge of comparator output */ + COMPARATOR_TRIGGER_FALLING_EDGE, + /** Trigger on both edges of comparator output */ + COMPARATOR_TRIGGER_BOTH_EDGES +}; + +/** Comparator callback template */ +typedef void (*comparator_callback_t)(const struct device *dev, void *user_data); + +/** @cond INTERNAL_HIDDEN */ + +typedef int (*comparator_api_get_output)(const struct device *dev); +typedef int (*comparator_api_set_trigger)(const struct device *dev, + enum comparator_trigger trigger); +typedef int (*comparator_api_set_trigger_callback)(const struct device *dev, + comparator_callback_t callback, + void *user_data); +typedef int (*comparator_api_trigger_is_pending)(const struct device *dev); + +__subsystem struct comparator_driver_api { + comparator_api_get_output get_output; + comparator_api_set_trigger set_trigger; + comparator_api_set_trigger_callback set_trigger_callback; + comparator_api_trigger_is_pending trigger_is_pending; +}; + +/** @endcond */ + +/** + * @brief Get comparator's output state + * + * @param dev Comparator device + * + * @retval 1 Output state is high + * @retval 0 Output state is low + * @retval -errno code Failure + */ +__syscall int comparator_get_output(const struct device *dev); + +static inline int z_impl_comparator_get_output(const struct device *dev) +{ + const struct comparator_driver_api *api = + (const struct comparator_driver_api *)dev->api; + + return api->get_output(dev); +} + +/** + * @brief Set comparator's trigger + * + * @param dev Comparator device + * @param trigger Trigger for signal and callback + * + * @retval 0 Successful + * @retval -errno code Failure + */ +__syscall int comparator_set_trigger(const struct device *dev, + enum comparator_trigger trigger); + +static inline int z_impl_comparator_set_trigger(const struct device *dev, + enum comparator_trigger trigger) +{ + const struct comparator_driver_api *api = + (const struct comparator_driver_api *)dev->api; + + return api->set_trigger(dev, trigger); +} + +/** + * @brief Set comparator's trigger callback + * + * @param dev Comparator device + * @param callback Trigger callback + * @param user_data User data passed to callback + * + * @retval 0 Successful + * @retval -errno code Failure + * + * @note Set callback to NULL to disable callback + * @note Callback is called immediately if trigger is pending + */ +static inline int comparator_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + const struct comparator_driver_api *api = + (const struct comparator_driver_api *)dev->api; + + return api->set_trigger_callback(dev, callback, user_data); +} + +/** + * @brief Check if comparator's trigger is pending and clear it + * + * @param dev Comparator device + * + * @retval 1 Trigger was pending + * @retval 0 Trigger was cleared + * @retval -errno code Failure + */ +__syscall int comparator_trigger_is_pending(const struct device *dev); + +static inline int z_impl_comparator_trigger_is_pending(const struct device *dev) +{ + const struct comparator_driver_api *api = + (const struct comparator_driver_api *)dev->api; + + return api->trigger_is_pending(dev); +} + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_H_ */ diff --git a/include/zephyr/drivers/comparator/fake_comp.h b/include/zephyr/drivers/comparator/fake_comp.h new file mode 100644 index 00000000000..c24858c1997 --- /dev/null +++ b/include/zephyr/drivers/comparator/fake_comp.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_FAKE_H_ +#define ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_FAKE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DECLARE_FAKE_VALUE_FUNC(int, + comp_fake_comp_get_output, + const struct device *); + +DECLARE_FAKE_VALUE_FUNC(int, + comp_fake_comp_set_trigger, + const struct device *, + enum comparator_trigger); + +DECLARE_FAKE_VALUE_FUNC(int, + comp_fake_comp_set_trigger_callback, + const struct device *, + comparator_callback_t, + void *); + +DECLARE_FAKE_VALUE_FUNC(int, + comp_fake_comp_trigger_is_pending, + const struct device *); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_COMPARATOR_FAKE_H_ */ diff --git a/include/zephyr/drivers/comparator/mcux_acmp.h b/include/zephyr/drivers/comparator/mcux_acmp.h new file mode 100644 index 00000000000..3d06644f0e8 --- /dev/null +++ b/include/zephyr/drivers/comparator/mcux_acmp.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_ +#define ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum comp_mcux_acmp_offset_mode { + COMP_MCUX_ACMP_OFFSET_MODE_LEVEL0 = 0, + COMP_MCUX_ACMP_OFFSET_MODE_LEVEL1, +}; + +enum comp_mcux_acmp_hysteresis_mode { + COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL0 = 0, + COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL1, + COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL2, + COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL3, +}; + +struct comp_mcux_acmp_mode_config { + enum comp_mcux_acmp_offset_mode offset_mode; + enum comp_mcux_acmp_hysteresis_mode hysteresis_mode; + bool enable_high_speed_mode; + bool invert_output; + bool use_unfiltered_output; + bool enable_pin_output; +}; + +enum comp_mcux_acmp_mux_input { + COMP_MCUX_ACMP_MUX_INPUT_IN0 = 0, + COMP_MCUX_ACMP_MUX_INPUT_IN1, + COMP_MCUX_ACMP_MUX_INPUT_IN2, + COMP_MCUX_ACMP_MUX_INPUT_IN3, + COMP_MCUX_ACMP_MUX_INPUT_IN4, + COMP_MCUX_ACMP_MUX_INPUT_IN5, + COMP_MCUX_ACMP_MUX_INPUT_IN6, + COMP_MCUX_ACMP_MUX_INPUT_IN7, +}; + +enum comp_mcux_acmp_port_input { + COMP_MCUX_ACMP_PORT_INPUT_DAC = 0, + COMP_MCUX_ACMP_PORT_INPUT_MUX, +}; + +struct comp_mcux_acmp_input_config { + enum comp_mcux_acmp_mux_input positive_mux_input; + enum comp_mcux_acmp_mux_input negative_mux_input; + enum comp_mcux_acmp_port_input positive_port_input; + enum comp_mcux_acmp_port_input negative_port_input; +}; + +struct comp_mcux_acmp_filter_config { + bool enable_sample; + uint8_t filter_count; + uint8_t filter_period; +}; + +enum comp_mcux_acmp_dac_vref_source { + COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN1 = 0, + COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN2, +}; + +struct comp_mcux_acmp_dac_config { + enum comp_mcux_acmp_dac_vref_source vref_source; + uint8_t value; + bool enable_output; + bool enable_high_speed_mode; +}; + +enum comp_mcux_acmp_dm_clock { + COMP_MCUX_ACMP_DM_CLOCK_SLOW = 0, + COMP_MCUX_ACMP_DM_CLOCK_FAST, +}; + +enum comp_mcux_acmp_dm_sample_time { + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T1 = 0, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T2, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T4, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T8, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T16, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T32, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T64, + COMP_MCUX_ACMP_DM_SAMPLE_TIME_T256, +}; + +enum comp_mcux_acmp_dm_phase_time { + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT0 = 0, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT1, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT2, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT3, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT4, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT5, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT6, + COMP_MCUX_ACMP_DM_PHASE_TIME_ALT7, +}; + +struct comp_mcux_acmp_dm_config { + bool enable_positive_channel; + bool enable_negative_channel; + bool enable_resistor_divider; + enum comp_mcux_acmp_dm_clock clock_source; + enum comp_mcux_acmp_dm_sample_time sample_time; + enum comp_mcux_acmp_dm_phase_time phase1_time; + enum comp_mcux_acmp_dm_phase_time phase2_time; +}; + +int comp_mcux_acmp_set_mode_config(const struct device *dev, + const struct comp_mcux_acmp_mode_config *config); + +int comp_mcux_acmp_set_input_config(const struct device *dev, + const struct comp_mcux_acmp_input_config *config); + +int comp_mcux_acmp_set_filter_config(const struct device *dev, + const struct comp_mcux_acmp_filter_config *config); + +int comp_mcux_acmp_set_dac_config(const struct device *dev, + const struct comp_mcux_acmp_dac_config *config); + +int comp_mcux_acmp_set_dm_config(const struct device *dev, + const struct comp_mcux_acmp_dm_config *config); + +int comp_mcux_acmp_set_window_mode(const struct device *dev, bool enable); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_ */ diff --git a/include/zephyr/drivers/comparator/nrf_comp.h b/include/zephyr/drivers/comparator/nrf_comp.h new file mode 100644 index 00000000000..59e1cbbb3ce --- /dev/null +++ b/include/zephyr/drivers/comparator/nrf_comp.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_COMP_H_ +#define ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_COMP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Positive input selection */ +enum comp_nrf_comp_psel { + /** AIN0 external input */ + COMP_NRF_COMP_PSEL_AIN0, + /** AIN1 external input */ + COMP_NRF_COMP_PSEL_AIN1, + /** AIN2 external input */ + COMP_NRF_COMP_PSEL_AIN2, + /** AIN3 external input */ + COMP_NRF_COMP_PSEL_AIN3, + /** AIN4 external input */ + COMP_NRF_COMP_PSEL_AIN4, + /** AIN5 external input */ + COMP_NRF_COMP_PSEL_AIN5, + /** AIN6 external input */ + COMP_NRF_COMP_PSEL_AIN6, + /** AIN7 external input */ + COMP_NRF_COMP_PSEL_AIN7, + /** VDD / 2 */ + COMP_NRF_COMP_PSEL_VDD_DIV2, + /** VDDH / 5 */ + COMP_NRF_COMP_PSEL_VDDH_DIV5, +}; + +/** External reference selection */ +enum comp_nrf_comp_extrefsel { + /** AIN0 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN0, + /** AIN1 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN1, + /** AIN2 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN2, + /** AIN3 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN3, + /** AIN4 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN4, + /** AIN5 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN5, + /** AIN6 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN6, + /** AIN7 external input */ + COMP_NRF_COMP_EXTREFSEL_AIN7, +}; + +/** Reference selection */ +enum comp_nrf_comp_refsel { + /** Internal 1.2V reference */ + COMP_NRF_COMP_REFSEL_INT_1V2, + /** Internal 1.8V reference */ + COMP_NRF_COMP_REFSEL_INT_1V8, + /** Internal 2.4V reference */ + COMP_NRF_COMP_REFSEL_INT_2V4, + /** AVDD 1.8V reference */ + COMP_NRF_COMP_REFSEL_AVDDAO1V8, + /** VDD reference */ + COMP_NRF_COMP_REFSEL_VDD, + /** Use external analog reference */ + COMP_NRF_COMP_REFSEL_AREF, +}; + +/** Speed mode selection */ +enum comp_nrf_comp_sp_mode { + /** Low-power mode */ + COMP_NRF_COMP_SP_MODE_LOW, + /** Normal mode */ + COMP_NRF_COMP_SP_MODE_NORMAL, + /** High-speed mode */ + COMP_NRF_COMP_SP_MODE_HIGH, +}; + +/** Current source configuration */ +enum comp_nrf_comp_isource { + /** Current source disabled */ + COMP_NRF_COMP_ISOURCE_DISABLED, + /** 2.5uA current source enabled */ + COMP_NRF_COMP_ISOURCE_2UA5, + /** 5uA current source enabled */ + COMP_NRF_COMP_ISOURCE_5UA, + /** 10uA current source enabled */ + COMP_NRF_COMP_ISOURCE_10UA, +}; + +/** + * @brief Single-ended mode configuration structure + * + * @note extrefsel is only used if refsel == COMP_NRF_COMP_REFSEL_AREF + * @note Hysteresis down in volts = ((th_down + 1) / 64) * ref + * @note Hysteresis up in volts = ((th_up + 1) / 64) * ref + */ +struct comp_nrf_comp_se_config { + /** Positive input selection */ + enum comp_nrf_comp_psel psel; + /** Speed mode selection */ + enum comp_nrf_comp_sp_mode sp_mode; + /** Current source configuration */ + enum comp_nrf_comp_isource isource; + /** External reference selection */ + enum comp_nrf_comp_extrefsel extrefsel; + /** Reference selection */ + enum comp_nrf_comp_refsel refsel; + /** Hysteresis down threshold configuration */ + uint8_t th_down; + /** Hysteresis up threshold configuration */ + uint8_t th_up; +}; + +/** + * @brief Configure comparator in single-ended mode + * + * @param dev Comparator device instance + * @param config Single-ended mode configuration + * + * @retval 0 if successful + * @retval negative errno-code otherwise + */ +int comp_nrf_comp_configure_se(const struct device *dev, + const struct comp_nrf_comp_se_config *config); + +/** Differential mode configuration structure */ +struct comp_nrf_comp_diff_config { + /** Positive input selection */ + enum comp_nrf_comp_psel psel; + /** Speed mode selection */ + enum comp_nrf_comp_sp_mode sp_mode; + /** Current source configuration */ + enum comp_nrf_comp_isource isource; + /** Negative input selection */ + enum comp_nrf_comp_extrefsel extrefsel; + /** Hysteresis configuration */ + bool enable_hyst; +}; + +/** + * @brief Configure comparator in differential mode + * + * @param dev Comparator device instance + * @param config Differential mode configuration + * + * @retval 0 if successful + * @retval negative errno-code otherwise + */ +int comp_nrf_comp_configure_diff(const struct device *dev, + const struct comp_nrf_comp_diff_config *config); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_COMP_H_ */ diff --git a/include/zephyr/drivers/comparator/nrf_lpcomp.h b/include/zephyr/drivers/comparator/nrf_lpcomp.h new file mode 100644 index 00000000000..e1f2343a8de --- /dev/null +++ b/include/zephyr/drivers/comparator/nrf_lpcomp.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_LPCOMP_H_ +#define ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_LPCOMP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Positive input selection */ +enum comp_nrf_lpcomp_psel { + /** AIN0 external input */ + COMP_NRF_LPCOMP_PSEL_AIN0, + /** AIN1 external input */ + COMP_NRF_LPCOMP_PSEL_AIN1, + /** AIN2 external input */ + COMP_NRF_LPCOMP_PSEL_AIN2, + /** AIN3 external input */ + COMP_NRF_LPCOMP_PSEL_AIN3, + /** AIN4 external input */ + COMP_NRF_LPCOMP_PSEL_AIN4, + /** AIN5 external input */ + COMP_NRF_LPCOMP_PSEL_AIN5, + /** AIN6 external input */ + COMP_NRF_LPCOMP_PSEL_AIN6, + /** AIN7 external input */ + COMP_NRF_LPCOMP_PSEL_AIN7, +}; + +/** External reference selection */ +enum comp_nrf_lpcomp_extrefsel { + /** AIN0 external input */ + COMP_NRF_LPCOMP_EXTREFSEL_AIN0, + /** AIN1 external input */ + COMP_NRF_LPCOMP_EXTREFSEL_AIN1, +}; + +/** Reference selection */ +enum comp_nrf_lpcomp_refsel { + /** Use (VDD * (1/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_1_8, + /** Use (VDD * (2/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_2_8, + /** Use (VDD * (3/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_3_8, + /** Use (VDD * (4/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_4_8, + /** Use (VDD * (5/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_5_8, + /** Use (VDD * (6/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_6_8, + /** Use (VDD * (7/8)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_7_8, + /** Use (VDD * (1/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_1_16, + /** Use (VDD * (3/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_3_16, + /** Use (VDD * (5/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_5_16, + /** Use (VDD * (7/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_7_16, + /** Use (VDD * (9/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_9_16, + /** Use (VDD * (11/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_11_16, + /** Use (VDD * (13/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_13_16, + /** Use (VDD * (15/16)) as reference */ + COMP_NRF_LPCOMP_REFSEL_VDD_15_16, + /** Use external analog reference */ + COMP_NRF_LPCOMP_REFSEL_AREF, +}; + +/** + * @brief Configuration structure + * + * @note extrefsel is only used if refsel == COMP_NRF_LPCOMP_REFSEL_AREF + */ +struct comp_nrf_lpcomp_config { + /** Positive input selection */ + enum comp_nrf_lpcomp_psel psel; + /** External reference selection */ + enum comp_nrf_lpcomp_extrefsel extrefsel; + /** Reference selection */ + enum comp_nrf_lpcomp_refsel refsel; + /** Hysteresis configuration */ + bool enable_hyst; +}; + +/** + * @brief Configure comparator + * + * @param dev Comparator device instance + * @param config Configuration + * + * @retval 0 if successful + * @retval negative errno-code otherwise + */ +int comp_nrf_lpcomp_configure(const struct device *dev, + const struct comp_nrf_lpcomp_config *config); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_COMP_NRF_LPCOMP_H_ */ diff --git a/modules/hal_nxp/Kconfig b/modules/hal_nxp/Kconfig index aaec4e84aba..4f055faa07d 100644 --- a/modules/hal_nxp/Kconfig +++ b/modules/hal_nxp/Kconfig @@ -4,4 +4,5 @@ # SPDX-License-Identifier: Apache-2.0 # -# file is empty and kept as a place holder if/when Kconfig is needed +config MCUX_ACMP + bool "Include ACMP driver from MCUX SDK" diff --git a/samples/sensor/mcux_acmp/prj.conf b/samples/sensor/mcux_acmp/prj.conf index 8b70c7ae30b..202fe6b8564 100644 --- a/samples/sensor/mcux_acmp/prj.conf +++ b/samples/sensor/mcux_acmp/prj.conf @@ -1,2 +1,2 @@ CONFIG_SENSOR=y -CONFIG_MCUX_ACMP_TRIGGER=y +CONFIG_SENSOR_MCUX_ACMP_TRIGGER=y diff --git a/samples/sensor/mcux_acmp/sample.yaml b/samples/sensor/mcux_acmp/sample.yaml index fa7782d896d..a00773fdbb6 100644 --- a/samples/sensor/mcux_acmp/sample.yaml +++ b/samples/sensor/mcux_acmp/sample.yaml @@ -27,4 +27,4 @@ tests: sample.sensor.mcux_acmp.no_trigger: build_only: true extra_configs: - - CONFIG_MCUX_ACMP_TRIGGER=n + - CONFIG_SENSOR_MCUX_ACMP_TRIGGER=n diff --git a/tests/drivers/build_all/comparator/CMakeLists.txt b/tests/drivers/build_all/comparator/CMakeLists.txt new file mode 100644 index 00000000000..edb50bd78e5 --- /dev/null +++ b/tests/drivers/build_all/comparator/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(build_all_comparator) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_dac.dts b/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_dac.dts new file mode 100644 index 00000000000..86ee224d74c --- /dev/null +++ b/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_dac.dts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + acmp1_default: acmp1_default { + group0 { + pinmux = <&iomuxc_gpio_ad_01_acmp1_in2>; + drive-strength = "high"; + bias-pull-up; + slew-rate = "fast"; + }; + }; +}; + +&acmp1 { + status = "okay"; + pinctrl-0 = <&acmp1_default>; + pinctrl-names = "default"; + + positive-mux-input = "IN2"; + positive-port-input = "MUX"; + negative-mux-input = "IN2"; + negative-port-input = "DAC"; + + dac-vref-source = "VIN1"; + dac-value = <128>; + dac-enable; + + offset-mode = "LEVEL0"; + hysteresis-mode = "LEVEL0"; + enable-high-speed-mode; + + filter-enable-sample; + filter-count = <4>; + filter-period = <32>; + + discrete-mode-enable-positive-channel; + discrete-mode-enable-resistor-divider; + discrete-mode-clock-source = "FAST"; + discrete-mode-sample-time = "T1"; + discrete-mode-phase1-time = "ALT4"; + discrete-mode-phase2-time = "ALT7"; +}; diff --git a/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_mux.dts b/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_mux.dts new file mode 100644 index 00000000000..a240e8ce4fb --- /dev/null +++ b/tests/drivers/build_all/comparator/mcux_acmp/mimxrt1176_mux_mux.dts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + acmp1_default: acmp1_default { + group0 { + pinmux = <&iomuxc_gpio_ad_00_acmp1_in1>, + <&iomuxc_gpio_ad_01_acmp1_in2>; + drive-strength = "high"; + bias-pull-up; + slew-rate = "fast"; + }; + }; +}; + +&acmp1 { + status = "okay"; + pinctrl-0 = <&acmp1_default>; + pinctrl-names = "default"; + + positive-mux-input = "IN1"; + negative-mux-input = "IN2"; +}; diff --git a/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_dac.dts b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_dac.dts new file mode 100644 index 00000000000..23f8bb7c4fd --- /dev/null +++ b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_dac.dts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mke15z7_pinctrl.dtsi" + +&cmp0 { + pinctrl-0 = <&cmp0_default>; + pinctrl-names = "default"; + status = "okay"; + + positive-mux-input = "IN0"; + positive-port-input = "MUX"; + negative-mux-input = "IN0"; + negative-port-input = "DAC"; + + dac-vref-source = "VIN1"; + dac-value = <128>; + dac-enable; +}; diff --git a/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_mux.dts b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_mux.dts new file mode 100644 index 00000000000..61e0353d5db --- /dev/null +++ b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_mux_mux.dts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mke15z7_pinctrl.dtsi" + +&cmp0 { + pinctrl-0 = <&cmp0_default>; + pinctrl-names = "default"; + status = "okay"; + + positive-mux-input = "IN0"; + negative-mux-input = "IN1"; +}; diff --git a/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_pinctrl.dtsi b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_pinctrl.dtsi new file mode 100644 index 00000000000..62bddf7b924 --- /dev/null +++ b/tests/drivers/build_all/comparator/mcux_acmp/mke15z7_pinctrl.dtsi @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +&pinctrl { + cmp0_default: cmp0_default { + group0 { + pinmux = , + , + , + ; + + drive-strength = "high"; + }; + }; +}; diff --git a/tests/drivers/build_all/comparator/nrf_comp/diff.overlay b/tests/drivers/build_all/comparator/nrf_comp/diff.overlay new file mode 100644 index 00000000000..8b8e9a02c2b --- /dev/null +++ b/tests/drivers/build_all/comparator/nrf_comp/diff.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "DIFF"; + psel = "AIN0"; + extrefsel = "AIN1"; + sp-mode = "HIGH"; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/comparator/nrf_comp/se.overlay b/tests/drivers/build_all/comparator/nrf_comp/se.overlay new file mode 100644 index 00000000000..e4eb56f61a9 --- /dev/null +++ b/tests/drivers/build_all/comparator/nrf_comp/se.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "SE"; + psel = "AIN0"; + refsel = "INT_1V2"; + sp-mode = "HIGH"; + th-up = <36>; + th-down = <28>; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/comparator/nrf_comp/se_aref.overlay b/tests/drivers/build_all/comparator/nrf_comp/se_aref.overlay new file mode 100644 index 00000000000..0d36a3e40b4 --- /dev/null +++ b/tests/drivers/build_all/comparator/nrf_comp/se_aref.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "SE"; + psel = "AIN0"; + extrefsel = "AIN1"; + refsel = "AREF"; + sp-mode = "HIGH"; + th-up = <36>; + th-down = <28>; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/comparator/nrf_lpcomp/ext_ref.overlay b/tests/drivers/build_all/comparator/nrf_lpcomp/ext_ref.overlay new file mode 100644 index 00000000000..95e44fbed3d --- /dev/null +++ b/tests/drivers/build_all/comparator/nrf_lpcomp/ext_ref.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + compatible = "nordic,nrf-lpcomp"; + psel = "AIN0"; + refsel = "VDD_4_8"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/comparator/nrf_lpcomp/int_ref.overlay b/tests/drivers/build_all/comparator/nrf_lpcomp/int_ref.overlay new file mode 100644 index 00000000000..7aadd8b3fae --- /dev/null +++ b/tests/drivers/build_all/comparator/nrf_lpcomp/int_ref.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + compatible = "nordic,nrf-lpcomp"; + psel = "AIN0"; + refsel = "AREF"; + extrefsel = "AIN1"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/comparator/prj.conf b/tests/drivers/build_all/comparator/prj.conf new file mode 100644 index 00000000000..9e78e0bbe53 --- /dev/null +++ b/tests/drivers/build_all/comparator/prj.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_TEST=y +CONFIG_TEST_USERSPACE=y +CONFIG_COMPARATOR=y diff --git a/tests/drivers/build_all/comparator/src/main.c b/tests/drivers/build_all/comparator/src/main.c new file mode 100644 index 00000000000..a05ee591430 --- /dev/null +++ b/tests/drivers/build_all/comparator/src/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int main(void) +{ + return 0; +} diff --git a/tests/drivers/build_all/comparator/testcase.yaml b/tests/drivers/build_all/comparator/testcase.yaml new file mode 100644 index 00000000000..a0578daa9ff --- /dev/null +++ b/tests/drivers/build_all/comparator/testcase.yaml @@ -0,0 +1,90 @@ +common: + build_only: true + tags: + - drivers + - comparator +tests: + drivers.build_all.comparator.nrf_comp.diff: + extra_args: + - DTC_OVERLAY_FILE="nrf_comp/diff.overlay" + platform_allow: + - nrf52dk/nrf52810 + - nrf52dk/nrf52832 + - nrf52833dk/nrf52820 + - nrf52833dk/nrf52833 + - nrf52840dk/nrf52811 + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf9280pdk/nrf9280/cpuapp + drivers.build_all.comparator.nrf_comp.se_aref: + extra_args: + - DTC_OVERLAY_FILE="nrf_comp/se_aref.overlay" + platform_allow: + - nrf52dk/nrf52810 + - nrf52dk/nrf52832 + - nrf52833dk/nrf52820 + - nrf52833dk/nrf52833 + - nrf52840dk/nrf52811 + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf9280pdk/nrf9280/cpuapp + drivers.build_all.comparator.nrf_comp.se: + extra_args: + - DTC_OVERLAY_FILE="nrf_comp/se.overlay" + platform_allow: + - nrf52dk/nrf52810 + - nrf52dk/nrf52832 + - nrf52833dk/nrf52820 + - nrf52833dk/nrf52833 + - nrf52840dk/nrf52811 + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf9280pdk/nrf9280/cpuapp + drivers.build_all.comparator.nrf_lpcomp.ext_ref: + extra_args: + - DTC_OVERLAY_FILE="nrf_lpcomp/ext_ref.overlay" + platform_allow: + - nrf51dk/nrf51822 + - nrf52dk/nrf52832 + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf9280pdk/nrf9280/cpuapp + drivers.build_all.comparator.nrf_lpcomp.int_ref: + extra_args: + - DTC_OVERLAY_FILE="nrf_lpcomp/int_ref.overlay" + platform_allow: + - nrf51dk/nrf51822 + - nrf52dk/nrf52832 + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf9280pdk/nrf9280/cpuapp + drivers.build_all.comparator.mcux_acmp.mimxrt1176_mux_dac: + extra_args: + - DTC_OVERLAY_FILE="mcux_acmp/mimxrt1176_mux_dac.dts" + platform_allow: + - mimxrt1170_evk/mimxrt1176/cm4 + - mimxrt1170_evk/mimxrt1176/cm7 + drivers.build_all.comparator.mcux_acmp.mimxrt1176_mux_mux: + extra_args: + - DTC_OVERLAY_FILE="mcux_acmp/mimxrt1176_mux_mux.dts" + platform_allow: + - mimxrt1170_evk/mimxrt1176/cm4 + - mimxrt1170_evk/mimxrt1176/cm7 + drivers.build_all.comparator.mcux_acmp.mke15z7_mux_dac: + extra_args: + - DTC_OVERLAY_FILE="mcux_acmp/mke15z7_mux_dac.dts" + platform_allow: + - frdm_ke15z + drivers.build_all.comparator.mcux_acmp.mke15z7_mux_mux: + extra_args: + - DTC_OVERLAY_FILE="mcux_acmp/mke15z7_mux_mux.dts" + platform_allow: + - frdm_ke15z diff --git a/tests/drivers/comparator/gpio_loopback/CMakeLists.txt b/tests/drivers/comparator/gpio_loopback/CMakeLists.txt new file mode 100644 index 00000000000..43c6821adab --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(comparator_gpio_loopback) + +target_sources(app PRIVATE src/test.c) diff --git a/tests/drivers/comparator/gpio_loopback/boards/frdm_ke15z.overlay b/tests/drivers/comparator/gpio_loopback/boards/frdm_ke15z.overlay new file mode 100644 index 00000000000..531275ceb63 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/frdm_ke15z.overlay @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* + * PTA1 looped back to PTA0 + */ + +/ { + aliases { + test-comp = &cmp0; + }; + + zephyr,user { + test-gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpioa { + status = "okay"; +}; + +&pinctrl { + cmp0_default: cmp0_default { + group0 { + pinmux = ; + drive-strength = "high"; + }; + }; +}; + +&cmp0 { + pinctrl-0 = <&cmp0_default>; + pinctrl-names = "default"; + status = "okay"; + + positive-mux-input = "IN0"; + positive-port-input = "MUX"; + negative-mux-input = "IN0"; + negative-port-input = "DAC"; + + dac-vref-source = "VIN1"; + dac-value = <128>; + dac-enable; +}; diff --git a/tests/drivers/comparator/gpio_loopback/boards/nrf5340dk_nrf5340_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..a837527e342 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* + * P0.05 looped back to P0.04 + */ + +/ { + aliases { + test-comp = ∁ + }; + + zephyr,user { + test-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio0{ + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..aa39ab60e9e --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* + * P1.03 looped back to P1.02 + */ + +/ { + aliases { + test-comp = ∁ + }; + + zephyr,user { + test-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio1 { + status = "okay"; +}; + +/* Temporary workaround to reserve P1.03 for cpuapp */ +&led1 { + gpios = < &gpio1 0x3 0x0 >; +}; + +/* Temporary workaround to reserve P1.02 for cpuapp */ +&led2 { + gpios = < &gpio1 0x2 0x0 >; +}; diff --git a/tests/drivers/comparator/gpio_loopback/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..768965e7c58 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* + * P1.10 looped back to P1.11 + */ + +/ { + aliases { + test-comp = ∁ + }; + + zephyr,user { + test-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio1 { + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/prj.conf b/tests/drivers/comparator/gpio_loopback/prj.conf new file mode 100644 index 00000000000..5ca57533d76 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/prj.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ZTEST=y +CONFIG_GPIO=y +CONFIG_COMPARATOR=y diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf5340dk_nrf5340_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..e11bdcd3173 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "SE"; + psel = "AIN0"; /* P0.04 */ + refsel = "VDD"; + sp-mode = "HIGH"; + th-up = <34>; + th-down = <30>; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..f90c2051255 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "SE"; + psel = "AIN2"; /* P1.02 */ + refsel = "INT_1V2"; + sp-mode = "HIGH"; + th-up = <63>; + th-down = <59>; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..3a7a8f0ef81 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + main-mode = "SE"; + psel = "AIN4"; /* P1.11 */ + refsel = "INT_1V2"; + sp-mode = "HIGH"; + th-up = <63>; + th-down = <59>; + isource = "DISABLED"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/snippet.yml b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/snippet.yml new file mode 100644 index 00000000000..e030c0e5543 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_comp/snippet.yml @@ -0,0 +1,15 @@ +# Copyright 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +name: gpio_loopback_nrf_comp + +boards: + nrf54h20dk/nrf54h20/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf54h20dk_nrf54h20_cpuapp.overlay + nrf54l15dk/nrf54l15/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf54l15dk_nrf54l15_cpuapp.overlay + nrf5340dk/nrf5340/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf5340dk_nrf5340_cpuapp.overlay diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf5340dk_nrf5340_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..d35a20dfc22 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + compatible = "nordic,nrf-lpcomp"; + psel = "AIN0"; /* P0.04 */ + refsel = "VDD_4_8"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..349cd7051f9 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + compatible = "nordic,nrf-lpcomp"; + psel = "AIN2"; /* P1.02 */ + refsel = "VDD_4_8"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..ebb652bdd87 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&comp { + compatible = "nordic,nrf-lpcomp"; + psel = "AIN4"; /* P1.11 */ + refsel = "VDD_4_8"; + status = "okay"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/snippet.yml b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/snippet.yml new file mode 100644 index 00000000000..70ff2a7e314 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/nrf_lpcomp/snippet.yml @@ -0,0 +1,15 @@ +# Copyright 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +name: gpio_loopback_nrf_lpcomp + +boards: + nrf54h20dk/nrf54h20/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf54h20dk_nrf54h20_cpuapp.overlay + nrf54l15dk/nrf54l15/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf54l15dk_nrf54l15_cpuapp.overlay + nrf5340dk/nrf5340/cpuapp: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nrf5340dk_nrf5340_cpuapp.overlay diff --git a/tests/drivers/comparator/gpio_loopback/src/test.c b/tests/drivers/comparator/gpio_loopback/src/test.c new file mode 100644 index 00000000000..930422b1a65 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/src/test.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +static const struct device *test_dev = DEVICE_DT_GET(DT_ALIAS(test_comp)); +static const struct gpio_dt_spec test_pin = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), test_gpios); +static K_SEM_DEFINE(test_sem, 0, 1); + +static void test_callback(const struct device *dev, void *user_data) +{ + zassert_equal(&test_sem, user_data); + k_sem_give(&test_sem); +} + +static void *test_setup(void) +{ + zassert_ok(gpio_pin_configure_dt(&test_pin, GPIO_OUTPUT_INACTIVE)); + return NULL; +} + +static void test_before(void *f) +{ + ARG_UNUSED(f); + + k_sem_reset(&test_sem); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + zassert_ok(comparator_set_trigger(test_dev, COMPARATOR_TRIGGER_NONE)); + zassert_ok(comparator_set_trigger_callback(test_dev, NULL, NULL)); + zassert_between_inclusive(comparator_trigger_is_pending(test_dev), 0, 1); +} + +ZTEST_SUITE(comparator_gpio_loopback, NULL, test_setup, test_before, NULL, NULL); + +ZTEST(comparator_gpio_loopback, test_get_output) +{ + zassert_equal(comparator_get_output(test_dev), 0); + k_msleep(1); + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_equal(comparator_get_output(test_dev), 1); + k_msleep(1); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + k_msleep(1); + zassert_equal(comparator_get_output(test_dev), 0); +} + +ZTEST(comparator_gpio_loopback, test_no_trigger_no_pending) +{ + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); +} + +ZTEST(comparator_gpio_loopback, test_trigger_rising_edge_pending) +{ + zassert_ok(comparator_set_trigger(test_dev, COMPARATOR_TRIGGER_RISING_EDGE)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); +} + +ZTEST(comparator_gpio_loopback, test_trigger_falling_edge_pending) +{ + zassert_ok(comparator_set_trigger(test_dev, COMPARATOR_TRIGGER_FALLING_EDGE)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); +} + +ZTEST(comparator_gpio_loopback, test_trigger_both_edges_pending) +{ + zassert_ok(comparator_set_trigger(test_dev, COMPARATOR_TRIGGER_BOTH_EDGES)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); + zassert_ok(gpio_pin_set_dt(&test_pin, 0)); + k_msleep(1); + zassert_equal(comparator_trigger_is_pending(test_dev), 1); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); +} + +ZTEST(comparator_gpio_loopback, test_trigger_callback) +{ + zassert_ok(comparator_set_trigger_callback(test_dev, test_callback, &test_sem)); + k_msleep(1); + zassert_equal(k_sem_take(&test_sem, K_NO_WAIT), -EBUSY); + zassert_ok(comparator_set_trigger(test_dev, COMPARATOR_TRIGGER_RISING_EDGE)); + k_msleep(1); + zassert_equal(k_sem_take(&test_sem, K_NO_WAIT), -EBUSY); + zassert_ok(gpio_pin_set_dt(&test_pin, 1)); + k_msleep(1); + zassert_ok(k_sem_take(&test_sem, K_NO_WAIT)); + zassert_equal(comparator_trigger_is_pending(test_dev), 0); +} diff --git a/tests/drivers/comparator/gpio_loopback/testcase.yaml b/tests/drivers/comparator/gpio_loopback/testcase.yaml new file mode 100644 index 00000000000..beb5cd39276 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/testcase.yaml @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +common: + harness_config: + fixture: gpio_loopback + tags: + - drivers + - comparator +tests: + drivers.comparator.gpio_loopback.mcux_acmp: + platform_allow: + - frdm_ke15z + drivers.comparator.gpio_loopback.nrf_comp: + extra_args: + - SNIPPET="gpio_loopback_nrf_comp" + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf5340dk/nrf5340/cpuapp + drivers.comparator.gpio_loopback.nrf_lpcomp: + extra_args: + - SNIPPET="gpio_loopback_nrf_lpcomp" + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + - nrf54l15dk/nrf54l15/cpuapp + - nrf5340dk/nrf5340/cpuapp diff --git a/tests/drivers/comparator/shell/CMakeLists.txt b/tests/drivers/comparator/shell/CMakeLists.txt new file mode 100644 index 00000000000..c2f34f71e68 --- /dev/null +++ b/tests/drivers/comparator/shell/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(comparator_shell) + +target_sources(app PRIVATE src/test.c) diff --git a/tests/drivers/comparator/shell/app.overlay b/tests/drivers/comparator/shell/app.overlay new file mode 100644 index 00000000000..3bb03b5a569 --- /dev/null +++ b/tests/drivers/comparator/shell/app.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/{ + fake_comp: fake_comp { + compatible = "zephyr,fake-comp"; + status = "okay"; + }; +}; diff --git a/tests/drivers/comparator/shell/prj.conf b/tests/drivers/comparator/shell/prj.conf new file mode 100644 index 00000000000..a1eb4324cee --- /dev/null +++ b/tests/drivers/comparator/shell/prj.conf @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SHELL=y +CONFIG_SHELL_BACKEND_SERIAL=n +CONFIG_SHELL_VT100_COMMANDS=n +CONFIG_SHELL_BACKEND_DUMMY=y +CONFIG_COMPARATOR=y +CONFIG_COMPARATOR_SHELL=y +CONFIG_ZTEST=y diff --git a/tests/drivers/comparator/shell/src/test.c b/tests/drivers/comparator/shell/src/test.c new file mode 100644 index 00000000000..dacefc9311a --- /dev/null +++ b/tests/drivers/comparator/shell/src/test.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +DEFINE_FFF_GLOBALS; + +#define FAKE_COMP_NODE DT_NODELABEL(fake_comp) +#define FAKE_COMP_NAME DEVICE_DT_NAME(FAKE_COMP_NODE) + +#define TEST_TRIGGER_DELAY K_SECONDS(1) + +#define TEST_AWAIT_TRIGGER_TIMEOUT_BELOW_MIN_CMD \ + ("comp await_trigger " FAKE_COMP_NAME " 0") + +#define TEST_AWAIT_TRIGGER_TIMEOUT_ABOVE_MAX_CMD \ + ("comp await_trigger " FAKE_COMP_NAME " " \ + STRINGIFY(CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT + 1)) + +#define TEST_AWAIT_TRIGGER_TIMEOUT_BROKEN_CMD \ + ("comp await_trigger " FAKE_COMP_NAME " d") + +static const struct shell *test_sh; +static const struct device *test_dev = DEVICE_DT_GET(FAKE_COMP_NODE); +static comparator_callback_t test_callback; +static void *test_callback_user_data; +static struct k_spinlock test_callback_spinlock; +static struct k_work_delayable test_trigger_dwork; + +static int test_get_output_stub_1(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 1; +} + +static int test_get_output_stub_0(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +static int test_get_output_stub_eio(const struct device *dev) +{ + ARG_UNUSED(dev); + + return -EIO; +} + +static int test_set_trigger_stub_ok(const struct device *dev, enum comparator_trigger trigger) +{ + ARG_UNUSED(dev); + ARG_UNUSED(trigger); + + return 0; +} + +static int test_set_trigger_stub_eio(const struct device *dev, enum comparator_trigger trigger) +{ + ARG_UNUSED(dev); + ARG_UNUSED(trigger); + + return -EIO; +} + +static int test_set_trigger_callback_mock_0(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + ARG_UNUSED(dev); + + K_SPINLOCK(&test_callback_spinlock) { + test_callback = callback; + test_callback_user_data = user_data; + } + + return 0; +} + +static int test_set_trigger_callback_stub_0(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(callback); + ARG_UNUSED(user_data); + + return 0; +} + +static int test_set_trigger_callback_stub_eio(const struct device *dev, + comparator_callback_t callback, + void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(callback); + ARG_UNUSED(user_data); + + return -EIO; +} + +static int test_trigger_is_pending_stub_1(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 1; +} + +static int test_trigger_is_pending_stub_0(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +static int test_trigger_is_pending_stub_eio(const struct device *dev) +{ + ARG_UNUSED(dev); + + return -EIO; +} + + +static void test_trigger_handler(struct k_work *work) +{ + ARG_UNUSED(work); + + test_callback(test_dev, test_callback_user_data); +} + +static void test_schedule_trigger(void) +{ + k_work_schedule(&test_trigger_dwork, TEST_TRIGGER_DELAY); +} + +static void test_cancel_trigger(void) +{ + struct k_work_sync sync; + + k_work_cancel_delayable_sync(&test_trigger_dwork, &sync); +} + +static void *test_setup(void) +{ + k_work_init_delayable(&test_trigger_dwork, test_trigger_handler); + test_sh = shell_backend_dummy_get_ptr(); + WAIT_FOR(shell_ready(test_sh), 20000, k_msleep(1)); + zassert_true(shell_ready(test_sh), "timed out waiting for dummy shell backend"); + return NULL; +} + +static void test_after(void *f) +{ + ARG_UNUSED(f); + + test_cancel_trigger(); +} + +ZTEST(comparator_shell, test_get_output) +{ + int ret; + const char *out; + size_t out_size; + + shell_backend_dummy_clear_output(test_sh); + comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_1; + ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME); + zassert_ok(ret); + zassert_equal(comp_fake_comp_get_output_fake.call_count, 1); + zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\n1\r\n"); + + comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_0; + ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME); + zassert_ok(ret); + zassert_equal(comp_fake_comp_get_output_fake.call_count, 2); + zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\n0\r\n"); + + comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_eio; + ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME); + zassert_equal(ret, -EIO); + zassert_equal(comp_fake_comp_get_output_fake.call_count, 3); + zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\nfailed to get output\r\n"); +} + +ZTEST(comparator_shell, test_set_trigger) +{ + int ret; + const char *out; + size_t out_size; + + comp_fake_comp_set_trigger_fake.custom_fake = test_set_trigger_stub_ok; + + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " NONE"); + zassert_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 1); + zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev); + zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_NONE); + + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " RISING_EDGE"); + zassert_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 2); + zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev); + zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_RISING_EDGE); + + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " FALLING_EDGE"); + zassert_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 3); + zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev); + zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_FALLING_EDGE); + + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " BOTH_EDGES"); + zassert_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 4); + zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev); + zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_BOTH_EDGES); + + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " INVALID"); + zassert_equal(ret, -EINVAL); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 4); + + comp_fake_comp_set_trigger_fake.custom_fake = test_set_trigger_stub_eio; + + shell_backend_dummy_clear_output(test_sh); + ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " BOTH_EDGES"); + zassert_equal(ret, -EIO); + zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 5); + zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev); + zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_BOTH_EDGES); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\nfailed to set trigger\r\n"); +} + +ZTEST(comparator_shell, test_await_trigger_set_callback_fail) +{ + int ret; + const char *out; + size_t out_size; + + shell_backend_dummy_clear_output(test_sh); + comp_fake_comp_set_trigger_callback_fake.custom_fake = test_set_trigger_callback_stub_eio; + ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME); + zassert_ok(0); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 1); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val, 0); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\nfailed to set trigger callback\r\n"); +} + +ZTEST(comparator_shell, test_await_trigger_timeout) +{ + int ret; + const char *out; + size_t out_size; + + shell_backend_dummy_clear_output(test_sh); + comp_fake_comp_set_trigger_callback_fake.custom_fake = test_set_trigger_callback_stub_0; + ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME); + zassert_ok(0); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 2); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[0], 0); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[1], 0); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\ntimed out\r\n"); +} + +ZTEST(comparator_shell, test_await_trigger_invalid_timeout_arg) +{ + int ret; + + ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_BELOW_MIN_CMD); + zassert_not_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0); + + ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_ABOVE_MAX_CMD); + zassert_not_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0); + + ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_BROKEN_CMD); + zassert_not_ok(ret); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0); +} + +ZTEST(comparator_shell, test_await_trigger) +{ + int ret; + const char *out; + size_t out_size; + comparator_api_set_trigger_callback seq[2]; + + shell_backend_dummy_clear_output(test_sh); + seq[0] = test_set_trigger_callback_mock_0; + seq[1] = test_set_trigger_callback_stub_0; + comp_fake_comp_set_trigger_callback_fake.custom_fake_seq = seq; + comp_fake_comp_set_trigger_callback_fake.custom_fake_seq_len = ARRAY_SIZE(seq); + test_schedule_trigger(); + ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME); + zassert_ok(0); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 2); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg0_history[0], test_dev); + zassert_not_equal(comp_fake_comp_set_trigger_callback_fake.arg1_history[0], NULL); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[0], 0); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg0_history[1], test_dev); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg1_history[1], NULL); + zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[1], 0); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\ntriggered\r\n"); +} + +ZTEST(comparator_shell, test_trigger_is_pending) +{ + int ret; + const char *out; + size_t out_size; + + shell_backend_dummy_clear_output(test_sh); + comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_1; + ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME); + zassert_ok(ret); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 1); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\n1\r\n"); + + comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_0; + ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME); + zassert_ok(ret); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 2); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\n0\r\n"); + + comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_eio; + ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME); + zassert_equal(ret, -EIO); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 3); + zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev); + out = shell_backend_dummy_get_output(test_sh, &out_size); + zassert_str_equal(out, "\r\nfailed to get trigger status\r\n"); +} + +ZTEST_SUITE(comparator_shell, NULL, test_setup, test_after, NULL, NULL); diff --git a/tests/drivers/comparator/shell/testcase.yaml b/tests/drivers/comparator/shell/testcase.yaml new file mode 100644 index 00000000000..26d1fc169c3 --- /dev/null +++ b/tests/drivers/comparator/shell/testcase.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +tests: + drivers.comparator.shell: + platform_allow: + - native_sim + - native_sim/native/64 + tags: + - drivers + - comparator + - shell