Skip to content

Commit 5ea8f99

Browse files
committed
drivers: misc: renesas_lvd: Add support for Renesas LVD driver
Introduce Low/Programmable voltage detection driver for Renesas RX which using r_lvd_rx from RDP. The target is to monitor the voltage level input to Vcc/CMPA2 pin using a program Signed-off-by: Quy Tran <[email protected]>
1 parent d8f38dc commit 5ea8f99

File tree

10 files changed

+380
-0
lines changed

10 files changed

+380
-0
lines changed

drivers/misc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_subdirectory_ifdef(CONFIG_TIMEAWARE_GPIO timeaware_gpio)
1010
add_subdirectory_ifdef(CONFIG_DEVMUX devmux)
1111
add_subdirectory_ifdef(CONFIG_NORDIC_VPR_LAUNCHER nordic_vpr_launcher)
1212
add_subdirectory_ifdef(CONFIG_MCUX_FLEXIO mcux_flexio)
13+
add_subdirectory_ifdef(CONFIG_RENESAS_LVD renesas_lvd)
1314
add_subdirectory_ifdef(CONFIG_RENESAS_RA_EXTERNAL_INTERRUPT renesas_ra_external_interrupt)
1415
add_subdirectory_ifdef(CONFIG_RENESAS_RX_EXTERNAL_INTERRUPT renesas_rx_external_interrupt)
1516
add_subdirectory_ifdef(CONFIG_NXP_RTXXX_DSP_CTRL nxp_rtxxx_dsp_ctrl)

drivers/misc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ source "drivers/misc/nordic_vpr_launcher/Kconfig"
1717
source "drivers/misc/mcux_flexio/Kconfig"
1818
source "drivers/misc/coresight/Kconfig"
1919
source "drivers/misc/interconn/Kconfig"
20+
source "drivers/misc/renesas_lvd/Kconfig"
2021
source "drivers/misc/renesas_ra_external_interrupt/Kconfig"
2122
source "drivers/misc/renesas_rx_external_interrupt/Kconfig"
2223
source "drivers/misc/nxp_rtxxx_dsp_ctrl/Kconfig"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/misc/renesas_lvd/renesas_lvd.h)
4+
5+
zephyr_library()
6+
7+
zephyr_library_sources_ifdef(CONFIG_USERSPACE renesas_lvd_handlers.c)
8+
9+
zephyr_library_sources_ifdef(CONFIG_RENESAS_RX_LVD renesas_rx_lvd.c)

