From cf9e92742b8b8dcaa7804109e94d7dba95fde60d Mon Sep 17 00:00:00 2001 From: Khoa Nguyen Date: Wed, 5 Feb 2025 16:23:13 +0700 Subject: [PATCH] drivers: comparator: Initial Comparator support for Renesas RA Initial Comparator driver support for Renesas RA Signed-off-by: Khoa Nguyen --- drivers/comparator/CMakeLists.txt | 1 + drivers/comparator/Kconfig | 1 + drivers/comparator/Kconfig.renesas_ra | 11 + drivers/comparator/comparator_renesas_ra.c | 282 ++++++++++++++++++ .../comparator/renesas,ra-acmphs-global.yaml | 49 +++ .../comparator/renesas,ra-acmphs.yaml | 119 ++++++++ .../dt-bindings/pinctrl/renesas/pinctrl-ra.h | 2 + modules/Kconfig.renesas_fsp | 5 + 8 files changed, 470 insertions(+) create mode 100644 drivers/comparator/Kconfig.renesas_ra create mode 100644 drivers/comparator/comparator_renesas_ra.c create mode 100644 dts/bindings/comparator/renesas,ra-acmphs-global.yaml create mode 100644 dts/bindings/comparator/renesas,ra-acmphs.yaml diff --git a/drivers/comparator/CMakeLists.txt b/drivers/comparator/CMakeLists.txt index cbfa301d580..5d8ef055461 100644 --- a/drivers/comparator/CMakeLists.txt +++ b/drivers/comparator/CMakeLists.txt @@ -12,3 +12,4 @@ zephyr_library_sources_ifdef(CONFIG_COMPARATOR_MCUX_ACMP comparator_mcux_acmp.c) 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) diff --git a/drivers/comparator/Kconfig b/drivers/comparator/Kconfig index 79538a6cc22..2cc02c24085 100644 --- a/drivers/comparator/Kconfig +++ b/drivers/comparator/Kconfig @@ -24,5 +24,6 @@ rsource "Kconfig.mcux_acmp" rsource "Kconfig.nrf_comp" rsource "Kconfig.nrf_lpcomp" rsource "Kconfig.shell" +rsource "Kconfig.renesas_ra" endif # COMPARATOR diff --git a/drivers/comparator/Kconfig.renesas_ra b/drivers/comparator/Kconfig.renesas_ra new file mode 100644 index 00000000000..f66390a32ab --- /dev/null +++ b/drivers/comparator/Kconfig.renesas_ra @@ -0,0 +1,11 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config COMPARATOR_RENESAS_RA + bool "Renesas RA ACMPHS" + default y + depends on DT_HAS_RENESAS_RA_ACMPHS_ENABLED + select USE_RA_FSP_ACMPHS + select PINCTRL + help + Enable Renesas RA ACMPHS Driver. diff --git a/drivers/comparator/comparator_renesas_ra.c b/drivers/comparator/comparator_renesas_ra.c new file mode 100644 index 00000000000..6b0cb98249b --- /dev/null +++ b/drivers/comparator/comparator_renesas_ra.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_acmphs + +#include +#include +#include +#include +#include +#include +#include +#include "r_acmphs.h" + +LOG_MODULE_REGISTER(acmphs_renesas_ra, CONFIG_COMPARATOR_LOG_LEVEL); + +#define ACMPHS_RENESAS_RA_FLAG_HS BIT(0) + +void comp_hs_int_isr(void); + +struct acmphs_renesas_ra_global_config { + const struct pinctrl_dev_config *pcfg; +}; + +struct acmphs_renesas_ra_config { + const struct pinctrl_dev_config *pcfg; +}; + +struct acmphs_renesas_ra_data { + const struct device *dev; + acmphs_instance_ctrl_t acmphs; + comparator_cfg_t fsp_config; + comparator_callback_t user_cb; + void *user_cb_data; + atomic_t flags; +}; + +static int acmphs_renesas_get_output(const struct device *dev) +{ + struct acmphs_renesas_ra_data *data = dev->data; + comparator_status_t status; + fsp_err_t fsp_err; + + fsp_err = R_ACMPHS_StatusGet(&data->acmphs, &status); + if (FSP_SUCCESS != fsp_err) { + return -EIO; + } + + switch (status.state) { + case COMPARATOR_STATE_OUTPUT_LOW: + return 0; + + case COMPARATOR_STATE_OUTPUT_HIGH: + return 1; + + case COMPARATOR_STATE_OUTPUT_DISABLED: + LOG_ERR("Need to set trigger to open comparator first"); + return -EIO; + } + + return 0; +} + +static int acmphs_renesas_set_trigger(const struct device *dev, enum comparator_trigger trigger) +{ + struct acmphs_renesas_ra_data *data = dev->data; + + /* Disable interrupt */ + data->acmphs.p_reg->CMPCTL &= ~R_ACMPHS0_CMPCTL_CEG_Msk; + + switch (trigger) { + case COMPARATOR_TRIGGER_RISING_EDGE: + data->fsp_config.trigger = COMPARATOR_TRIGGER_RISING; + break; + + case COMPARATOR_TRIGGER_FALLING_EDGE: + data->fsp_config.trigger = COMPARATOR_TRIGGER_FALLING; + break; + + case COMPARATOR_TRIGGER_BOTH_EDGES: + data->fsp_config.trigger = COMPARATOR_TRIGGER_BOTH_EDGE; + break; + + case COMPARATOR_TRIGGER_NONE: + data->fsp_config.trigger = COMPARATOR_TRIGGER_NO_EDGE; + break; + } + + data->acmphs.p_reg->CMPCTL |= (data->fsp_config.trigger << R_ACMPHS0_CMPCTL_CEG_Pos); + + return 0; +} + +static int acmphs_renesas_set_trigger_callback(const struct device *dev, + comparator_callback_t callback, void *user_data) +{ + struct acmphs_renesas_ra_data *data = dev->data; + + /* Disable interrupt */ + data->acmphs.p_reg->CMPCTL &= ~R_ACMPHS0_CMPCTL_CEG_Msk; + + data->user_cb = callback; + data->user_cb_data = user_data; + + /* Enable interrupt */ + data->acmphs.p_reg->CMPCTL |= (data->fsp_config.trigger << R_ACMPHS0_CMPCTL_CEG_Pos); + + return 0; +} + +static int acmphs_renesas_trigger_is_pending(const struct device *dev) +{ + struct acmphs_renesas_ra_data *data = dev->data; + + if (data->flags & ACMPHS_RENESAS_RA_FLAG_HS) { + atomic_and(&data->flags, ~ACMPHS_RENESAS_RA_FLAG_HS); + return 1; + } + + return 0; +} + +static void acmphs_renesas_ra_hs_isr(comparator_callback_args_t *fsp_args) +{ + const struct device *dev = fsp_args->p_context; + struct acmphs_renesas_ra_data *data = dev->data; + comparator_callback_t cb = data->user_cb; + + if (cb != NULL) { + cb(dev, data->user_cb_data); + return; + } + + atomic_or(&data->flags, ACMPHS_RENESAS_RA_FLAG_HS); +} + +static int acmphs_renesas_ra_global_init(const struct device *dev) +{ + const struct acmphs_renesas_ra_global_config *cfg = dev->config; + int ret; + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_DBG("pin VCOUT initial failed"); + return ret; + } + + return 0; +} + +static int acmphs_renesas_ra_init(const struct device *dev) +{ + struct acmphs_renesas_ra_data *data = dev->data; + const struct acmphs_renesas_ra_config *cfg = dev->config; + fsp_err_t fsp_err; + int ret; + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_DBG("pin default initial failed"); + return ret; + } + + data->fsp_config.p_context = dev; + + fsp_err = R_ACMPHS_Open(&data->acmphs, &data->fsp_config); + if (FSP_SUCCESS != fsp_err) { + return -EIO; + } + + /* + * Once the analog comparator is configurate, the program must wait + * for the ACMPHS stabilization time (300ns enabling + 200ns input switching) + * before using the comparator. + */ + k_usleep(5); + + fsp_err = R_ACMPHS_OutputEnable(&data->acmphs); + if (FSP_SUCCESS != fsp_err) { + return -EIO; + } + + return 0; +} + +static DEVICE_API(comparator, acmphs_renesas_ra_api) = { + .get_output = acmphs_renesas_get_output, + .set_trigger = acmphs_renesas_set_trigger, + .set_trigger_callback = acmphs_renesas_set_trigger_callback, + .trigger_is_pending = acmphs_renesas_trigger_is_pending, +}; + +PINCTRL_DT_DEFINE(DT_INST(0, renesas_ra_acmphs_global)); + +static const struct acmphs_renesas_ra_global_config acmphs_renesas_ra_global_config = { + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST(0, renesas_ra_acmphs_global)), +}; + +DEVICE_DT_DEFINE(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_acmphs_global), + acmphs_renesas_ra_global_init, NULL, NULL, &acmphs_renesas_ra_global_config, + PRE_KERNEL_2, CONFIG_COMPARATOR_INIT_PRIORITY, NULL) + +#define ACMPHS_RENESAS_RA_IRQ_INIT(idx) \ + { \ + R_ICU->IELSR_b[DT_INST_IRQ_BY_NAME(idx, hs, irq)].IELS = \ + BSP_PRV_IELS_ENUM(CONCAT(EVENT_ACMPHS, DT_INST_PROP(idx, channel), _INT)); \ + \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, hs, irq), \ + DT_INST_IRQ_BY_NAME(idx, hs, priority), comp_hs_int_isr, \ + DEVICE_DT_INST_GET(idx), 0); \ + \ + irq_enable(DT_INST_IRQ_BY_NAME(idx, hs, irq)); \ + } + +#define FILTER_PARAMETER(idx) \ + COND_CODE_1(IS_EQ(DT_INST_PROP(idx, noise_filter), 1), \ + (COMPARATOR_FILTER_OFF), (UTIL_CAT(COMPARATOR_FILTER_, DT_INST_PROP(idx, noise_filter)))) + +#define INVERT_PARAMETER(idx) \ + COND_CODE_1(DT_INST_PROP(idx, output_invert_polarity), \ + (COMPARATOR_POLARITY_INVERT_ON), (COMPARATOR_POLARITY_INVERT_OFF)) + +#define PIN_OUTPUT_PARAMETER(idx) \ + COND_CODE_1(DT_INST_PROP(idx, pin_output_enable), \ + (COMPARATOR_PIN_OUTPUT_ON), (COMPARATOR_PIN_OUTPUT_OFF)) + +#define IRQ_PARAMETER(idx) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs), \ + (DT_INST_IRQ_BY_NAME(idx, hs, irq)), (FSP_INVALID_VECTOR)) + +#define IPL_PARAMETER(idx) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs), \ + (DT_INST_IRQ_BY_NAME(idx, hs, priority)), (BSP_IRQ_DISABLED)) + +#define IRQ_INIT_MACRO_FUNCTION(idx) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs), (ACMPHS_RENESAS_RA_IRQ_INIT(idx);), ()) + +#define ACMPHS_RENESAS_RA_INIT(idx) \ + PINCTRL_DT_INST_DEFINE(idx); \ + static r_acmphs_extended_cfg_t g_acmphs_cfg_extend_##idx = { \ + .input_voltage = UTIL_CAT(ACMPHS_INPUT_, \ + DT_INST_STRING_UPPER_TOKEN(idx, compare_input_source)), \ + .reference_voltage = \ + UTIL_CAT(ACMPHS_REFERENCE_, \ + DT_INST_STRING_UPPER_TOKEN(idx, reference_input_source)), \ + .maximum_status_retries = 1024, \ + }; \ + static const struct acmphs_renesas_ra_config acmphs_renesas_ra_config_##idx = { \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ + }; \ + static struct acmphs_renesas_ra_data acmphs_renesas_ra_data_##idx = { \ + .dev = DEVICE_DT_INST_GET(idx), \ + .fsp_config = \ + { \ + .channel = DT_INST_PROP(idx, channel), \ + .mode = COMPARATOR_MODE_NORMAL, \ + .trigger = COMPARATOR_TRIGGER_NO_EDGE, \ + .filter = FILTER_PARAMETER(idx), \ + .invert = INVERT_PARAMETER(idx), \ + .pin_output = PIN_OUTPUT_PARAMETER(idx), \ + .p_extend = &g_acmphs_cfg_extend_##idx, \ + .irq = IRQ_PARAMETER(idx), \ + .ipl = IPL_PARAMETER(idx), \ + .p_callback = acmphs_renesas_ra_hs_isr, \ + }, \ + .flags = 0, \ + }; \ + static int acmphs_renesas_ra_init##idx(const struct device *dev) \ + { \ + IRQ_INIT_MACRO_FUNCTION(idx) \ + return acmphs_renesas_ra_init(dev); \ + } \ + \ + DEVICE_DT_INST_DEFINE(idx, acmphs_renesas_ra_init##idx, NULL, \ + &acmphs_renesas_ra_data_##idx, &acmphs_renesas_ra_config_##idx, \ + POST_KERNEL, CONFIG_COMPARATOR_INIT_PRIORITY, \ + &acmphs_renesas_ra_api) + +DT_INST_FOREACH_STATUS_OKAY(ACMPHS_RENESAS_RA_INIT); diff --git a/dts/bindings/comparator/renesas,ra-acmphs-global.yaml b/dts/bindings/comparator/renesas,ra-acmphs-global.yaml new file mode 100644 index 00000000000..900d3b52597 --- /dev/null +++ b/dts/bindings/comparator/renesas,ra-acmphs-global.yaml @@ -0,0 +1,49 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: | + Renesas RA ACMPHS (High-Speed Analog COMParator) Global + + The following example displays the minimum node layout: + + acmphs_global: acmphs_global@deadbeef { + compatible = "renesas,ra-acmphs-global"; + reg = <0xdeadbeef 0x200>; + status = "disabled"; + + acmphs0: acmphs0 { + ... + }; + }; + + If the pin-output-enable is selected in the comparator controller node, + this global node needs to define the VCOUT pin to enable output on that pin. + + Note: The VCOUT pinctrl for the comparator global node is already defined + on each board that supported comparator. Please check the board’s pinctrl + before defining it. + + acmphs_vcout: acmphs_vcout { + group1 { + /* VCOUT */ + psels = ; + }; + }; + + &acmphs_global { + pinctrl-0 = <&acmphs_vcout>; + pinctrl-names = "default"; + status = "okay"; + + acmphs0 { + ... + }; + }; + + Note: There is only one VCOUT shared across all comparator controller nodes. + Therefore, the VCOUT value is the logical OR of the output signals from all + comparator controllers that have pin-output-enable enabled. + +compatible: "renesas,ra-acmphs-global" + +include: [base.yaml, pinctrl-device.yaml] diff --git a/dts/bindings/comparator/renesas,ra-acmphs.yaml b/dts/bindings/comparator/renesas,ra-acmphs.yaml new file mode 100644 index 00000000000..252d161bdec --- /dev/null +++ b/dts/bindings/comparator/renesas,ra-acmphs.yaml @@ -0,0 +1,119 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: | + Renesas RA ACMPHS (High-Speed Analog COMParator) Controller + + The following example displays the minimum node layout: + + acmphs_global: acmphs_global@deadbeef { + ... + + acmphs0: acmphs0 { + compatible = "renesas,ra-acmphs"; + channel = <0>; + status = "disabled"; + }; + }; + + Enabling the comparator controller node requires setting the minimum + default configuration of the comparator. This includes selecting the + positive and negative inputs, and routing them using pinctrl: + + Note: A set of pinctrl for the default comparator controller node is + already defined on each board that supported comparator. Please check + the board’s pinctrl before defining it. + + &pinctrl { + acmphs_ivref0: acmphs_ivref0 { + group1 { + /* IVREF0 */ + psels = ; + renesas,analog-enable; + }; + }; + + acmphs0_ivcmp0: acmphs0_ivcmp0 { + group1 { + /* CH0 IVCMP0 */ + psels = ; + renesas,analog-enable; + }; + }; + }; + + &acmphs_global { + status = "okay"; + + acmphs0 { + pinctrl-0 = <&acmphs_ivref0 &acmphs0_ivcmp0>; + pinctrl-names = "default"; + interrupts = <90 12>; + interrupt-names = "hs"; + reference-input-source = "ivref0"; + compare-input-source = "ivcmp0"; + status = "okay"; + }; + }; + +compatible: "renesas,ra-acmphs" + +include: [base.yaml, pinctrl-device.yaml] + +properties: + channel: + type: int + required: true + + interrupts: + description: | + IRQ number and priority to use for interrupt Comparator: + - "hs": generated on valid edge detection from comparison result + Note: when this interrupts property is not defined, none of the trigger APIs + can be used (set_trigger, set_trigger_callback, trigger_is_pending). + + reference-input-source: + type: string + required: true + enum: + - "ivref0" + - "ivref1" + - "ivref2" + - "ivref3" + description: | + Select the Analog reference voltage source. + Note: ivref2 is connected to Vref + ivref3 is connected to DA0 (DAC) + If either of these is selected, there is no need to configure pinsel for pinctrl. + + compare-input-source: + type: string + required: true + enum: + - "ivcmp0" + - "ivcmp1" + - "ivcmp2" + - "ivcmp3" + description: | + Select the Analog compare voltage source. + Note: ivcmp1 is connected to DA0 (DAC) + If ivcmp1 is selected, there is no need to configure pinsel for pinctrl. + + noise-filter: + type: int + enum: [1, 8, 16, 32] + default: 32 + description: | + Select the PCLK divisor for the hardware digital debounce filter. + Larger divisors provide a longer debounce and take longer for the output to update. + + output-invert-polarity: + type: boolean + description: | + When enabled comparator output is inverted. + + pin-output-enable: + type: boolean + description: | + Turn this on to enable the CMPOUTn signal for this channel. + The CMPOUTn signal for each channel is OR'd together and the result is output to VCOUT. diff --git a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h index 42b095a3d07..cbb3dac6ffd 100644 --- a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h +++ b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h @@ -16,6 +16,7 @@ #define RA_PSEL_HIZ_JTAG_SWD 0x0 #define RA_PSEL_ADC 0x0 #define RA_PSEL_DAC 0x0 +#define RA_PSEL_ACMPHS 0x0 #define RA_PSEL_AGT 0x1 #define RA_PSEL_GPT0 0x2 #define RA_PSEL_GPT1 0x3 @@ -32,6 +33,7 @@ #define RA_PSEL_SPI 0x6 #define RA_PSEL_I2C 0x7 #define RA_PSEL_CLKOUT_RTC 0x9 +#define RA_PSEL_ACMPHS_VCOUT 0x9 #define RA_PSEL_CAC_ADC 0xa #define RA_PSEL_CAC_DAC 0xa #define RA_PSEL_BUS 0xb diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index 1fc6eedb663..7bc5bbc006e 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -154,6 +154,11 @@ config USE_RA_FSP_DAC help Enable RA FSP DAC driver +config USE_RA_FSP_ACMPHS + bool + help + Enable RA FSP ACMPHS driver + endif # HAS_RENESAS_RA_FSP if HAS_RENESAS_RZ_FSP