Skip to content

Commit 0d01c39

Browse files
committed
drivers: comparator: Add comparator driver support for RX
Add comparator support for Renesas RX with LVD Signed-off-by: Quy Tran <[email protected]>
1 parent 33248c0 commit 0d01c39

File tree

7 files changed

+407
-0
lines changed

7 files changed

+407
-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_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
@@ -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.renesas_rx"
3031

3132
endif # COMPARATOR
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2025 Renesas Electronics Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COMPARATOR_RENESAS_RX_LVD
5+
bool "Renesas RX LVD"
6+
default y
7+
depends on DT_HAS_RENESAS_RX_LVD_ENABLED
8+
select USE_RX_RDP_LVD
9+
select PINCTRL
10+
help
11+
Enable Comparator driver with Low Voltage Detection (LVD) feature for Renesas RX MCUs.
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
* Copyright (c) 2025 Renesas Electronics Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT renesas_rx_lvd
8+
9+
#include <zephyr/drivers/comparator.h>
10+
#include <zephyr/drivers/pinctrl.h>
11+
#include <zephyr/logging/log.h>
12+
#include <zephyr/sys/atomic.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/irq.h>
15+
#include <soc.h>
16+
#include "r_lvd_rx_if.h"
17+
18+
LOG_MODULE_REGISTER(renesas_rx_lvd, CONFIG_COMPARATOR_LOG_LEVEL);
19+
20+
#define LVD0_NODE DT_NODELABEL(lvd0)
21+
#define LVD1_NODE DT_NODELABEL(lvd1)
22+
#define LVD_RENESAS_RX_FLAG BIT(0)
23+
/*
24+
* The extern functions below are implemented in the r_lvd_rx_hw.c source file.
25+
* For more information, please refer to r_lvd_rx_hw.c in HAL Renesas
26+
*/
27+
extern void lvd_ch1_isr(void);
28+
extern void lvd_ch2_isr(void);
29+
extern void lvd_start_lvd(lvd_channel_t ch, lvd_trigger_t trigger);
30+
extern void lvd_stop_lvd(lvd_channel_t ch);
31+
extern void lvd_start_int(lvd_channel_t ch, void (*p_callback)(void *));
32+
extern void lvd_stop_int(lvd_channel_t ch);
33+
extern void lvd_hw_enable_reset_int(lvd_channel_t ch, bool enable);
34+
extern void lvd_hw_enable_reg_protect(bool enable);
35+
36+
struct lvd_renesas_rx_data {
37+
lvd_config_t lvd_config;
38+
void (*callback)(void *args);
39+
comparator_callback_t user_cb;
40+
void *user_cb_data;
41+
atomic_t flags;
42+
};
43+
44+
struct lvd_renesas_rx_config {
45+
lvd_channel_t channel;
46+
uint8_t vdet_target;
47+
uint8_t lvd_action;
48+
bool lvd_support_cmpa;
49+
};
50+
51+
static int lvd_renesas_rx_get_output(const struct device *dev)
52+
{
53+
const struct lvd_renesas_rx_config *config = dev->config;
54+
lvd_status_position_t status_position;
55+
/* unused variable, just for API compatibility */
56+
lvd_status_cross_t unused_status_cross;
57+
lvd_err_t err;
58+
59+
err = R_LVD_GetStatus(config->channel, &status_position, &unused_status_cross);
60+
if (err != 0) {
61+
LOG_ERR("Failed to get status");
62+
return -EIO;
63+
}
64+
65+
switch (status_position) {
66+
case LVD_STATUS_POSITION_ABOVE:
67+
return 1;
68+
69+
case LVD_STATUS_POSITION_BELOW:
70+
return 0;
71+
72+
default:
73+
LOG_ERR("Invalid status, please check the configuration");
74+
return -EIO;
75+
}
76+
}
77+
78+
static int lvd_renesas_rx_set_trigger(const struct device *dev, enum comparator_trigger trigger)
79+
{
80+
struct lvd_renesas_rx_data *data = dev->data;
81+
const struct lvd_renesas_rx_config *config = dev->config;
82+
83+
lvd_hw_enable_reg_protect(false);
84+
lvd_stop_lvd(config->channel);
85+
lvd_stop_int(config->channel);
86+
87+
switch (trigger) {
88+
case COMPARATOR_TRIGGER_RISING_EDGE:
89+
data->lvd_config.trigger = LVD_TRIGGER_RISE;
90+
break;
91+
92+
case COMPARATOR_TRIGGER_FALLING_EDGE:
93+
data->lvd_config.trigger = LVD_TRIGGER_FALL;
94+
break;
95+
96+
case COMPARATOR_TRIGGER_BOTH_EDGES:
97+
data->lvd_config.trigger = LVD_TRIGGER_BOTH;
98+
break;
99+
100+
case COMPARATOR_TRIGGER_NONE:
101+
LOG_ERR("Trigger NONE is not supported");
102+
return -ENOTSUP;
103+
}
104+
105+
lvd_start_int(config->channel, data->callback);
106+
lvd_start_lvd(config->channel, data->lvd_config.trigger);
107+
lvd_hw_enable_reg_protect(true);
108+
109+
return 0;
110+
}
111+
112+
static int lvd_renesas_rx_set_trigger_callback(const struct device *dev,
113+
comparator_callback_t callback, void *user_data)
114+
{
115+
struct lvd_renesas_rx_data *data = dev->data;
116+
const struct lvd_renesas_rx_config *config = dev->config;
117+
118+
if ((config->lvd_action == 0) || (config->lvd_action == 3)) {
119+
LOG_ERR("Callback function is not supported with the current action");
120+
return -ENOTSUP;
121+
}
122+
123+
/* Disable interrupt */
124+
lvd_hw_enable_reset_int(config->channel, false);
125+
126+
data->user_cb = callback;
127+
data->user_cb_data = user_data;
128+
129+
/* Enable interrupt */
130+
lvd_hw_enable_reset_int(config->channel, true);
131+
return 0;
132+
}
133+
134+
static int lvd_renesas_rx_trigger_is_pending(const struct device *dev)
135+
{
136+
struct lvd_renesas_rx_data *data = dev->data;
137+
const struct lvd_renesas_rx_config *config = dev->config;
138+
139+
if (data->flags & LVD_RENESAS_RX_FLAG) {
140+
atomic_and(&data->flags, ~LVD_RENESAS_RX_FLAG);
141+
R_LVD_ClearStatus(config->channel);
142+
return 1;
143+
}
144+
145+
return 0;
146+
}
147+
148+
static int renesas_rx_pin_set_cmpa(const struct device *dev)
149+
{
150+
const struct lvd_renesas_rx_config *config = dev->config;
151+
const struct pinctrl_dev_config *pcfg;
152+
int ret;
153+
154+
if (config->channel == 0) {
155+
if (DT_NODE_HAS_PROP(LVD0_NODE, pinctrl_0)) {
156+
PINCTRL_DT_DEFINE(LVD0_NODE);
157+
pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD0_NODE);
158+
} else {
159+
LOG_ERR("No pinctrl-0 property found in the device tree");
160+
return -EINVAL;
161+
}
162+
} else {
163+
if (DT_NODE_HAS_PROP(LVD1_NODE, pinctrl_0)) {
164+
PINCTRL_DT_DEFINE(LVD1_NODE);
165+
pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD1_NODE);
166+
} else {
167+
LOG_ERR("No pinctrl_0 property found in the device tree");
168+
return -EINVAL;
169+
}
170+
}
171+
172+
/* In the case of monitoring the CMPA pin, set the CMPA pin. */
173+
ret = pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
174+
if (ret < 0) {
175+
LOG_ERR("Failed to apply pinctrl state: %d\n", ret);
176+
return -EINVAL;
177+
}
178+
179+
return 0;
180+
}
181+
182+
static inline void lvd_irq_connect(void)
183+
{
184+
#if DT_NODE_HAS_STATUS_OKAY(LVD0_NODE)
185+
IRQ_CONNECT(DT_IRQN(LVD0_NODE), DT_IRQ(LVD0_NODE, priority), lvd_ch1_isr,
186+
DEVICE_DT_GET(LVD0_NODE), 0);
187+
irq_enable(DT_IRQN(LVD0_NODE));
188+
#endif
189+
#if DT_NODE_HAS_STATUS_OKAY(LVD1_NODE)
190+
IRQ_CONNECT(DT_IRQN(LVD1_NODE), DT_IRQ(LVD1_NODE, priority), lvd_ch2_isr,
191+
DEVICE_DT_GET(LVD1_NODE), 0);
192+
irq_enable(DT_IRQN(LVD1_NODE));
193+
#endif
194+
}
195+
196+
static int lvd_renesas_rx_init(const struct device *dev)
197+
{
198+
lvd_err_t err;
199+
200+
lvd_irq_connect();
201+
202+
const struct lvd_renesas_rx_config *config = dev->config;
203+
const struct lvd_renesas_rx_data *data = dev->data;
204+
205+
/* In reset or no-action when LVD is detected, callback will not be triggered. */
206+
err = R_LVD_Open(config->channel, &data->lvd_config, data->callback);
207+
if (err != 0) {
208+
LOG_ERR("Failed to initialize LVD channel %d", config->channel);
209+
return -EIO;
210+
}
211+
212+
/* Set the CMPA pin if the target is CMPA */
213+
/* NOTE: For the RX130 series, CMPA is only used on channel 2. */
214+
if ((config->lvd_support_cmpa) && (config->vdet_target == 1)) {
215+
return renesas_rx_pin_set_cmpa(dev);
216+
}
217+
218+
return 0;
219+
}
220+
221+
static DEVICE_API(comparator, lvd_renesas_rx_api) = {
222+
.get_output = lvd_renesas_rx_get_output,
223+
.set_trigger = lvd_renesas_rx_set_trigger,
224+
.set_trigger_callback = lvd_renesas_rx_set_trigger_callback,
225+
.trigger_is_pending = lvd_renesas_rx_trigger_is_pending,
226+
};
227+
228+
#define LVD_RENESAS_RX_INIT(index) \
229+
\
230+
static const struct lvd_renesas_rx_config lvd_renesas_rx_config_##index = { \
231+
.channel = DT_INST_PROP(index, channel), \
232+
.lvd_action = DT_INST_ENUM_IDX(index, lvd_action), \
233+
.vdet_target = DT_INST_ENUM_IDX(index, vdet_target), \
234+
.lvd_support_cmpa = DT_INST_PROP(index, lvd_support_cmpa), \
235+
}; \
236+
\
237+
void rx_lvd_callback_##index(void *args) \
238+
{ \
239+
ARG_UNUSED(args); \
240+
const struct device *dev = DEVICE_DT_GET(DT_INST(index, renesas_rx_lvd)); \
241+
struct lvd_renesas_rx_data *data = dev->data; \
242+
comparator_callback_t cb = data->user_cb; \
243+
\
244+
/* Call the user's callback function*/ \
245+
if (cb) { \
246+
cb(dev, data->user_cb_data); \
247+
return; \
248+
} \
249+
atomic_or(&data->flags, LVD_RENESAS_RX_FLAG); \
250+
}; \
251+
\
252+
static struct lvd_renesas_rx_data lvd_renesas_rx_data_##index = { \
253+
.lvd_config = \
254+
{ \
255+
.trigger = DT_INST_ENUM_IDX(index, lvd_trigger), \
256+
}, \
257+
.callback = rx_lvd_callback_##index, \
258+
.flags = 0, \
259+
}; \
260+
\
261+
DEVICE_DT_INST_DEFINE(index, lvd_renesas_rx_init, NULL, &lvd_renesas_rx_data_##index, \
262+
&lvd_renesas_rx_config_##index, PRE_KERNEL_1, \
263+
CONFIG_COMPARATOR_INIT_PRIORITY, &lvd_renesas_rx_api);
264+
265+
DT_INST_FOREACH_STATUS_OKAY(LVD_RENESAS_RX_INIT)

0 commit comments

Comments
 (0)