drivers/misc/renesas_lvd/Kconfig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2025 Renesas Electronics Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Renesas Low/Programmable Voltage Detection driver
5+
6+
menuconfig RENESAS_LVD
7+
bool "Renesas Low/Programmable Voltage Detection Driver"
8+
depends on SOC_FAMILY_RENESAS_RX
9+
default y
10+
help
11+
Enable config options for Renesas Low/Programmable Voltage Detection
12+
13+
if RENESAS_LVD
14+
15+
config RENESAS_LVD_INIT_PRIORITY
16+
int "Renesas Low/Programmable Voltage Detection initialization priority"
17+
default KERNEL_INIT_PRIORITY_DEVICE
18+
help
19+
System initialization priority for Renesas Low/Programmable Voltage Detection drivers.
20+
21+
source "drivers/misc/renesas_lvd/Kconfig.renesas_rx_lvd"
22+
23+
endif # RENESAS_LVD
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2025 Renesas Electronics Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Renesas RX Voltage Detection Circuit
5+
6+
config RENESAS_RX_LVD
7+
bool "Renesas RX Low-Voltage Detection Driver"
8+
depends on DT_HAS_RENESAS_RX_LVD_ENABLED
9+
default y
10+
select USE_RX_RDP_LVD
11+
select PINCTRL
12+
help
13+
Enable Renesas RX low voltage detection driver
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2025 Renesas Electronics Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/drivers/misc/renesas_lvd/renesas_lvd.h>
8+
#include <zephyr/internal/syscall_handler.h>
9+
10+
int z_vrfy_renesas_lvd_get_status(const struct device *dev, renesas_lvd_status_t *status)
11+
{
12+
renesas_lvd_status_t status_copy;
13+
14+
K_OOPS(K_SYSCALL_DRIVER_RENESAS_LVD(dev, get_status));
15+
K_OOPS(k_usermode_from_copy(&status_copy, status, sizeof(status_copy)));
16+
17+
int ret = z_impl_renesas_lvd_get_status(dev, &status_copy);
18+
19+
K_OOPS(k_usermode_to_copy(status, &status_copy, sizeof(*status)));
20+
21+
return ret;
22+
}
23+
#include <zephyr/syscalls/renesas_lvd_get_status_mrsh.c>
24+
25+
int z_vrfy_renesas_lvd_clear_status(const struct device *dev)
26+
{
27+
K_OOPS(K_SYSCALL_DRIVER_RENESAS_LVD(dev, clear_status));
28+
return z_impl_renesas_lvd_clear_status(dev);
29+
}
30+
#include <zephyr/syscalls/renesas_lvd_clear_status_mrsh.c>
31+
32+
static int z_vrfy_renesas_lvd_callback_set(const struct device *dev,
33+
renesas_lvd_callback_t callback, void *user_data)
34+
{
35+
K_OOPS(K_SYSCALL_DRIVER_RENESAS_LVD(dev, callback_set));
36+
return z_impl_renesas_lvd_callback_set(dev, callback, user_data);
37+
}
38+
#include <zephyr/syscalls/renesas_lvd_callback_set_mrsh.c>
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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/kernel.h>
10+
#include <zephyr/devicetree.h>
11+
#include <zephyr/irq.h>
12+
#include <soc.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/drivers/misc/renesas_lvd/renesas_lvd.h>
15+
#include <zephyr/drivers/pinctrl.h>
16+
#include "r_lvd_rx_if.h"
17+
18+
LOG_MODULE_REGISTER(renesas_rx_lvd, CONFIG_SOC_LOG_LEVEL);
19+
20+
#define LVD0_NODE DT_NODELABEL(lvd0)
21+
#define LVD1_NODE DT_NODELABEL(lvd1)
22+
/*
23+
* The extern functions below are implemented in the r_lvd_rx_hw.c source file.
24+
* For more information, please refer to r_lvd_rx_hw.c in HAL Renesas
25+
*/
26+
extern void lvd_ch1_isr(void);
27+
extern void lvd_ch2_isr(void);
28+
29+
struct rx_lvd_data {
30+
void (*callback)(void *args);
31+
renesas_lvd_callback_t user_callback;
32+
void *user_data;
33+
};
34+
35+
struct rx_lvd_config {
36+
lvd_channel_t channel;
37+
lvd_config_t lvd_config;
38+
uint8_t vdet_target;
39+
uint8_t lvd_action;
40+
bool lvd_support_cmpa2;
41+
};
42+
43+
static int renesas_rx_lvd_get_status(const struct device *dev, renesas_lvd_status_t *status)
44+
{
45+
const struct rx_lvd_config *config = dev->config;
46+
lvd_status_position_t status_position;
47+
lvd_status_cross_t status_cross;
48+
lvd_err_t err;
49+
50+
err = R_LVD_GetStatus(config->channel, &status_position, &status_cross);
51+
if (err != 0) {
52+
LOG_ERR("Failed to get LVD status");
53+
return -EINVAL;
54+
}
55+
56+
status->position = (renesas_lvd_position_t)status_position;
57+
status->cross = (renesas_lvd_cross_t)status_cross;
58+
59+
return 0;
60+
}
61+
62+
static int renesas_rx_lvd_clear_status(const struct device *dev)
63+
{
64+
const struct rx_lvd_config *config = dev->config;
65+
lvd_err_t err;
66+
67+
err = R_LVD_ClearStatus(config->channel);
68+
if (err != 0) {
69+
LOG_ERR("Failed to clear LVD status");
70+
return -EINVAL;
71+
}
72+
73+
return 0;
74+
}
75+
76+
static int renesas_rx_lvd_callback_set(const struct device *dev, renesas_lvd_callback_t callback,
77+
void *user_data)
78+
{
79+
struct rx_lvd_data *data = dev->data;
80+
81+
data->user_callback = callback;
82+
data->user_data = user_data;
83+
84+
return 0;
85+
}
86+
87+
static int renesas_rx_pin_set_cmpa2(const struct device *dev)
88+
{
89+
const struct rx_lvd_config *config = dev->config;
90+
const struct pinctrl_dev_config *pcfg;
91+
92+
if (config->channel == 0) {
93+
if (DT_NODE_HAS_PROP(LVD0_NODE, pinctrl_0)) {
94+
PINCTRL_DT_DEFINE(LVD0_NODE);
95+
pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD0_NODE);
96+
} else {
97+
LOG_ERR("No pinctrl-0 property found in the device tree");
98+
return -EINVAL;
99+
}
100+
} else {
101+
if (DT_NODE_HAS_PROP(LVD1_NODE, pinctrl_0)) {
102+
PINCTRL_DT_DEFINE(LVD1_NODE);
103+
pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD1_NODE);
104+
} else {
105+
LOG_ERR("No pinctrl_0 property found in the device tree");
106+
return -EINVAL;
107+
}
108+
}
109+
110+
/* In the case of monitoring the CMPA2 pin, set the CMPA2 pin. */
111+
/* This only applicable to channel 1 with the LVDb driver */
112+
int ret = pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
113+
114+
if (ret < 0) {
115+
LOG_ERR("Failed to apply pinctrl state: %d\n", ret);
116+
return -EINVAL;
117+
}
118+
119+
return 0;
120+
}
121+
122+
#define LVD_IRQ_CONNECT() \
123+
do { \
124+
IF_ENABLED(DT_NODE_HAS_STATUS_OKAY(LVD0_NODE), ( \
125+
IRQ_CONNECT(DT_IRQN(LVD0_NODE), \
126+
DT_IRQ(LVD0_NODE, priority), \
127+
lvd_ch1_isr, \
128+
DEVICE_DT_GET(LVD0_NODE), \
129+
0); \
130+
irq_enable(DT_IRQN(LVD0_NODE)); \
131+
)) \
132+
IF_ENABLED(DT_NODE_HAS_STATUS_OKAY(LVD1_NODE), ( \
133+
IRQ_CONNECT(DT_IRQN(LVD1_NODE), \
134+
DT_IRQ(LVD1_NODE, priority), \
135+
lvd_ch2_isr, \
136+
DEVICE_DT_GET(LVD1_NODE), \
137+
0); \
138+
irq_enable(DT_IRQN(LVD1_NODE)); \
139+
)) \
140+
} while (0)
141+
142+
static int renesas_rx_lvd_init(const struct device *dev)
143+
{
144+
lvd_err_t err;
145+
146+
LVD_IRQ_CONNECT();
147+
148+
const struct rx_lvd_config *config = dev->config;
149+
const struct rx_lvd_data *data = dev->data;
150+
151+
/* In reset or no-action when LVD is detected, callback will not be triggered. */
152+
err = R_LVD_Open(config->channel, &config->lvd_config, data->callback);
153+
if (err != 0) {
154+
LOG_ERR("Failed to initialize LVD channel %d", config->channel);
155+
return -EIO;
156+
}
157+
158+
/* Set the CMPA2 pin if the target is CMPA2 */
159+
/* NOTE: For the RX130 series, CMPA2 is only used on channel 2. */
160+
if ((config->lvd_support_cmpa2) && (config->vdet_target == 1)) {
161+
return renesas_rx_pin_set_cmpa2(dev);
162+
}
163+
164+
return 0;
165+
}
166+
167+
static DEVICE_API(renesas_lvd, renesas_rx_lvd_driver_api) = {
168+
.get_status = renesas_rx_lvd_get_status,
169+
.clear_status = renesas_rx_lvd_clear_status,
170+
.callback_set = renesas_rx_lvd_callback_set,
171+
};
172+
173+
#define RENESAS_RX_LVD_INIT(index) \
174+
\
175+
static const struct rx_lvd_config rx_lvd_config_##index = { \
176+
.channel = DT_INST_PROP(index, channel), \
177+
.lvd_config = \
178+
{ \
179+
.trigger = DT_INST_ENUM_IDX(index, lvd_trigger), \
180+
}, \
181+
.lvd_action = DT_INST_ENUM_IDX(index, lvd_action), \
182+
.vdet_target = DT_INST_ENUM_IDX(index, vdet_target), \
183+
.lvd_support_cmpa2 = DT_INST_PROP(index, renesas_lvd_support_cmpa2), \
184+
}; \
185+
\
186+
void rx_lvd_callback_##index(void *args) \
187+
{ \
188+
ARG_UNUSED(args); \
189+
const struct device *dev = DEVICE_DT_GET(DT_INST(index, renesas_rx_lvd)); \
190+
struct rx_lvd_data *data = dev->data; \
191+
\
192+
/* Call the user's callback function*/ \
193+
if (data->user_callback) { \
194+
data->user_callback(dev, data->user_data); \
195+
} \
196+
}; \
197+
\
198+
static struct rx_lvd_data rx_lvd_data_##index = { \
199+
.callback = rx_lvd_callback_##index, \
200+
}; \
201+
\
202+
DEVICE_DT_INST_DEFINE(index, renesas_rx_lvd_init, NULL, &rx_lvd_data_##index, \
203+
&rx_lvd_config_##index, PRE_KERNEL_1, \
204+
CONFIG_RENESAS_LVD_INIT_PRIORITY, &renesas_rx_lvd_driver_api);
205+
206+
DT_INST_FOREACH_STATUS_OKAY(RENESAS_RX_LVD_INIT)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright (c) 2025 Renesas Electronics Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Renesas RX LVD driver
5+
6+
compatible: "renesas,rx-lvd"
7+
8+
include: [base.yaml, pinctrl-device.yaml]
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
channel:
15+
type: int
16+
description: RX LVD channel
17+
18+
lvd-action:
19+
required: true
20+
type: string
21+
enum:
22+
- "reset"
23+
- "non-maskable-interrupt"
24+
- "maskable-interrupt"
25+
- "no-action"
26+
description: |
27+
Choose the action to be taken when the LVD is detected.
28+
29+
voltage-level:
30+
required: true
31+
type: int
32+
description: |
33+
Specifies the voltage detection level for each channel, as an integer
34+
value that represents the voltage in hundredths of a volt.
35+
Example:
36+
- To set the voltage detection level to 3.00 V, specify '300'.
37+
- To set the voltage detection level to 3.84 V, specify '384'.
38+
- To set the voltage detection level to default value, specify '0xFFFF'
39+
For specific voltage detection support levels of each RX MCU series,
40+
please refer to the HWM.
41+
42+
renesas,lvd-support-cmpa2:
43+
type: boolean
44+
description: |
45+
Specifies whether the RX LVD driver supports CMPA pin as target.
46+
Note: For RX130 series, CMPA pin is available only for channel 1.
47+
48+
vdet-target:
49+
required: true
50+
type: string
51+
enum:
52+
- vcc
53+
- cmpa
54+
description: |
55+
Specifies the target to be monitored for each channel.
56+
Note: For RX130 series, CMPA pin is available only for channel 1.
57+
58+
lvd-trigger:
59+
required: true
60+
type: string
61+
enum:
62+
- "rising"
63+
- "falling"
64+
- "both"
65+
description: |
66+
Specifies the voltage detection conditions and influences interrupt conditions
67+
68+
lvd-stabilization:
69+
type: int
70+
enum: [0, 1]
71+
description: |
72+
Specifies the reset negation timing for each channel, with reset selected as processing.
73+
- 0: After a LVD reset, negation occurs when a certain period elapses after the
74+
monitored voltage goes above the voltage detection level.
75+
- 1: Negation occurs when a certain period elapses after the LVD reset assertion.
76+
Note: "a certain period" here means a wait time after a voltage monitoring reset.
77+
Refer to the User's Manual: Hardware for details.
78+
79+
pinctrl-0:
80+
description: Pin control for LVD when using CMPA pin as target
81+
82+
pinctrl-names:
83+
description: Pin control names for LVD when using CMPA pin as target

0 commit comments

Comments
 (0)