Skip to content

Commit a62bf4b

Browse files
ZhaoxiangJinnashif
authored andcommitted
drivers: comparator: Add high-speed comparator support
Add high-speed comparator support Signed-off-by: Zhaoxiang Jin <Zhaoxiang.Jin_1@nxp.com>
1 parent bc93570 commit a62bf4b

File tree

5 files changed

+504
-0
lines changed

5 files changed

+504
-0
lines changed

drivers/comparator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ zephyr_library_sources_ifdef(CONFIG_COMPARATOR_MCUX_ACMP comparator_mcux_acmp.c)
1515
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c)
1616
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c)
1717
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NXP_CMP comparator_nxp_cmp.c)
18+
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NXP_HSCMP comparator_nxp_hscmp.c)
1819
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RA comparator_renesas_ra.c)
1920
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RA_LVD comparator_renesas_ra_lvd.c)
2021
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RX_LVD comparator_renesas_rx_lvd.c)

drivers/comparator/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ rsource "Kconfig.mcux_acmp"
2626
rsource "Kconfig.nrf_comp"
2727
rsource "Kconfig.nrf_lpcomp"
2828
rsource "Kconfig.nxp_cmp"
29+
rsource "Kconfig.nxp_hscmp"
2930
rsource "Kconfig.renesas_ra"
3031
rsource "Kconfig.renesas_rx"
3132
rsource "Kconfig.shell"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright 2025 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COMPARATOR_NXP_HSCMP
5+
bool "NXP HSCMP comparator driver"
6+
default y
7+
depends on DT_HAS_NXP_HSCMP_ENABLED
8+
select PINCTRL
9+
select CLOCK_CONTROL
10+
select RESET
11+
select REGULATOR
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
/*
2+
* Copyright 2025 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/irq.h>
8+
#include <zephyr/pm/device.h>
9+
#include <zephyr/devicetree.h>
10+
#include <zephyr/logging/log.h>
11+
#include <zephyr/drivers/reset.h>
12+
#include <zephyr/drivers/pinctrl.h>
13+
#include <zephyr/drivers/regulator.h>
14+
#include <zephyr/drivers/comparator.h>
15+
#include <zephyr/drivers/clock_control.h>
16+
17+
LOG_MODULE_REGISTER(nxp_hscmp, CONFIG_COMPARATOR_LOG_LEVEL);
18+
19+
#define DT_DRV_COMPAT nxp_hscmp
20+
21+
struct nxp_hscmp_config {
22+
HSCMP_Type *base;
23+
bool enable_stop_mode;
24+
bool invert_output;
25+
bool enable_pin_out;
26+
bool use_unfiltered_output;
27+
bool positive_mux_is_dac;
28+
bool negative_mux_is_dac;
29+
uint8_t filter_count;
30+
uint8_t filter_period;
31+
uint8_t positive_mux_input;
32+
uint8_t negative_mux_input;
33+
uint8_t dac_value;
34+
uint8_t dac_vref_source;
35+
uint8_t hysteresis_mode;
36+
uint8_t power_mode;
37+
const struct device *clock_dev;
38+
clock_control_subsys_t clock_subsys;
39+
struct reset_dt_spec reset;
40+
void (*irq_config_func)(const struct device *dev);
41+
const struct pinctrl_dev_config *pcfg;
42+
const struct device *ref_supplies;
43+
int32_t ref_supply_val;
44+
};
45+
46+
struct nxp_hscmp_data {
47+
uint8_t interrupt_mask;
48+
comparator_callback_t callback;
49+
void *user_data;
50+
};
51+
52+
static int nxp_hscmp_get_output(const struct device *dev)
53+
{
54+
const struct nxp_hscmp_config *config = dev->config;
55+
56+
return (config->base->CSR & HSCMP_CSR_COUT_MASK) ? 1 : 0;
57+
}
58+
59+
static int nxp_hscmp_set_trigger(const struct device *dev,
60+
enum comparator_trigger trigger)
61+
{
62+
const struct nxp_hscmp_config *config = dev->config;
63+
struct nxp_hscmp_data *data = dev->data;
64+
65+
config->base->IER &= ~(HSCMP_IER_CFR_IE_MASK | HSCMP_IER_CFF_IE_MASK);
66+
data->interrupt_mask = 0U;
67+
68+
switch (trigger) {
69+
case COMPARATOR_TRIGGER_NONE:
70+
break;
71+
case COMPARATOR_TRIGGER_RISING_EDGE:
72+
data->interrupt_mask = HSCMP_IER_CFR_IE_MASK;
73+
break;
74+
case COMPARATOR_TRIGGER_FALLING_EDGE:
75+
data->interrupt_mask = HSCMP_IER_CFF_IE_MASK;
76+
break;
77+
case COMPARATOR_TRIGGER_BOTH_EDGES:
78+
data->interrupt_mask = HSCMP_IER_CFR_IE_MASK | HSCMP_IER_CFF_IE_MASK;
79+
break;
80+
default:
81+
LOG_ERR("Invalid trigger type.");
82+
return -EINVAL;
83+
}
84+
85+
/* Clear latched status flags before enabling interrupts. */
86+
config->base->CSR |= (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
87+
88+
if ((data->interrupt_mask != 0U) && (data->callback != NULL)) {
89+
config->base->IER |= data->interrupt_mask;
90+
}
91+
92+
return 0;
93+
}
94+
95+
static int nxp_hscmp_trigger_is_pending(const struct device *dev)
96+
{
97+
const struct nxp_hscmp_config *config = dev->config;
98+
struct nxp_hscmp_data *data = dev->data;
99+
uint32_t status_flags;
100+
101+
status_flags = config->base->CSR & (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
102+
config->base->CSR |= (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
103+
104+
if (((data->interrupt_mask & HSCMP_IER_CFF_IE_MASK) != 0U) &&
105+
((status_flags & HSCMP_CSR_CFF_MASK) != 0U)) {
106+
return 1;
107+
}
108+
109+
if (((data->interrupt_mask & HSCMP_IER_CFR_IE_MASK) != 0U) &&
110+
((status_flags & HSCMP_CSR_CFR_MASK) != 0U)) {
111+
return 1;
112+
}
113+
114+
return 0;
115+
}
116+
117+
static int nxp_hscmp_set_trigger_callback(const struct device *dev,
118+
comparator_callback_t callback,
119+
void *user_data)
120+
{
121+
const struct nxp_hscmp_config *config = dev->config;
122+
struct nxp_hscmp_data *data = dev->data;
123+
124+
config->base->CCR0 &= ~HSCMP_CCR0_CMP_EN_MASK;
125+
126+
data->callback = callback;
127+
data->user_data = user_data;
128+
129+
/* Clear any pending flags when (re)arming the callback. */
130+
config->base->CSR |= (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
131+
132+
if ((data->callback != NULL) && (data->interrupt_mask != 0U)) {
133+
config->base->IER |= data->interrupt_mask;
134+
} else {
135+
config->base->IER &= ~(HSCMP_IER_CFR_IE_MASK | HSCMP_IER_CFF_IE_MASK);
136+
}
137+
138+
config->base->CCR0 |= HSCMP_CCR0_CMP_EN_MASK;
139+
140+
return 0;
141+
}
142+
143+
static void nxp_hscmp_irq_handler(const struct device *dev)
144+
{
145+
const struct nxp_hscmp_config *config = dev->config;
146+
struct nxp_hscmp_data *data = dev->data;
147+
148+
/* Clear interrupt status flags */
149+
config->base->CSR |= (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
150+
151+
if (data->callback == NULL) {
152+
LOG_WRN("No callback can be executed.");
153+
return;
154+
}
155+
156+
data->callback(dev, data->user_data);
157+
}
158+
159+
#if CONFIG_PM_DEVICE
160+
static int nxp_hscmp_pm_callback(const struct device *dev,
161+
enum pm_device_action action)
162+
{
163+
const struct nxp_hscmp_config *config = dev->config;
164+
165+
if (action == PM_DEVICE_ACTION_RESUME) {
166+
config->base->CCR0 |= HSCMP_CCR0_CMP_EN_MASK;
167+
return 0;
168+
}
169+
170+
if (action == PM_DEVICE_ACTION_SUSPEND) {
171+
config->base->CCR0 &= ~HSCMP_CCR0_CMP_EN_MASK;
172+
return 0;
173+
}
174+
175+
return -ENOTSUP;
176+
}
177+
#endif
178+
179+
static int nxp_hscmp_init(const struct device *dev)
180+
{
181+
const struct nxp_hscmp_config *config = dev->config;
182+
const struct device *regulator = config->ref_supplies;
183+
int32_t vref_uv = config->ref_supply_val * 1000;
184+
HSCMP_Type *base = config->base;
185+
int ret;
186+
187+
if (!device_is_ready(config->clock_dev)) {
188+
LOG_ERR("Clock device is not ready");
189+
return -ENODEV;
190+
}
191+
192+
ret = clock_control_on(config->clock_dev, config->clock_subsys);
193+
if (ret != 0) {
194+
LOG_ERR("Device clock turn on failed (%d)", ret);
195+
return ret;
196+
}
197+
198+
if (!device_is_ready(config->reset.dev)) {
199+
LOG_ERR("Reset device is not ready");
200+
return -ENODEV;
201+
}
202+
203+
ret = reset_line_assert(config->reset.dev, config->reset.id);
204+
if (ret != 0) {
205+
LOG_ERR("Failed to assert reset line (%d)", ret);
206+
return ret;
207+
}
208+
209+
ret = reset_line_deassert(config->reset.dev, config->reset.id);
210+
if (ret != 0) {
211+
LOG_ERR("Failed to deassert reset line (%d)", ret);
212+
return ret;
213+
}
214+
215+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
216+
if (ret < 0) {
217+
LOG_ERR("Failed to configure pins (%d)", ret);
218+
return ret;
219+
}
220+
221+
if (regulator != NULL) {
222+
ret = regulator_enable(regulator);
223+
if (ret) {
224+
LOG_ERR("Failed to enable regulator (%d)", ret);
225+
return ret;
226+
}
227+
ret = regulator_set_voltage(regulator, vref_uv, vref_uv);
228+
if (ret < 0) {
229+
LOG_ERR("Failed to set regulator voltage (%d)", ret);
230+
return ret;
231+
}
232+
}
233+
234+
/* Disable comparator before configuring. */
235+
base->CCR0 &= ~HSCMP_CCR0_CMP_EN_MASK;
236+
237+
base->CCR0 = ((base->CCR0 & ~(HSCMP_CCR0_CMP_STOP_EN_MASK)) |
238+
HSCMP_CCR0_CMP_STOP_EN(config->enable_stop_mode ? 1U : 0U));
239+
240+
base->CCR1 = ((base->CCR1 & ~(HSCMP_CCR1_COUT_INV_MASK | HSCMP_CCR1_COUT_PEN_MASK |
241+
HSCMP_CCR1_COUT_SEL_MASK | HSCMP_CCR1_FILT_CNT_MASK |
242+
HSCMP_CCR1_FILT_PER_MASK | HSCMP_CCR1_SAMPLE_EN_MASK |
243+
HSCMP_CCR1_WINDOW_EN_MASK)) |
244+
HSCMP_CCR1_COUT_INV(config->invert_output ? 1U : 0U) |
245+
HSCMP_CCR1_COUT_PEN(config->enable_pin_out ? 1U : 0U));
246+
247+
if (config->use_unfiltered_output) {
248+
base->CCR1 |= HSCMP_CCR1_COUT_SEL_MASK;
249+
} else {
250+
base->CCR1 &= ~HSCMP_CCR1_COUT_SEL_MASK;
251+
if (config->filter_count != 0U) {
252+
base->CCR1 |= HSCMP_CCR1_FILT_CNT(config->filter_count);
253+
base->CCR1 |= HSCMP_CCR1_FILT_PER(config->filter_period);
254+
}
255+
}
256+
257+
/* Configure inputmux, input source, hysteresis, and power mode. */
258+
base->CCR2 = ((base->CCR2 & ~(HSCMP_CCR2_CMP_HPMD_MASK | HSCMP_CCR2_CMP_NPMD_MASK |
259+
HSCMP_CCR2_HYSTCTR_MASK | HSCMP_CCR2_PSEL_MASK | HSCMP_CCR2_MSEL_MASK)) |
260+
HSCMP_CCR2_PSEL(config->positive_mux_is_dac ? 5U : config->positive_mux_input) |
261+
HSCMP_CCR2_MSEL(config->negative_mux_is_dac ? 5U : config->negative_mux_input) |
262+
HSCMP_CCR2_HYSTCTR(config->hysteresis_mode));
263+
264+
switch (config->power_mode) {
265+
case 1U: /* high speed */
266+
base->CCR2 |= HSCMP_CCR2_CMP_HPMD_MASK;
267+
break;
268+
case 2U: /* nano power */
269+
base->CCR2 |= HSCMP_CCR2_CMP_NPMD_MASK;
270+
break;
271+
default: /* low power */
272+
break;
273+
}
274+
275+
/* Configure DAC if needed. */
276+
base->DCR &= ~(HSCMP_DCR_DAC_EN_MASK | HSCMP_DCR_DAC_HPMD_MASK | HSCMP_DCR_VRSEL_MASK |
277+
HSCMP_DCR_DAC_DATA_MASK);
278+
279+
if (config->positive_mux_is_dac || config->negative_mux_is_dac) {
280+
base->DCR |= (HSCMP_DCR_VRSEL(config->dac_vref_source) |
281+
HSCMP_DCR_DAC_DATA(config->dac_value) | HSCMP_DCR_DAC_EN_MASK);
282+
}
283+
284+
/* Clear status flags before enabling interrupts or the comparator. */
285+
base->CSR = (HSCMP_CSR_CFF_MASK | HSCMP_CSR_CFR_MASK);
286+
base->IER &= ~(HSCMP_IER_CFR_IE_MASK | HSCMP_IER_CFF_IE_MASK);
287+
288+
config->irq_config_func(dev);
289+
290+
base->CCR0 |= HSCMP_CCR0_CMP_EN_MASK;
291+
292+
#if CONFIG_PM_DEVICE
293+
return pm_device_driver_init(dev, nxp_hscmp_pm_callback);
294+
#else
295+
return 0;
296+
#endif
297+
}
298+
299+
static DEVICE_API(comparator, nxp_hscmp_api) = {
300+
.get_output = nxp_hscmp_get_output,
301+
.set_trigger = nxp_hscmp_set_trigger,
302+
.set_trigger_callback = nxp_hscmp_set_trigger_callback,
303+
.trigger_is_pending = nxp_hscmp_trigger_is_pending,
304+
};
305+
306+
#define HSCMP_DT_ENUM_IDX(inst, prop, default_val) \
307+
DT_ENUM_IDX_OR(DT_DRV_INST(inst), prop, default_val)
308+
309+
#define HSCMP_DT_MUX_IS_DAC(inst, prop) DT_ENUM_HAS_VALUE(DT_DRV_INST(inst), prop, dac)
310+
#define HSCMP_DT_MUX_IDX(inst, prop) HSCMP_DT_ENUM_IDX(inst, prop, 0)
311+
312+
#if CONFIG_PM_DEVICE
313+
#define HSCMP_PM_DEVICE_DEFINE PM_DEVICE_DT_INST_DEFINE(inst, nxp_hscmp_pm_callback);
314+
#define HSCMP_PM_DEVICE_GET PM_DEVICE_DT_INST_GET(inst)
315+
#else
316+
#define HSCMP_PM_DEVICE_DEFINE
317+
#define HSCMP_PM_DEVICE_GET NULL
318+
#endif
319+
320+
#define NXP_HSCMP_DEVICE_INIT(inst) \
321+
PINCTRL_DT_INST_DEFINE(inst); \
322+
\
323+
static struct nxp_hscmp_data _CONCAT(data, inst) = { \
324+
.interrupt_mask = 0U, \
325+
}; \
326+
\
327+
HSCMP_PM_DEVICE_DEFINE \
328+
\
329+
static void _CONCAT(nxp_hscmp_irq_config, inst)(const struct device *dev) \
330+
{ \
331+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
332+
nxp_hscmp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
333+
irq_enable(DT_INST_IRQN(inst)); \
334+
} \
335+
\
336+
static const struct nxp_hscmp_config _CONCAT(config, inst) = { \
337+
.base = (HSCMP_Type *)DT_INST_REG_ADDR(inst), \
338+
.enable_stop_mode = DT_INST_PROP(inst, enable_stop_mode), \
339+
.invert_output = DT_INST_PROP(inst, invert_output), \
340+
.enable_pin_out = DT_INST_PROP(inst, enable_pin_out), \
341+
.use_unfiltered_output = DT_INST_PROP(inst, use_unfiltered_output), \
342+
.filter_count = DT_INST_PROP_OR(inst, filter_count, 0), \
343+
.filter_period = DT_INST_PROP_OR(inst, filter_period, 0), \
344+
.positive_mux_is_dac = HSCMP_DT_MUX_IS_DAC(inst, positive_mux_input), \
345+
.negative_mux_is_dac = HSCMP_DT_MUX_IS_DAC(inst, negative_mux_input), \
346+
.positive_mux_input = HSCMP_DT_MUX_IDX(inst, positive_mux_input), \
347+
.negative_mux_input = HSCMP_DT_MUX_IDX(inst, negative_mux_input), \
348+
.dac_value = DT_INST_PROP_OR(inst, dac_value, 0), \
349+
.dac_vref_source = HSCMP_DT_ENUM_IDX(inst, dac_vref_source, 0), \
350+
.hysteresis_mode = DT_INST_ENUM_IDX_OR(inst, hysteresis_mode, 0), \
351+
.power_mode = HSCMP_DT_ENUM_IDX(inst, power_mode, 0), \
352+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
353+
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(inst, name), \
354+
.reset = RESET_DT_SPEC_INST_GET(inst), \
355+
.irq_config_func = _CONCAT(nxp_hscmp_irq_config, inst), \
356+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
357+
.ref_supplies = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, nxp_references), \
358+
(DEVICE_DT_GET(DT_INST_PHANDLE(inst, nxp_references))), (NULL)), \
359+
.ref_supply_val = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, nxp_references), \
360+
(DT_INST_PHA(inst, nxp_references, vref_mv)), (0)), \
361+
}; \
362+
\
363+
DEVICE_DT_INST_DEFINE(inst, nxp_hscmp_init, HSCMP_PM_DEVICE_GET, \
364+
&_CONCAT(data, inst), &_CONCAT(config, inst), POST_KERNEL, \
365+
CONFIG_COMPARATOR_INIT_PRIORITY, &nxp_hscmp_api);
366+
367+
DT_INST_FOREACH_STATUS_OKAY(NXP_HSCMP_DEVICE_INIT)

0 commit comments

Comments
 (0)