From 80cc1ca8085cd4637491990ba14ed0f631950a4d Mon Sep 17 00:00:00 2001 From: Alexander Kozhinov Date: Sat, 1 Feb 2025 16:26:10 +0100 Subject: [PATCH 1/2] drivers: comparator: stm32 Add comparator driver for ST's stm32 series. Signed-off-by: Alexander Kozhinov --- drivers/comparator/CMakeLists.txt | 1 + drivers/comparator/Kconfig | 1 + drivers/comparator/Kconfig.stm32_comp | 19 + drivers/comparator/comparator_stm32_comp.c | 378 +++++++++++++++++++ dts/arm/st/g4/stm32g4.dtsi | 27 ++ dts/arm/st/h7/stm32h7.dtsi | 28 ++ dts/bindings/comparator/st,stm32-comp.yaml | 185 +++++++++ dts/bindings/comparator/st,stm32h7-comp.yaml | 38 ++ 8 files changed, 677 insertions(+) create mode 100644 drivers/comparator/Kconfig.stm32_comp create mode 100644 drivers/comparator/comparator_stm32_comp.c create mode 100644 dts/bindings/comparator/st,stm32-comp.yaml create mode 100644 dts/bindings/comparator/st,stm32h7-comp.yaml diff --git a/drivers/comparator/CMakeLists.txt b/drivers/comparator/CMakeLists.txt index cbfa301d580f..9cdb05fc3ae2 100644 --- a/drivers/comparator/CMakeLists.txt +++ b/drivers/comparator/CMakeLists.txt @@ -12,3 +12,4 @@ 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) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_STM32_COMP comparator_stm32_comp.c) diff --git a/drivers/comparator/Kconfig b/drivers/comparator/Kconfig index 79538a6cc222..aa6224b300ba 100644 --- a/drivers/comparator/Kconfig +++ b/drivers/comparator/Kconfig @@ -24,5 +24,6 @@ rsource "Kconfig.mcux_acmp" rsource "Kconfig.nrf_comp" rsource "Kconfig.nrf_lpcomp" rsource "Kconfig.shell" +rsource "Kconfig.stm32_comp" endif # COMPARATOR diff --git a/drivers/comparator/Kconfig.stm32_comp b/drivers/comparator/Kconfig.stm32_comp new file mode 100644 index 000000000000..7a2ae9f383be --- /dev/null +++ b/drivers/comparator/Kconfig.stm32_comp @@ -0,0 +1,19 @@ +# Copyright (c) 2025 Alexander Kozhinov +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_STM32_COMP + bool "ST STM32 comparator driver" + default y + depends on DT_HAS_ST_STM32_COMP_ENABLED + select PINCTRL + select USE_STM32_LL_EXTI # EXTI_STM32 + select USE_STM32_LL_COMP + +if COMPARATOR_STM32_COMP + +config COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING + bool + default n + default y if SOC_SERIES_STM32G4X + +endif # COMPARATOR_STM32_COMP diff --git a/drivers/comparator/comparator_stm32_comp.c b/drivers/comparator/comparator_stm32_comp.c new file mode 100644 index 000000000000..20b89aa0b444 --- /dev/null +++ b/drivers/comparator/comparator_stm32_comp.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2025 Alexander Kozhinov + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +LOG_MODULE_REGISTER(stm32_comp, CONFIG_COMPARATOR_LOG_LEVEL); + +#define DT_DRV_COMPAT st_stm32_comp + +#define LL_COMP_INPUT_PLUS_IN0 LL_COMP_INPUT_PLUS_IO1 +#define LL_COMP_INPUT_PLUS_IN1 LL_COMP_INPUT_PLUS_IO2 + +#define LL_COMP_INPUT_MINUS_IN0 LL_COMP_INPUT_MINUS_IO1 +#define LL_COMP_INPUT_MINUS_IN1 LL_COMP_INPUT_MINUS_IO2 + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp) && defined(CONFIG_CPU_CORTEX_M4) +#define EXTI_CLEAR_FLAG_0_31(exti_line) LL_C2_EXTI_ClearFlag_0_31(exti_line) +#define EXTI_CLEAR_FLAG_32_63(exti_line) LL_C2_EXTI_ClearFlag_32_63(exti_line) + +#define EXTI_IS_ACTIVE_FLAG_0_31(exti_line) LL_C2_EXTI_IsActiveFlag_0_31(exti_line) +#define EXTI_IS_ACTIVE_FLAG_32_63(exti_line) LL_C2_EXTI_IsActiveFlag_32_63(exti_line) +#define EXTI_IS_ACTIVE_FLAG_64_95(exti_line) LL_C2_EXTI_IsActiveFlag_64_95(exti_line) +#else /* st_stm32h7_comp and CONFIG_CPU_CORTEX_M4 */ +#define EXTI_CLEAR_FLAG_0_31(exti_line) LL_EXTI_ClearFlag_0_31(exti_line) +#define EXTI_CLEAR_FLAG_32_63(exti_line) LL_EXTI_ClearFlag_32_63(exti_line) + +#define EXTI_IS_ACTIVE_FLAG_0_31(exti_line) LL_EXTI_IsActiveFlag_0_31(exti_line) +#define EXTI_IS_ACTIVE_FLAG_32_63(exti_line) LL_EXTI_IsActiveFlag_32_63(exti_line) +#define EXTI_IS_ACTIVE_FLAG_64_95(exti_line) LL_EXTI_IsActiveFlag_64_95(exti_line) +#endif /* st_stm32h7_comp and CONFIG_CPU_CORTEX_M4 */ + +#define STM32_COMP_DT_INST_P_IN(inst) \ + CONCAT(LL_COMP_INPUT_PLUS_, DT_INST_STRING_TOKEN(inst, positive_input)) + +#define STM32_COMP_DT_INST_N_IN(inst) \ + CONCAT(LL_COMP_INPUT_MINUS_, DT_INST_STRING_TOKEN(inst, negative_input)) + +#define STM32_COMP_DT_INST_HYST_MODE(inst) \ + CONCAT(LL_COMP_HYSTERESIS_, DT_INST_STRING_TOKEN(inst, hysteresis)) + +#define STM32_COMP_DT_INST_INV_OUT(inst) \ + CONCAT(LL_COMP_OUTPUTPOL_, DT_INST_STRING_TOKEN(inst, invert_output)) + +#define STM32_COMP_DT_INST_BLANK_SEL(inst) \ + CONCAT(LL_COMP_BLANKINGSRC_, DT_INST_STRING_TOKEN(inst, st_blank_sel)) + +#define STM32_COMP_DT_INST_LOCK(inst) DT_INST_PROP(inst, st_lock_enable) + +#define STM32_COMP_DT_MILLER_EFFECT_HOLD_ENABLE(inst) \ + DT_INST_PROP(inst, st_miller_effect_hold_enable) + +#define STM32_COMP_DT_EXTI_LINE(inst) CONCAT(LL_EXTI_LINE_, DT_INST_PROP(inst, st_exti_line)) + +#define STM32_COMP_DT_EXTI_LINE_NUMBER(inst) DT_INST_PROP(inst, st_exti_line) + +#define STM32_COMP_DT_POWER_MODE(inst) \ + CONCAT(LL_COMP_POWERMODE_, DT_INST_STRING_TOKEN(inst, st_power_mode)) + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp) +#define STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst) \ + { \ + .PowerMode = STM32_COMP_DT_POWER_MODE(inst), \ + .InputPlus = STM32_COMP_DT_INST_P_IN(inst), \ + .InputMinus = STM32_COMP_DT_INST_N_IN(inst), \ + .InputHysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \ + .OutputPolarity = STM32_COMP_DT_INST_INV_OUT(inst), \ + .OutputBlankingSource = STM32_COMP_DT_INST_BLANK_SEL(inst), \ + } +#else /* st_stm32h7_comp */ +#define STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst) \ + { \ + .InputPlus = STM32_COMP_DT_INST_P_IN(inst), \ + .InputMinus = STM32_COMP_DT_INST_N_IN(inst), \ + .InputHysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \ + .OutputPolarity = STM32_COMP_DT_INST_INV_OUT(inst), \ + .OutputBlankingSource = STM32_COMP_DT_INST_BLANK_SEL(inst), \ + } +#endif /* st_stm32h7_comp */ + +struct stm32_comp_config { + COMP_TypeDef *comp; + const struct stm32_pclken *pclken; + const struct pinctrl_dev_config *pincfg; + void (*irq_init)(void); + const uint32_t irq_nr; + const LL_COMP_InitTypeDef comp_config; + const uint32_t exti_line; + const uint8_t exti_line_number; + const bool lock_enable; + const bool miller_effect_hold_enable; +}; + +struct stm32_comp_data { + comparator_callback_t callback; + void *user_data; +}; + +__maybe_unused static bool stm32_comp_is_resumed(void) +{ +#if CONFIG_PM_DEVICE + enum pm_device_state state; + + (void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state); + return state == PM_DEVICE_STATE_ACTIVE; +#else + return true; +#endif /* CONFIG_PM_DEVICE */ +} + +static int stm32_comp_get_output(const struct device *dev) +{ + const struct stm32_comp_config *cfg = dev->config; + + return LL_COMP_ReadOutputLevel(cfg->comp); +} + +static int stm32_comp_set_trigger(const struct device *dev, enum comparator_trigger trigger) +{ + const struct stm32_comp_config *cfg = dev->config; + struct stm32_comp_data *data = dev->data; + int ret = 0; + + LL_EXTI_InitTypeDef exti_init_data = {0}; + + LL_EXTI_StructInit(&exti_init_data); + exti_init_data.LineCommand = ENABLE; + exti_init_data.Mode = LL_EXTI_MODE_IT; + + if (cfg->exti_line_number < 32U) { + exti_init_data.Line_0_31 = cfg->exti_line; + } else if (cfg->exti_line_number < 64U) { + exti_init_data.Line_32_63 = cfg->exti_line; + } else { +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp) + exti_init_data.Line_64_95 = cfg->exti_line; +#endif /* st_stm32h7_comp */ + } + + switch (trigger) { + case COMPARATOR_TRIGGER_NONE: + exti_init_data.Trigger = LL_EXTI_TRIGGER_NONE; + break; + + case COMPARATOR_TRIGGER_RISING_EDGE: + exti_init_data.Trigger = LL_EXTI_TRIGGER_RISING; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + exti_init_data.Trigger = LL_EXTI_TRIGGER_FALLING; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + exti_init_data.Trigger = LL_EXTI_TRIGGER_RISING_FALLING; + break; + } + + irq_disable(cfg->irq_nr); + LL_COMP_Disable(cfg->comp); + + ret = (int)LL_EXTI_Init(&exti_init_data); + if (ret != 0) { + LOG_ERR("%s: EXTI init failed (%d)", dev->name, ret); + return ret; + } + + if (stm32_comp_is_resumed()) { + LL_COMP_Enable(cfg->comp); + } + + if (data->callback != NULL) { + irq_enable(cfg->irq_nr); + } + + return ret; +} + +static void exti_clear_flag(const struct stm32_comp_config *cfg) +{ + /* + * If some stm32 series has EXTI comparator line connected above + * line number 63 - the if-else statement below shall be extended accordingly + */ + if (cfg->exti_line_number < 32U) { + EXTI_CLEAR_FLAG_0_31(cfg->exti_line); + } else { + EXTI_CLEAR_FLAG_32_63(cfg->exti_line); + } +} + +static int stm32_comp_trigger_is_pending(const struct device *dev) +{ + const struct stm32_comp_config *cfg = dev->config; + int ret = 0; + + if (cfg->exti_line_number < 32U) { + ret = EXTI_IS_ACTIVE_FLAG_0_31(cfg->exti_line); + } else if (cfg->exti_line_number < 63U) { + ret = EXTI_IS_ACTIVE_FLAG_32_63(cfg->exti_line); + } else { +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp) + ret = EXTI_IS_ACTIVE_FLAG_64_95(cfg->exti_line); +#else /* st_stm32h7_comp */ + ret = -ENOTSUP; +#endif /* st_stm32h7_comp */ + } + exti_clear_flag(cfg); + return ret; +} + +static int stm32_comp_set_trigger_callback(const struct device *dev, comparator_callback_t callback, + void *user_data) +{ + const struct stm32_comp_config *cfg = dev->config; + struct stm32_comp_data *data = dev->data; + + irq_disable(cfg->irq_nr); + + data->callback = callback; + data->user_data = user_data; + + irq_enable(cfg->irq_nr); + + if (data->callback != NULL && stm32_comp_trigger_is_pending(dev)) { + callback(dev, user_data); + } + + return 0; +} + +static DEVICE_API(comparator, stm32_comp_comp_api) = { + .get_output = stm32_comp_get_output, + .set_trigger = stm32_comp_set_trigger, + .set_trigger_callback = stm32_comp_set_trigger_callback, + .trigger_is_pending = stm32_comp_trigger_is_pending, +}; + +static int stm32_comp_pm_callback(const struct device *dev, enum pm_device_action action) +{ + const struct stm32_comp_config *cfg = dev->config; + + if (action == PM_DEVICE_ACTION_RESUME) { + if (cfg->lock_enable) { + LL_COMP_Lock(cfg->comp); + } + LL_COMP_Enable(cfg->comp); + } + +#if CONFIG_PM_DEVICE + if (action == PM_DEVICE_ACTION_SUSPEND) { + LL_COMP_Disable(cfg->comp); + } +#endif + + return 0; +} + +static void stm32_comp_irq_handler(const struct device *dev) +{ + const struct stm32_comp_config *cfg = dev->config; + struct stm32_comp_data *data = dev->data; + + exti_clear_flag(cfg); + + if (data->callback == NULL) { + return; + } + + data->callback(dev, data->user_data); +} + +static int stm32_comp_init(const struct device *dev) +{ + const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + const struct stm32_comp_config *cfg = dev->config; + int ret = 0; + + ret = device_is_ready(clk); + if (!ret) { + LOG_ERR("%s clock control device not ready (%d)", dev->name, ret); + return ret; + } + + /* Enable COMP bus clock */ + ret = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken[0]); + if (ret != 0) { + LOG_ERR("%s clock op failed (%d)", dev->name, ret); + return ret; + } + + /* Enable COMP clock source */ + clock_control_configure(clk, (clock_control_subsys_t)&cfg->pclken[1], NULL); + if (ret != 0) { + LOG_ERR("%s clock configure failed (%d)", dev->name, ret); + return ret; + } + + /* Configure COMP inputs as specified in Device Tree, if any */ + ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT); + if (ret < 0 && ret != -ENOENT) { + /* + * If the COMP is used only with internal channels, then no pinctrl is + * provided in Device Tree, and pinctrl_apply_state returns -ENOENT, + * but this should not be treated as an error. + */ + LOG_ERR("%s pinctrl setup failed (%d)", dev->name, ret); + return ret; + } + + ret = LL_COMP_Init(cfg->comp, (LL_COMP_InitTypeDef *)&cfg->comp_config); + if (ret != 0) { + LOG_ERR("COMP instance is locked (%d)", ret); + return ret; + } + + if (cfg->miller_effect_hold_enable) { +#if defined(CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING) + SET_BIT(cfg->comp->CSR, BIT(1U)); +#endif /* CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING */ + } + + cfg->irq_init(); + + return pm_device_driver_init(dev, stm32_comp_pm_callback); +} + +#define STM32_COMP_IRQ_HANDLER_SYM(inst) _CONCAT(stm32_comp_irq_init, inst) + +#define STM32_COMP_IRQ_HANDLER_DEFINE(inst) \ + static void STM32_COMP_IRQ_HANDLER_SYM(inst)(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \ + stm32_comp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \ + \ + irq_enable(DT_INST_IRQN(inst)); \ + } + +#define STM32_COMP_DEVICE(inst) \ + static const struct stm32_pclken comp_clk[] = STM32_DT_INST_CLOCKS(inst); \ + \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static struct stm32_comp_data _CONCAT(data, inst); \ + \ + STM32_COMP_IRQ_HANDLER_DEFINE(inst) \ + \ + static const struct stm32_comp_config _CONCAT(config, inst) = { \ + .comp = (COMP_TypeDef *)DT_INST_REG_ADDR(inst), \ + .pclken = comp_clk, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .irq_init = STM32_COMP_IRQ_HANDLER_SYM(inst), \ + .irq_nr = DT_INST_IRQN(inst), \ + .comp_config = STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst), \ + .exti_line = STM32_COMP_DT_EXTI_LINE(inst), \ + .exti_line_number = STM32_COMP_DT_EXTI_LINE_NUMBER(inst), \ + .lock_enable = STM32_COMP_DT_INST_LOCK(inst), \ + .miller_effect_hold_enable = STM32_COMP_DT_MILLER_EFFECT_HOLD_ENABLE(inst)}; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, stm32_comp_pm_callback); \ + \ + DEVICE_DT_INST_DEFINE(inst, stm32_comp_init, PM_DEVICE_DT_INST_GET(inst), \ + &_CONCAT(data, inst), &_CONCAT(config, inst), POST_KERNEL, \ + CONFIG_COMPARATOR_INIT_PRIORITY, &stm32_comp_comp_api); + +DT_INST_FOREACH_STATUS_OKAY(STM32_COMP_DEVICE) diff --git a/dts/arm/st/g4/stm32g4.dtsi b/dts/arm/st/g4/stm32g4.dtsi index 20d3d38df39b..9ca77da07b09 100644 --- a/dts/arm/st/g4/stm32g4.dtsi +++ b/dts/arm/st/g4/stm32g4.dtsi @@ -1,6 +1,7 @@ /* * Copyright (c) 2021 The Chromium OS Authors * Copyright (c) 2019 Richard Osterloh + * Copyright (c) 2024 Alexander Kozhinov * Copyright (c) 2024 STMicroelectronics * * SPDX-License-Identifier: Apache-2.0 @@ -662,6 +663,32 @@ interrupts = <63 0>; status = "disabled"; }; + + comp1: comparator@40010200 { + compatible = "st,stm32-comp"; + reg = <0x40010200 0x4>; + interrupts = <64 0>; + st,exti-line = <21>; + st,blank-sel = "NONE"; + positive-input = "IN0"; + negative-input = "VREFINT"; + invert-output = "NONINVERTED"; + clocks = <&rcc STM32_CLOCK(APB2, 0U)>; + status = "disabled"; + }; + + comp2: comparator@40010204 { + compatible = "st,stm32-comp"; + reg = <0x40010204 0x4>; + interrupts = <64 0>; + st,exti-line = <22>; + st,blank-sel = "NONE"; + positive-input = "IN0"; + negative-input = "VREFINT"; + invert-output = "NONINVERTED"; + clocks = <&rcc STM32_CLOCK(APB2, 0U)>; + status = "disabled"; + }; }; die_temp: dietemp { diff --git a/dts/arm/st/h7/stm32h7.dtsi b/dts/arm/st/h7/stm32h7.dtsi index d2b9bd1bcb2e..aed9049e0c5f 100644 --- a/dts/arm/st/h7/stm32h7.dtsi +++ b/dts/arm/st/h7/stm32h7.dtsi @@ -1083,6 +1083,34 @@ clocks = <&rcc STM32_CLOCK(AHB2, 0U)>; status = "disabled"; }; + + comp1_ch1: comparator@5800380c { + compatible = "st,stm32h7-comp", "st,stm32-comp"; + reg = <0x5800380c 0x200>; + interrupts = <137 0>; + st,exti-line = <20>; + st,blank-sel = "NONE"; + st,power-mode = "MEDIUMSPEED"; + positive-input = "IN0"; + negative-input = "VREFINT"; + invert-output = "NONINVERTED"; + clocks = <&rcc STM32_CLOCK(APB4, 14U)>; + status = "disabled"; + }; + + comp1_ch2: comparator@58003810 { + compatible = "st,stm32h7-comp", "st,stm32-comp"; + reg = <0x58003810 0x200>; + interrupts = <137 0>; + st,exti-line = <20>; + st,blank-sel = "NONE"; + st,power-mode = "MEDIUMSPEED"; + positive-input = "IN0"; + negative-input = "VREFINT"; + invert-output = "NONINVERTED"; + clocks = <&rcc STM32_CLOCK(APB4, 14U)>; + status = "disabled"; + }; }; die_temp: dietemp { diff --git a/dts/bindings/comparator/st,stm32-comp.yaml b/dts/bindings/comparator/st,stm32-comp.yaml new file mode 100644 index 000000000000..ddc6c3e8d770 --- /dev/null +++ b/dts/bindings/comparator/st,stm32-comp.yaml @@ -0,0 +1,185 @@ +# Copyright (c) 2025 Alexander Kozhinov +# SPDX-License-Identifier: Apache-2.0 + +description: | + STM32 Comparator + + The following example displays the minimum node layout: + + comp1: comparator@deadbeef { + compatible = "st,stm32-comp"; + reg = <0xdeadbeef 0x4>; + interrupts = <0 0>; + 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 { + comp1_default: comp1_default { + ... + }; + }; + + &comp1 { + status = "okay"; + pinctrl-0 = <&comp1_default>; + pinctrl-names = "default"; + + positive-input = "IO0"; + negative-input = "IO1"; + }; + +compatible: "st,stm32-comp" + +include: + - base.yaml + - pinctrl-device.yaml + +properties: + interrupts: + required: true + + reg: + required: true + + hysteresis: + type: string + enum: + - NONE + - LOW + - MEDIUM + - HIGH + - 10MV + - 20MV + - 30MV + - 40MV + - 50MV + - 60MV + - 70MV + default: NONE + + invert-output: + type: string + enum: + - NONINVERTED + - INVERTED + default: NONINVERTED + + st,exti-line: + required: true + type: int + description: | + Extended Interrupts and Event Controller (EXTI) interrupt + line number connected to the COMPx event. + + st,lock-enable: + type: boolean + + st,blank-sel: + type: string + description: | + The purpose of the blanking function is to prevent the current regulation + from tripping upon short current spikes at the beginning of PWM period + (typically the recovery current in power switch anti-parallel diodes). + This goes through setting a dead window defined with a timer output + compare signal. The blanking source is selected individually per + comparator channel. The inverted blanking signal is logical AND-ed with + the comparator stage output to produce the comparator channel x output. + source: STM32G4 Reference Manual (RM0440 Rev 8, page 768, chapter 24.3.6) + enum: + - NONE + - TIM1_OC5 + - TIM2_OC3 + - TIM3_OC3 + - TIM3_OC4 + - TIM4_OC3 + - TIM8_OC5 + - TIM15_OC1 + - TIM20_OC5 + - TIM1_OC5_COMP1 + - TIM1_OC5_COMP2 + - TIM1_OC5_COMP3 + - TIM1_OC5_COMP4 + - TIM1_OC5_COMP5 + - TIM1_OC5_COMP6 + - TIM1_OC5_COMP7 + - TIM2_OC3_COMP1 + - TIM2_OC3_COMP2 + - TIM2_OC3_COMP5 + - TIM2_OC4_COMP3 + - TIM2_OC4_COMP6 + - TIM3_OC3_COMP1 + - TIM3_OC3_COMP2 + - TIM3_OC3_COMP3 + - TIM3_OC3_COMP5 + - TIM3_OC3_COMP7 + - TIM3_OC4_COMP4 + - TIM8_OC5_COMP1 + - TIM8_OC5_COMP2 + - TIM8_OC5_COMP3 + - TIM8_OC5_COMP4 + - TIM8_OC5_COMP5 + - TIM8_OC5_COMP6 + - TIM8_OC5_COMP7 + - TIM15_OC1_COMP4 + - TIM15_OC2_COMP6 + - TIM15_OC2_COMP7 + default: NONE + + positive-input: + required: true + type: string + description: | + Selects positive input pin given by pinmux controller. This input can be + typically assigned to one of the predefinded package pins. + IO0 will correspond to first pinmux pin option and IO1 to the second one. + enum: + - IN0 + - IN1 + default: IN0 + + negative-input: + required: true + type: string + description: | + Selects negative input pin given by pinmux controller. + This input can be typically assigned to one of the predefinded package pins + or to one of internal signals like VREF_INT or DAC. + In case of package pin assignment IO0 will correspond to first pinmux pin + option and IO1 to the second one. + enum: + - 1_4VREFINT + - 1_2VREFINT + - 3_4VREFINT + - VREFINT + - DAC1_CH1 + - DAC1_CH2 + - DAC2_CH1 + - DAC3_CH1 + - DAC3_CH2 + - DAC4_CH1 + - DAC4_CH2 + - IN0 + - IN1 + default: VREFINT + + st,miller-effect-hold-enable: + type: boolean + description: | + Miller effect is a hidden feature of stm32, which may be enabled by setting + bit 1 of COMP_CxCSR register. + for more info see: + https://community.st.com/t5/stm32-mcus-products/stm32g431-comparator-bug/m-p/679540#M250779 + + st,power-mode: + type: string + description: | + Power mode is not supported by all stm32 series. + enum: + - HIGHSPEED + - MEDIUMSPEED + - ULTRALOWPOWER + default: MEDIUMSPEED diff --git a/dts/bindings/comparator/st,stm32h7-comp.yaml b/dts/bindings/comparator/st,stm32h7-comp.yaml new file mode 100644 index 000000000000..5a04bd5c203f --- /dev/null +++ b/dts/bindings/comparator/st,stm32h7-comp.yaml @@ -0,0 +1,38 @@ +# Copyright (c) 2025 Alexander Kozhinov +# SPDX-License-Identifier: Apache-2.0 + +description: | + STM32H7 Comparator + + The following example displays the minimum node layout: + + comp1: comparator@deadbeef { + compatible = "st,stm32h7-comp"; + reg = <0xdeadbeef 0x4>; + interrupts = <0 0>; + 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 { + comp1_default: comp1_default { + ... + }; + }; + + &comp1 { + status = "okay"; + pinctrl-0 = <&comp1_default>; + pinctrl-names = "default"; + + positive-input = "IO0"; + negative-input = "IO1"; + }; + +compatible: "st,stm32h7-comp" + +include: + - st,stm32-comp.yaml From fbe930f8ae7f7f1f3290b19f48ea08101818ecd6 Mon Sep 17 00:00:00 2001 From: Alexander Kozhinov Date: Mon, 3 Feb 2025 20:58:50 +0100 Subject: [PATCH 2/2] tests: drivers: comparator: gpio_loopback Add support for nucleo_h745zi_q board Add support for nucleo_g474re board Add stm32 snippets Signed-off-by: Alexander Kozhinov --- .../boards/nucleo_g474re.overlay | 34 +++++++++++++ .../boards/nucleo_h743zi.overlay | 51 +++++++++++++++++++ .../boards/nucleo_h745zi_q.overlay | 51 +++++++++++++++++++ .../stm32_comp/boards/nucleo_g474re.overlay | 34 +++++++++++++ .../stm32_comp/boards/nucleo_h743zi.overlay | 51 +++++++++++++++++++ .../stm32_comp/boards/nucleo_h745zi_q.overlay | 51 +++++++++++++++++++ .../snippets/stm32_comp/snippet.yml | 15 ++++++ .../comparator/gpio_loopback/testcase.yaml | 7 +++ 8 files changed, 294 insertions(+) create mode 100644 tests/drivers/comparator/gpio_loopback/boards/nucleo_g474re.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/boards/nucleo_h743zi.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/boards/nucleo_h745zi_q.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_g474re.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h743zi.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h745zi_q.overlay create mode 100644 tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/snippet.yml diff --git a/tests/drivers/comparator/gpio_loopback/boards/nucleo_g474re.overlay b/tests/drivers/comparator/gpio_loopback/boards/nucleo_g474re.overlay new file mode 100644 index 000000000000..ff521b2adc15 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nucleo_g474re.overlay @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/ { + aliases { + test-comp = &comp1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pa1: comp1_in_p_pa1 { + pinmux = ; + }; +}; + +&comp1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pa1>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN0"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/boards/nucleo_h743zi.overlay b/tests/drivers/comparator/gpio_loopback/boards/nucleo_h743zi.overlay new file mode 100644 index 000000000000..fb3880b7596d --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nucleo_h743zi.overlay @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/* + * PB0 looped back to PB2 + */ + +/ { + aliases { + test-comp = &comp1_ch1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pb0: comp1_in_p_pb0 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_p_pb2: comp1_in_p_pb2 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_m_pc4: comp1_in_m_pc4 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_out_pe12: comp1_out_pe12 { + pinmux = ; + slew-rate = "very-high-speed"; + }; +}; + +&comp1_ch1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pb2 &comp1_out_pe12>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN1"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/boards/nucleo_h745zi_q.overlay b/tests/drivers/comparator/gpio_loopback/boards/nucleo_h745zi_q.overlay new file mode 100644 index 000000000000..fb3880b7596d --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/boards/nucleo_h745zi_q.overlay @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/* + * PB0 looped back to PB2 + */ + +/ { + aliases { + test-comp = &comp1_ch1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pb0: comp1_in_p_pb0 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_p_pb2: comp1_in_p_pb2 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_m_pc4: comp1_in_m_pc4 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_out_pe12: comp1_out_pe12 { + pinmux = ; + slew-rate = "very-high-speed"; + }; +}; + +&comp1_ch1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pb2 &comp1_out_pe12>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN1"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_g474re.overlay b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_g474re.overlay new file mode 100644 index 000000000000..ff521b2adc15 --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_g474re.overlay @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/ { + aliases { + test-comp = &comp1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pa1: comp1_in_p_pa1 { + pinmux = ; + }; +}; + +&comp1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pa1>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN0"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h743zi.overlay b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h743zi.overlay new file mode 100644 index 000000000000..fb3880b7596d --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h743zi.overlay @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/* + * PB0 looped back to PB2 + */ + +/ { + aliases { + test-comp = &comp1_ch1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pb0: comp1_in_p_pb0 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_p_pb2: comp1_in_p_pb2 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_m_pc4: comp1_in_m_pc4 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_out_pe12: comp1_out_pe12 { + pinmux = ; + slew-rate = "very-high-speed"; + }; +}; + +&comp1_ch1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pb2 &comp1_out_pe12>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN1"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h745zi_q.overlay b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h745zi_q.overlay new file mode 100644 index 000000000000..fb3880b7596d --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/boards/nucleo_h745zi_q.overlay @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2025 Alexander Kozhinov + */ + + #include + +/* + * PB0 looped back to PB2 + */ + +/ { + aliases { + test-comp = &comp1_ch1; + }; + + zephyr,user { + /* connected also to green led and will toggle it */ + test-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; + }; +}; + +&pinctrl { + /omit-if-no-ref/ comp1_in_p_pb0: comp1_in_p_pb0 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_p_pb2: comp1_in_p_pb2 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_in_m_pc4: comp1_in_m_pc4 { + pinmux = ; + }; + + /omit-if-no-ref/ comp1_out_pe12: comp1_out_pe12 { + pinmux = ; + slew-rate = "very-high-speed"; + }; +}; + +&comp1_ch1 { + status = "okay"; + pinctrl-0 = <&comp1_in_p_pb2 &comp1_out_pe12>; + pinctrl-names = "default"; + hysteresis = "NONE"; + positive-input = "IN1"; + negative-input = "1_4VREFINT"; + invert-output = "NONINVERTED"; + st,blank-sel = "NONE"; +}; diff --git a/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/snippet.yml b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/snippet.yml new file mode 100644 index 000000000000..7e8e5cde394f --- /dev/null +++ b/tests/drivers/comparator/gpio_loopback/snippets/stm32_comp/snippet.yml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Alexander Kozhinov +# SPDX-License-Identifier: Apache-2.0 + +name: gpio_loopback_stm32_comp + +boards: + nucleo_g474re/stm32g474xx: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nucleo_g474re.overlay + nucleo_h743zi/stm32h743xx: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nucleo_h743zi.overlay + nucleo_h745zi_q/stm32h745xx/m7: + append: + EXTRA_DTC_OVERLAY_FILE: boards/nucleo_h745zi_q.overlay diff --git a/tests/drivers/comparator/gpio_loopback/testcase.yaml b/tests/drivers/comparator/gpio_loopback/testcase.yaml index ac3e43cb0480..60aa74b452a6 100644 --- a/tests/drivers/comparator/gpio_loopback/testcase.yaml +++ b/tests/drivers/comparator/gpio_loopback/testcase.yaml @@ -29,3 +29,10 @@ tests: - nrf54h20dk/nrf54h20/cpuapp - nrf54l15dk/nrf54l15/cpuapp - nrf5340dk/nrf5340/cpuapp + drivers.comparator.gpio_loopback.stm32_comp: + extra_args: + - SNIPPET="gpio_loopback_stm32_comp" + platform_allow: + - nucleo_h745zi_q/stm32h745xx/m7 + - nucleo_h743zi/stm32h743xx + - nucleo_g474re/stm32g474xx