Skip to content

Commit 6597539

Browse files
drivers: comparator: implement stm32 comparator driver
implement the driver for the stm32 comparator peripheral Signed-off-by: Alexander Kozhinov <[email protected]>
1 parent 2158fed commit 6597539

File tree

4 files changed

+351
-0
lines changed

4 files changed

+351
-0
lines changed

drivers/comparator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c)
1515
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c)
1616
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_SHELL comparator_shell.c)
1717
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RA comparator_renesas_ra.c)
18+
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_STM32_COMP comparator_stm32_comp.c)

drivers/comparator/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ rsource "Kconfig.nrf_comp"
2727
rsource "Kconfig.nrf_lpcomp"
2828
rsource "Kconfig.shell"
2929
rsource "Kconfig.renesas_ra"
30+
rsource "Kconfig.stm32_comp"
3031

3132
endif # COMPARATOR
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Alexander Kozhinov <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COMPARATOR_STM32_COMP
5+
bool "ST STM32 comparator driver"
6+
default y
7+
depends on DT_HAS_ST_STM32_COMP_ENABLED
8+
select PINCTRL
9+
select EXTI_STM32
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Kozhinov <[email protected]>
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <zephyr/irq.h>
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/pm/device.h>
9+
#include <zephyr/logging/log.h>
10+
#include <zephyr/sys/util_macro.h>
11+
#include <zephyr/drivers/pinctrl.h>
12+
#include <zephyr/drivers/comparator.h>
13+
#include <zephyr/drivers/clock_control.h>
14+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
15+
#include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
16+
17+
#include <stm32_ll_comp.h>
18+
#include <stm32_ll_system.h>
19+
20+
LOG_MODULE_REGISTER(stm32_comp, CONFIG_COMPARATOR_LOG_LEVEL);
21+
22+
#define DT_DRV_COMPAT st_stm32_comp
23+
24+
#define LL_COMP_INPUT_PLUS_IN0 LL_COMP_INPUT_PLUS_IO1
25+
#define LL_COMP_INPUT_PLUS_IN1 LL_COMP_INPUT_PLUS_IO2
26+
27+
#define LL_COMP_INPUT_MINUS_IN0 LL_COMP_INPUT_MINUS_IO1
28+
#define LL_COMP_INPUT_MINUS_IN1 LL_COMP_INPUT_MINUS_IO2
29+
30+
#define STM32_COMP_DT_INST_P_IN(inst) \
31+
CONCAT(LL_COMP_INPUT_PLUS_, DT_INST_STRING_TOKEN(inst, positive_input))
32+
33+
#define STM32_COMP_DT_INST_N_IN(inst) \
34+
CONCAT(LL_COMP_INPUT_MINUS_, DT_INST_STRING_TOKEN(inst, negative_input))
35+
36+
#define STM32_COMP_DT_INST_HYST_MODE(inst) \
37+
CONCAT(LL_COMP_HYSTERESIS_, DT_INST_STRING_TOKEN(inst, hysteresis))
38+
39+
#define STM32_COMP_DT_INST_INV_OUT(inst) \
40+
CONCAT(LL_COMP_OUTPUTPOL_, DT_INST_STRING_TOKEN(inst, invert_output))
41+
42+
#define STM32_COMP_DT_INST_BLANK_SEL(inst) \
43+
CONCAT(LL_COMP_BLANKINGSRC_, DT_INST_STRING_TOKEN(inst, st_blank_sel))
44+
45+
#define STM32_COMP_DT_INST_LOCK(inst) DT_INST_PROP(inst, st_lock_enable)
46+
47+
#define STM32_COMP_DT_MILLER_EFFECT_HOLD_DISABLE(inst) \
48+
DT_INST_PROP_OR(inst, st_miller_effect_hold_disable, false)
49+
50+
#define STM32_COMP_DT_EXTI_LINE_NUMBER(inst) DT_INST_PROP(inst, st_exti_line)
51+
52+
/* Value 0 always relates to the default value of COMP PWRMODE bit field */
53+
#define STM32_COMP_DT_POWER_MODE(inst) \
54+
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, st_power_mode), \
55+
(CONCAT(LL_COMP_POWERMODE_, \
56+
DT_INST_STRING_TOKEN(inst, st_power_mode))), \
57+
(0))
58+
59+
struct stm32_comp_config {
60+
COMP_TypeDef *comp;
61+
struct stm32_pclken *pclken;
62+
const struct pinctrl_dev_config *pincfg;
63+
void (*irq_init)(void);
64+
const uint32_t irq_nr;
65+
const uint32_t exti_line_number;
66+
const bool lock_enable;
67+
const bool miller_effect_hold_disable;
68+
const uint32_t power_mode;
69+
const uint32_t input_plus;
70+
const uint32_t input_minus;
71+
const uint32_t hysteresis;
72+
const uint32_t invert_output;
73+
const uint32_t blank_sel;
74+
};
75+
76+
struct stm32_comp_data {
77+
comparator_callback_t callback;
78+
void *user_data;
79+
};
80+
81+
static bool stm32_comp_is_resumed(const struct device *dev)
82+
{
83+
#ifdef CONFIG_PM_DEVICE
84+
enum pm_device_state state;
85+
86+
(void)pm_device_state_get(dev, &state);
87+
return state == PM_DEVICE_STATE_ACTIVE;
88+
#else
89+
return true;
90+
#endif /* CONFIG_PM_DEVICE */
91+
}
92+
93+
static int stm32_comp_get_output(const struct device *dev)
94+
{
95+
const struct stm32_comp_config *cfg = dev->config;
96+
COMP_TypeDef *comp = cfg->comp;
97+
98+
return LL_COMP_ReadOutputLevel(comp);
99+
}
100+
101+
static int stm32_comp_set_trigger(const struct device *dev, enum comparator_trigger trigger)
102+
{
103+
const struct stm32_comp_config *cfg = dev->config;
104+
struct stm32_comp_data *data = dev->data;
105+
stm32_exti_trigger_type exti_trigger = 0U;
106+
COMP_TypeDef *comp = cfg->comp;
107+
int ret = 0;
108+
109+
switch (trigger) {
110+
case COMPARATOR_TRIGGER_NONE:
111+
exti_trigger = STM32_EXTI_TRIG_NONE;
112+
break;
113+
case COMPARATOR_TRIGGER_RISING_EDGE:
114+
exti_trigger = STM32_EXTI_TRIG_RISING;
115+
break;
116+
case COMPARATOR_TRIGGER_FALLING_EDGE:
117+
exti_trigger = STM32_EXTI_TRIG_FALLING;
118+
break;
119+
case COMPARATOR_TRIGGER_BOTH_EDGES:
120+
exti_trigger = STM32_EXTI_TRIG_BOTH;
121+
break;
122+
default:
123+
LOG_ERR("%s: Unsupported trigger mode %d", dev->name, trigger);
124+
return -ENOTSUP;
125+
}
126+
127+
irq_disable(cfg->irq_nr);
128+
LL_COMP_Disable(comp);
129+
130+
ret = stm32_exti_enable(cfg->exti_line_number, exti_trigger,
131+
STM32_EXTI_MODE_IT);
132+
if (ret != 0) {
133+
LOG_ERR("%s: EXTI init failed (%d)", dev->name, ret);
134+
return ret;
135+
}
136+
137+
if (stm32_comp_is_resumed(dev)) {
138+
LL_COMP_Enable(comp);
139+
}
140+
141+
if (data->callback != NULL) {
142+
irq_enable(cfg->irq_nr);
143+
}
144+
145+
return ret;
146+
}
147+
148+
static int stm32_comp_trigger_is_pending(const struct device *dev)
149+
{
150+
const struct stm32_comp_config *cfg = dev->config;
151+
152+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
153+
stm32_exti_clear_pending(cfg->exti_line_number);
154+
return 1;
155+
}
156+
return 0;
157+
}
158+
159+
static int stm32_comp_set_trigger_callback(const struct device *dev, comparator_callback_t callback,
160+
void *user_data)
161+
{
162+
const struct stm32_comp_config *cfg = dev->config;
163+
struct stm32_comp_data *data = dev->data;
164+
165+
irq_disable(cfg->irq_nr);
166+
167+
data->callback = callback;
168+
data->user_data = user_data;
169+
170+
irq_enable(cfg->irq_nr);
171+
172+
if (data->callback != NULL && stm32_comp_trigger_is_pending(dev)) {
173+
callback(dev, user_data);
174+
}
175+
176+
return 0;
177+
}
178+
179+
static int stm32_comp_pm_callback(const struct device *dev, enum pm_device_action action)
180+
{
181+
const struct stm32_comp_config *cfg = dev->config;
182+
COMP_TypeDef *comp = cfg->comp;
183+
184+
if (LL_COMP_IsLocked(comp)) {
185+
LOG_ERR("%s is locked", dev->name);
186+
return -EACCES;
187+
188+
}
189+
190+
if (action == PM_DEVICE_ACTION_RESUME) {
191+
LL_COMP_Enable(comp);
192+
if (cfg->lock_enable) {
193+
LL_COMP_Lock(comp);
194+
}
195+
}
196+
197+
if (IS_ENABLED(CONFIG_PM_DEVICE) && action == PM_DEVICE_ACTION_SUSPEND) {
198+
LL_COMP_Disable(comp);
199+
}
200+
201+
return 0;
202+
}
203+
204+
static void stm32_comp_irq_handler(const struct device *dev)
205+
{
206+
const struct stm32_comp_config *cfg = dev->config;
207+
struct stm32_comp_data *data = dev->data;
208+
209+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
210+
stm32_exti_clear_pending(cfg->exti_line_number);
211+
}
212+
213+
if (data->callback == NULL) {
214+
return;
215+
}
216+
217+
data->callback(dev, data->user_data);
218+
}
219+
220+
static int stm32_comp_init(const struct device *dev)
221+
{
222+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
223+
const struct stm32_comp_config *cfg = dev->config;
224+
COMP_TypeDef *comp = cfg->comp;
225+
int ret = 0;
226+
227+
if (!device_is_ready(clk)) {
228+
LOG_ERR("%s clock control device not ready", dev->name);
229+
return -ENODEV;
230+
}
231+
232+
/* Enable COMP bus clock */
233+
ret = clock_control_on(clk, &cfg->pclken[0]);
234+
if (ret != 0) {
235+
LOG_ERR("%s clock op failed (%d)", dev->name, ret);
236+
return ret;
237+
}
238+
239+
/* Enable COMP clock source */
240+
clock_control_configure(clk, &cfg->pclken[1], NULL);
241+
if (ret != 0) {
242+
LOG_ERR("%s clock configure failed (%d)", dev->name, ret);
243+
return ret;
244+
}
245+
246+
/* Configure COMP inputs as specified in Device Tree, if any */
247+
ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
248+
if (ret < 0 && ret != -ENOENT) {
249+
/*
250+
* If the COMP is used only with internal channels, then no pinctrl is
251+
* provided in Device Tree, and pinctrl_apply_state returns -ENOENT,
252+
* but this should not be treated as an error.
253+
*/
254+
LOG_ERR("%s pinctrl setup failed (%d)", dev->name, ret);
255+
return ret;
256+
}
257+
258+
if (LL_COMP_IsLocked(comp)) {
259+
/* COMP instance shall not be locked */
260+
LOG_ERR("%s COMP instance is locked", dev->name);
261+
return -EACCES;
262+
}
263+
264+
#ifndef CONFIG_SOC_SERIES_STM32G4X
265+
LL_COMP_SetPowerMode(comp, cfg->power_mode);
266+
#endif /* !CONFIG_SOC_SERIES_STM32G4X */
267+
268+
LL_COMP_SetInputMinus(comp, cfg->input_minus);
269+
LL_COMP_SetInputPlus(comp, cfg->input_plus);
270+
LL_COMP_SetInputHysteresis(comp, cfg->hysteresis);
271+
LL_COMP_SetOutputPolarity(comp, cfg->invert_output);
272+
LL_COMP_SetOutputBlankingSource(comp, cfg->blank_sel);
273+
274+
#if defined(CONFIG_SOC_SERIES_STM32G4X)
275+
/* Undocumented bit, refer to st,miller-effect-hold-disable DT binding */
276+
WRITE_BIT(comp->CSR, 1, cfg->miller_effect_hold_disable);
277+
#endif /* CONFIG_SOC_SERIES_STM32G4X */
278+
279+
cfg->irq_init();
280+
281+
return pm_device_driver_init(dev, stm32_comp_pm_callback);
282+
}
283+
284+
static DEVICE_API(comparator, stm32_comp_comp_api) = {
285+
.get_output = stm32_comp_get_output,
286+
.set_trigger = stm32_comp_set_trigger,
287+
.set_trigger_callback = stm32_comp_set_trigger_callback,
288+
.trigger_is_pending = stm32_comp_trigger_is_pending,
289+
};
290+
291+
#define STM32_COMP_IRQ_HANDLER_SYM(inst) stm32_comp_irq_init##inst
292+
293+
#define STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
294+
static void STM32_COMP_IRQ_HANDLER_SYM(inst)(void) \
295+
{ \
296+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
297+
stm32_comp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
298+
irq_enable(DT_INST_IRQN(inst)); \
299+
}
300+
301+
#define STM32_COMP_DEVICE(inst) \
302+
\
303+
static struct stm32_pclken comp_##inst##_clk[] = STM32_DT_INST_CLOCKS(inst); \
304+
\
305+
PINCTRL_DT_INST_DEFINE(inst); \
306+
\
307+
static struct stm32_comp_data _CONCAT(data, inst); \
308+
\
309+
STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
310+
static const struct stm32_comp_config _CONCAT(config, inst) = { \
311+
.comp = (COMP_TypeDef *)DT_INST_REG_ADDR(inst), \
312+
.pclken = comp_##inst##_clk, \
313+
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
314+
.irq_init = STM32_COMP_IRQ_HANDLER_SYM(inst), \
315+
.irq_nr = DT_INST_IRQN(inst), \
316+
.exti_line_number = STM32_COMP_DT_EXTI_LINE_NUMBER(inst), \
317+
.lock_enable = STM32_COMP_DT_INST_LOCK(inst), \
318+
.miller_effect_hold_disable = \
319+
STM32_COMP_DT_MILLER_EFFECT_HOLD_DISABLE(inst), \
320+
.power_mode = STM32_COMP_DT_POWER_MODE(inst), \
321+
.input_plus = STM32_COMP_DT_INST_P_IN(inst), \
322+
.input_minus = STM32_COMP_DT_INST_N_IN(inst), \
323+
.hysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \
324+
.invert_output = STM32_COMP_DT_INST_INV_OUT(inst), \
325+
.blank_sel = STM32_COMP_DT_INST_BLANK_SEL(inst) \
326+
}; \
327+
\
328+
PM_DEVICE_DT_INST_DEFINE(inst, stm32_comp_pm_callback); \
329+
\
330+
DEVICE_DT_INST_DEFINE(inst, \
331+
stm32_comp_init, \
332+
PM_DEVICE_DT_INST_GET(inst), \
333+
&_CONCAT(data, inst), \
334+
&_CONCAT(config, inst), \
335+
POST_KERNEL, \
336+
CONFIG_COMPARATOR_INIT_PRIORITY, \
337+
&stm32_comp_comp_api \
338+
);
339+
340+
DT_INST_FOREACH_STATUS_OKAY(STM32_COMP_DEVICE)

0 commit comments

Comments
 (0)