diff --git a/drivers/comparator/CMakeLists.txt b/drivers/comparator/CMakeLists.txt index 43e98ce2a7000..19e0751b17217 100644 --- a/drivers/comparator/CMakeLists.txt +++ b/drivers/comparator/CMakeLists.txt @@ -15,3 +15,4 @@ 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_RENESAS_RA comparator_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RX_LVD comparator_renesas_rx_lvd.c) diff --git a/drivers/comparator/Kconfig b/drivers/comparator/Kconfig index 8aec586bdc3f2..be9ddd121f6c8 100644 --- a/drivers/comparator/Kconfig +++ b/drivers/comparator/Kconfig @@ -27,5 +27,6 @@ rsource "Kconfig.nrf_comp" rsource "Kconfig.nrf_lpcomp" rsource "Kconfig.shell" rsource "Kconfig.renesas_ra" +rsource "Kconfig.renesas_rx" endif # COMPARATOR diff --git a/drivers/comparator/Kconfig.renesas_rx b/drivers/comparator/Kconfig.renesas_rx new file mode 100644 index 0000000000000..eb957343e2c62 --- /dev/null +++ b/drivers/comparator/Kconfig.renesas_rx @@ -0,0 +1,11 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_RENESAS_RX_LVD + bool "Renesas RX LVD" + default y + depends on DT_HAS_RENESAS_RX_LVD_ENABLED + select USE_RX_RDP_LVD + select PINCTRL + help + Enable Comparator driver with Low Voltage Detection (LVD) feature for Renesas RX MCUs. diff --git a/drivers/comparator/comparator_renesas_rx_lvd.c b/drivers/comparator/comparator_renesas_rx_lvd.c new file mode 100644 index 0000000000000..e4511a19282de --- /dev/null +++ b/drivers/comparator/comparator_renesas_rx_lvd.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rx_lvd + +#include +#include +#include +#include +#include +#include +#include +#include "r_lvd_rx_if.h" + +LOG_MODULE_REGISTER(renesas_rx_lvd, CONFIG_COMPARATOR_LOG_LEVEL); + +#define LVD0_NODE DT_NODELABEL(lvd0) +#define LVD1_NODE DT_NODELABEL(lvd1) +#define LVD_RENESAS_RX_FLAG BIT(0) +/* + * The extern functions below are implemented in the r_lvd_rx_hw.c source file. + * For more information, please refer to r_lvd_rx_hw.c in HAL Renesas + */ +extern void lvd_ch1_isr(void); +extern void lvd_ch2_isr(void); +extern void lvd_start_lvd(lvd_channel_t ch, lvd_trigger_t trigger); +extern void lvd_stop_lvd(lvd_channel_t ch); +extern void lvd_start_int(lvd_channel_t ch, void (*p_callback)(void *)); +extern void lvd_stop_int(lvd_channel_t ch); +extern void lvd_hw_enable_reset_int(lvd_channel_t ch, bool enable); +extern void lvd_hw_enable_reg_protect(bool enable); + +struct lvd_renesas_rx_data { + lvd_config_t lvd_config; + void (*callback)(void *args); + comparator_callback_t user_cb; + void *user_cb_data; + atomic_t flags; +}; + +struct lvd_renesas_rx_config { + lvd_channel_t channel; + uint8_t vdet_target; + uint8_t lvd_action; + bool lvd_support_cmpa; +}; + +static int lvd_renesas_rx_get_output(const struct device *dev) +{ + const struct lvd_renesas_rx_config *config = dev->config; + lvd_status_position_t status_position; + /* unused variable, just for API compatibility */ + lvd_status_cross_t unused_status_cross; + lvd_err_t err; + + err = R_LVD_GetStatus(config->channel, &status_position, &unused_status_cross); + if (err != 0) { + LOG_ERR("Failed to get status"); + return -EIO; + } + + switch (status_position) { + case LVD_STATUS_POSITION_ABOVE: + return 1; + + case LVD_STATUS_POSITION_BELOW: + return 0; + + default: + LOG_ERR("Invalid status, please check the configuration"); + return -EIO; + } +} + +static int lvd_renesas_rx_set_trigger(const struct device *dev, enum comparator_trigger trigger) +{ + struct lvd_renesas_rx_data *data = dev->data; + const struct lvd_renesas_rx_config *config = dev->config; + + lvd_hw_enable_reg_protect(false); + lvd_stop_lvd(config->channel); + lvd_stop_int(config->channel); + + switch (trigger) { + case COMPARATOR_TRIGGER_RISING_EDGE: + data->lvd_config.trigger = LVD_TRIGGER_RISE; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + data->lvd_config.trigger = LVD_TRIGGER_FALL; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + data->lvd_config.trigger = LVD_TRIGGER_BOTH; + break; + + case COMPARATOR_TRIGGER_NONE: + LOG_ERR("Trigger NONE is not supported"); + return -ENOTSUP; + } + + lvd_start_int(config->channel, data->callback); + lvd_start_lvd(config->channel, data->lvd_config.trigger); + lvd_hw_enable_reg_protect(true); + + return 0; +} + +static int lvd_renesas_rx_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, void *user_data) +{ + struct lvd_renesas_rx_data *data = dev->data; + const struct lvd_renesas_rx_config *config = dev->config; + + if ((config->lvd_action == 0) || (config->lvd_action == 3)) { + LOG_ERR("Callback function is not supported with the current action"); + return -ENOTSUP; + } + + /* Disable interrupt */ + lvd_hw_enable_reset_int(config->channel, false); + + data->user_cb = callback; + data->user_cb_data = user_data; + + /* Enable interrupt */ + lvd_hw_enable_reset_int(config->channel, true); + return 0; +} + +static int lvd_renesas_rx_trigger_is_pending(const struct device *dev) +{ + struct lvd_renesas_rx_data *data = dev->data; + const struct lvd_renesas_rx_config *config = dev->config; + + if (data->flags & LVD_RENESAS_RX_FLAG) { + atomic_and(&data->flags, ~LVD_RENESAS_RX_FLAG); + R_LVD_ClearStatus(config->channel); + return 1; + } + + return 0; +} + +static int renesas_rx_pin_set_cmpa(const struct device *dev) +{ + const struct lvd_renesas_rx_config *config = dev->config; + const struct pinctrl_dev_config *pcfg; + int ret; + + if (config->channel == 0) { + if (DT_NODE_HAS_PROP(LVD0_NODE, pinctrl_0)) { + PINCTRL_DT_DEFINE(LVD0_NODE); + pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD0_NODE); + } else { + LOG_ERR("No pinctrl-0 property found in the device tree"); + return -EINVAL; + } + } else { + if (DT_NODE_HAS_PROP(LVD1_NODE, pinctrl_0)) { + PINCTRL_DT_DEFINE(LVD1_NODE); + pcfg = PINCTRL_DT_DEV_CONFIG_GET(LVD1_NODE); + } else { + LOG_ERR("No pinctrl_0 property found in the device tree"); + return -EINVAL; + } + } + + /* In the case of monitoring the CMPA pin, set the CMPA pin. */ + ret = pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl state: %d\n", ret); + return -EINVAL; + } + + return 0; +} + +static inline void lvd_irq_connect(void) +{ +#if DT_NODE_HAS_STATUS_OKAY(LVD0_NODE) + IRQ_CONNECT(DT_IRQN(LVD0_NODE), DT_IRQ(LVD0_NODE, priority), lvd_ch1_isr, + DEVICE_DT_GET(LVD0_NODE), 0); + irq_enable(DT_IRQN(LVD0_NODE)); +#endif +#if DT_NODE_HAS_STATUS_OKAY(LVD1_NODE) + IRQ_CONNECT(DT_IRQN(LVD1_NODE), DT_IRQ(LVD1_NODE, priority), lvd_ch2_isr, + DEVICE_DT_GET(LVD1_NODE), 0); + irq_enable(DT_IRQN(LVD1_NODE)); +#endif +} + +static int lvd_renesas_rx_init(const struct device *dev) +{ + lvd_err_t err; + + lvd_irq_connect(); + + const struct lvd_renesas_rx_config *config = dev->config; + const struct lvd_renesas_rx_data *data = dev->data; + + /* In reset or no-action when LVD is detected, callback will not be triggered. */ + err = R_LVD_Open(config->channel, &data->lvd_config, data->callback); + if (err != 0) { + LOG_ERR("Failed to initialize LVD channel %d", config->channel); + return -EIO; + } + + /* Set the CMPA pin if the target is CMPA */ + /* NOTE: For the RX130 series, CMPA is only used on channel 2. */ + if ((config->lvd_support_cmpa) && (config->vdet_target == 1)) { + return renesas_rx_pin_set_cmpa(dev); + } + + return 0; +} + +static DEVICE_API(comparator, lvd_renesas_rx_api) = { + .get_output = lvd_renesas_rx_get_output, + .set_trigger = lvd_renesas_rx_set_trigger, + .set_trigger_callback = lvd_renesas_rx_set_trigger_callback, + .trigger_is_pending = lvd_renesas_rx_trigger_is_pending, +}; + +#define LVD_RENESAS_RX_INIT(index) \ + \ + static const struct lvd_renesas_rx_config lvd_renesas_rx_config_##index = { \ + .channel = DT_INST_PROP(index, channel), \ + .lvd_action = DT_INST_ENUM_IDX(index, lvd_action), \ + .vdet_target = DT_INST_ENUM_IDX(index, vdet_target), \ + .lvd_support_cmpa = DT_INST_PROP(index, lvd_support_cmpa), \ + }; \ + \ + void rx_lvd_callback_##index(void *args) \ + { \ + ARG_UNUSED(args); \ + const struct device *dev = DEVICE_DT_GET(DT_INST(index, renesas_rx_lvd)); \ + struct lvd_renesas_rx_data *data = dev->data; \ + comparator_callback_t cb = data->user_cb; \ + \ + /* Call the user's callback function*/ \ + if (cb) { \ + cb(dev, data->user_cb_data); \ + return; \ + } \ + atomic_or(&data->flags, LVD_RENESAS_RX_FLAG); \ + }; \ + \ + static struct lvd_renesas_rx_data lvd_renesas_rx_data_##index = { \ + .lvd_config = \ + { \ + .trigger = DT_INST_ENUM_IDX(index, lvd_trigger), \ + }, \ + .callback = rx_lvd_callback_##index, \ + .flags = 0, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, lvd_renesas_rx_init, NULL, &lvd_renesas_rx_data_##index, \ + &lvd_renesas_rx_config_##index, PRE_KERNEL_1, \ + CONFIG_COMPARATOR_INIT_PRIORITY, &lvd_renesas_rx_api); + +DT_INST_FOREACH_STATUS_OKAY(LVD_RENESAS_RX_INIT) diff --git a/dts/bindings/comparator/renesas,rx-lvd.yaml b/dts/bindings/comparator/renesas,rx-lvd.yaml new file mode 100644 index 0000000000000..8b10b7ae1e1e9 --- /dev/null +++ b/dts/bindings/comparator/renesas,rx-lvd.yaml @@ -0,0 +1,123 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: | + Renesas RX LVD (Low-voltage detection) Controller + + The following example displays the minimum node layout: + + lvd: lvd@800e2 { + compatible = "renesas,rx-lvd"; + reg = <0x000800e2 0x02>; + interrupts = <89 1>; + status = "disabled"; + }; + + Enabling the comparator controller node requires setting the minimum + default configuration of the comparator. This includes selecting the + positive and negative inputs. + Note: negative input of this controller is selected through specific + voltage threshold levels, and positive input can be either Vcc or CMPA pin. + If CMPA pin is selected as positive input, pinctrl must be defined to + route CMPA pin to the controller. + + &pinctrl { + lvd1_cmpa: lvd1_cmpa { + group1 { + /* CMPA */ + psels = ; + renesas,analog-enable; + input-enable; + }; + }; + }; + + &lvd1 { + pinctrl-0 = <&lvd1_cmpa>; + pinctrl-names = "default"; + lvd-action = "maskable-interrupt"; + voltage_level = <384>; + vdet-target = "cmpa"; + lvd-trigger = "rising"; + lvd-stabilization = <0>; + status = "okay"; + }; + +compatible: "renesas,rx-lvd" + +include: [base.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + channel: + type: int + required: true + + lvd-action: + required: true + type: string + enum: + - "reset" + - "non-maskable-interrupt" + - "maskable-interrupt" + - "no-action" + description: | + Choose the action to be taken when the LVD is detected. + + lvd-support-cmpa: + type: boolean + description: | + Specifies whether the RX LVD supports CMPA pin as target. + + voltage-level: + required: true + type: int + description: | + Specifies the voltage detection level for each channel, as an integer + value that represents the voltage in hundredths of a volt. + Example: + - To set the voltage detection level to 3.00 V, specify '300'. + - To set the voltage detection level to 3.84 V, specify '384'. + - To set the voltage detection level to default value, specify '0xFFFF' + For specific voltage detection support levels of each RX MCU series, + please refer to the HWM. + + vdet-target: + required: true + type: string + enum: + - vcc + - cmpa + description: | + Specifies the target to be monitored for each channel. + Not all RX LVD channels support CMPA pin as target. + Please refer to the HWM for details. + + lvd-trigger: + required: true + type: string + enum: + - "rising" + - "falling" + - "both" + description: | + Specifies the voltage detection conditions and influences interrupt conditions + + lvd-stabilization: + type: int + enum: [0, 1] + description: | + Specifies the reset negation timing for each channel, with reset selected as processing. + - 0: After a LVD reset, negation occurs when a certain period elapses after the + monitored voltage goes above the voltage detection level. + - 1: Negation occurs when a certain period elapses after the LVD reset assertion. + Note: "a certain period" here means a wait time after a voltage monitoring reset. + Refer to the User's Manual: Hardware for details. + + pinctrl-0: + description: Pin control for LVD when using CMPA pin as target + + pinctrl-names: + description: Pin control names for LVD when using CMPA pin as target diff --git a/dts/rx/renesas/rx130-common.dtsi b/dts/rx/renesas/rx130-common.dtsi index 86e8232c04d2a..1967a9d47fbc4 100644 --- a/dts/rx/renesas/rx130-common.dtsi +++ b/dts/rx/renesas/rx130-common.dtsi @@ -833,6 +833,25 @@ status = "disabled"; }; + lvd0: lvd@800e0 { + compatible = "renesas,rx-lvd"; + reg = <0x000800E0 0x02>; + channel = <0>; + interrupts = <88 1>; + interrupt-names = "lvd"; + status = "disabled"; + }; + + lvd1: lvd@800e2 { + compatible = "renesas,rx-lvd"; + reg = <0x000800E2 0x02>; + channel = <1>; + interrupts = <89 1>; + interrupt-names = "lvd"; + lvd-support-cmpa; + status = "disabled"; + }; + ofsm: ofsm@ffffff80 { compatible = "zephyr,memory-region"; reg = <0xFFFFFF80 0x0F>; diff --git a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-rx.h b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-rx.h index 676ea5ded4b6c..600c01e606bfc 100644 --- a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-rx.h +++ b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-rx.h @@ -25,6 +25,7 @@ #define RX_PSEL_TMR 0x5 #define RX_PSEL_POE 0x7 #define RX_PSEL_ADC 0x0 +#define RX_PSEL_LVD 0x0 /* P0nPFS */ #define RX_PSEL_P0nPFS_HIZ 0x0 diff --git a/modules/Kconfig.renesas b/modules/Kconfig.renesas index f0b12bd26d28e..c0fccb394f68f 100644 --- a/modules/Kconfig.renesas +++ b/modules/Kconfig.renesas @@ -366,4 +366,9 @@ config USE_RX_RDP_ADC help Enable RX RDP ADC driver +config USE_RX_RDP_LVD + bool + help + Enable RX RDP LVD driver + endif # HAS_RENESAS_RX_RDP diff --git a/samples/boards/renesas/lvd/CMakeLists.txt b/samples/boards/renesas/lvd/CMakeLists.txt new file mode 100644 index 0000000000000..d867843a7a0b4 --- /dev/null +++ b/samples/boards/renesas/lvd/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lvd) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE src/main.c) diff --git a/samples/boards/renesas/lvd/README.rst b/samples/boards/renesas/lvd/README.rst new file mode 100644 index 0000000000000..3a880cecb1cf7 --- /dev/null +++ b/samples/boards/renesas/lvd/README.rst @@ -0,0 +1,30 @@ +.. zephyr:code-sample:: renesas_lvd + :name: Renesas Low-voltage Detection Sample using Comparator + + Demonstrates monitoring and reacting to voltage levels. + +Overview +******** + +This sample application shows how to use Comparator to monitor voltage levels +with Renesas Low-voltage Detection (LVD). + +Hardware Setup +************** + +- Ensure that the monitored target pin is supplied with power. + +Building and Running +******************** + +To build and flash the sample on a supported Renesas RX board: + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/renesas/lvd + :board: rsk_rx130@512kb + :goals: build flash + :compact: + +The comparator configures trigger is rising edge. When the voltage on the monitored pin +crosses the threshold (Vref) - defined in the board's device tree, an interrupt is generated +and turns the LED on. diff --git a/samples/boards/renesas/lvd/boards/rsk_rx130_512kb.overlay b/samples/boards/renesas/lvd/boards/rsk_rx130_512kb.overlay new file mode 100644 index 0000000000000..9f6923ad9932c --- /dev/null +++ b/samples/boards/renesas/lvd/boards/rsk_rx130_512kb.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + lvd1_default: lvd1_default { + group1 { + /* CMPA2 */ + psels = ; + renesas,analog-enable; + input-enable; + }; + }; +}; + +&lvd1 { + pinctrl-0 = <&lvd1_default>; + pinctrl-names = "default"; + lvd-action = "maskable-interrupt"; + voltage-level = <384>; + vdet-target = "cmpa"; + lvd-trigger = "rising"; + lvd-stabilization = <0>; + status = "okay"; +}; diff --git a/samples/boards/renesas/lvd/prj.conf b/samples/boards/renesas/lvd/prj.conf new file mode 100644 index 0000000000000..94d0c848f17ef --- /dev/null +++ b/samples/boards/renesas/lvd/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_GPIO=y +CONFIG_COMPARATOR=y diff --git a/samples/boards/renesas/lvd/sample.yaml b/samples/boards/renesas/lvd/sample.yaml new file mode 100644 index 0000000000000..fcf32daa61035 --- /dev/null +++ b/samples/boards/renesas/lvd/sample.yaml @@ -0,0 +1,7 @@ +sample: + name: Low Voltage Detector (LVD) +tests: + sample.boards.renesas.lvd: + platform_allow: + - rsk_rx130@512kb + tags: lvd diff --git a/samples/boards/renesas/lvd/src/main.c b/samples/boards/renesas/lvd/src/main.c new file mode 100644 index 0000000000000..14572b96663d3 --- /dev/null +++ b/samples/boards/renesas/lvd/src/main.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#if DT_HAS_COMPAT_STATUS_OKAY(renesas_rx_lvd) +#define LVD_DEV DT_INST(0, renesas_rx_lvd) +#else +#error "Please set the correct device" +#endif + +static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0}); +static const struct device *lvd_dev = DEVICE_DT_GET(LVD_DEV); + +#define VREF_MV (DT_PROP(LVD_DEV, voltage_level)) + +static void lvd_callback(const struct device *dev, void *user_data) +{ + int ret; + + printk("[WARNING] Voltage instability detected! Check power supply.\n"); + gpio_pin_set_dt(&led, 1); + + ret = comparator_get_output(lvd_dev); + if (ret < 0) { + printk("Error: failed to get comparator output\n"); + return; + } + printk("Comparator output is %s Vref (%.2fV)\n", ret ? "ABOVE" : "BELOW", + ((double)VREF_MV / 100)); +} + +int main(void) +{ + int ret; + + if (!device_is_ready(lvd_dev)) { + printk("Comparator device not ready\n"); + return 0; + } + + ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + if (ret != 0) { + printk("Error: failed to configure LED\n"); + return -EINVAL; + } + + gpio_pin_set_dt(&led, 0); + + ret = comparator_get_output(lvd_dev); + if (ret < 0) { + printk("Error: failed to get comparator output\n"); + return -EINVAL; + } + + printk("Comparator output is %s Vref (%.2fV)\n", ret ? "ABOVE" : "BELOW", + ((double)VREF_MV / 100)); + + ret = comparator_set_trigger(lvd_dev, COMPARATOR_TRIGGER_RISING_EDGE); + if (ret < 0) { + printk("Error: failed to set comparator trigger\n"); + return -EINVAL; + } + + ret = comparator_set_trigger_callback(lvd_dev, lvd_callback, NULL); + if (ret < 0) { + printk("Error: failed to set comparator callback\n"); + return -EINVAL; + } + + while (1) { + k_sleep(K_MSEC(100)); + } +} diff --git a/west.yml b/west.yml index e56baad71cc6e..bd5bef8963f4f 100644 --- a/west.yml +++ b/west.yml @@ -226,7 +226,7 @@ manifest: - hal - name: hal_renesas path: modules/hal/renesas - revision: f0b78440ba8f0b4a07cdd657031ac9ec1cc4c126 + revision: pull/136/head groups: - hal - name: hal_rpi_pico