diff --git a/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi b/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi index 0d7dbaf56de..8fc5744e9c6 100644 --- a/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi +++ b/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi @@ -35,4 +35,12 @@ drive-strength = "medium"; }; }; + + adc0_default: adc0_default { + group1 { + /* input */ + psels = ; + renesas,analog-enable; + }; + }; }; diff --git a/boards/renesas/ek_ra8m1/ek_ra8m1.dts b/boards/renesas/ek_ra8m1/ek_ra8m1.dts index 76d1b06616e..193b706612f 100644 --- a/boards/renesas/ek_ra8m1/ek_ra8m1.dts +++ b/boards/renesas/ek_ra8m1/ek_ra8m1.dts @@ -7,7 +7,7 @@ #include #include - +#include #include "ek_ra8m1-pinctrl.dtsi" / { @@ -152,3 +152,9 @@ mikrobus_serial: &uart3 {}; pinctrl-0 = <&iic1_default>; pinctrl-names = "default"; }; + +&adc0 { + status = "okay"; + pinctrl-0 = <&adc0_default>; + pinctrl-names = "default"; +}; diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 70138d82e99..f5750eeabf2 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -52,3 +52,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_NUMAKER adc_numaker.c) zephyr_library_sources_ifdef(CONFIG_ADC_ENE_KB1200 adc_ene_kb1200.c) zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_GAU adc_mcux_gau_adc.c) zephyr_library_sources_ifdef(CONFIG_ADC_AMBIQ adc_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_ADC_RENESAS_RA adc_renesas_ra.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 8e0149b7952..83f033c192c 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -130,4 +130,6 @@ source "drivers/adc/Kconfig.ene" source "drivers/adc/Kconfig.ambiq" +source "drivers/adc/Kconfig.renesas_ra" + endif # ADC diff --git a/drivers/adc/Kconfig.renesas_ra b/drivers/adc/Kconfig.renesas_ra new file mode 100644 index 00000000000..1027788f9c6 --- /dev/null +++ b/drivers/adc/Kconfig.renesas_ra @@ -0,0 +1,12 @@ +# Renesas RA Family + +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config ADC_RENESAS_RA + bool "Renesas RA ADC" + default y + depends on DT_HAS_RENESAS_RA_ADC_ENABLED + select USE_RA_FSP_ADC + help + Enable Renesas RA ADC Driver. diff --git a/drivers/adc/adc_renesas_ra.c b/drivers/adc/adc_renesas_ra.c new file mode 100644 index 00000000000..0174aaa20ad --- /dev/null +++ b/drivers/adc/adc_renesas_ra.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_adc + +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(adc_ra, CONFIG_ADC_LOG_LEVEL); + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +#define ADC_RA_MAX_RESOLUTION 12 + +void adc_scan_end_isr(void); + +/** + * @brief RA ADC config + * + * This structure contains constant data for given instance of RA ADC. + */ +struct adc_ra_config { + /** Number of supported channels */ + uint8_t num_channels; + /** pinctrl configs */ + const struct pinctrl_dev_config *pcfg; + /** function pointer to irq setup */ + void (*irq_configure)(void); +}; + +/** + * @brief RA ADC data + * + * This structure contains data structures used by a RA ADC. + */ +struct adc_ra_data { + /** Structure that handle state of ongoing read operation */ + struct adc_context ctx; + /** Pointer to RA ADC own device structure */ + const struct device *dev; + /** Structure that handle fsp ADC */ + adc_instance_ctrl_t adc; + /** Structure that handle fsp ADC config */ + struct st_adc_cfg f_config; + /** Structure that handle fsp ADC channel config */ + adc_channel_cfg_t f_channel_cfg; + /** Pointer to memory where next sample will be written */ + uint16_t *buf; + /** Mask with channels that will be sampled */ + uint32_t channels; + /** Buffer id */ + uint16_t buf_id; +}; + +/** + * @brief Setup channels before starting to scan ADC + * + * @param dev RA ADC device + * @param channel_cfg channel configuration + * + * @return 0 on success + * @return -ENOTSUP if channel id or differential is wrong value + * @return -EINVAL if channel configuration is invalid + */ +static int adc_ra_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) +{ + fsp_err_t fsp_err = FSP_SUCCESS; + struct adc_ra_data *data = dev->data; + + if (!((channel_cfg->channel_id >= 0 && channel_cfg->channel_id <= 2) || + (channel_cfg->channel_id >= 4 && channel_cfg->channel_id <= 8) || + (channel_cfg->channel_id >= 16 && channel_cfg->channel_id <= 19))) { + LOG_ERR("unsupported channel id '%d'", channel_cfg->channel_id); + return -ENOTSUP; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Acquisition time is not valid"); + return -EINVAL; + } + + if (channel_cfg->differential) { + LOG_ERR("unsupported differential mode"); + return -ENOTSUP; + } + + if (channel_cfg->gain != ADC_GAIN_1) { + LOG_ERR("Gain is not valid"); + return -EINVAL; + } + + data->f_channel_cfg.scan_mask |= (1U << channel_cfg->channel_id); + /* Configure ADC channel specific settings */ + fsp_err = R_ADC_ScanCfg(&data->adc, &data->f_channel_cfg); + if (FSP_SUCCESS != fsp_err) { + return -ENOTSUP; + } + + return 0; +} + +/** + * Interrupt handler + */ +static void adc_ra_isr(const struct device *dev) +{ + struct adc_ra_data *data = dev->data; + fsp_err_t fsp_err = FSP_SUCCESS; + adc_channel_t channel_id = 0; + uint32_t channels = 0; + int16_t *sample_buffer = (int16_t *)data->buf; + + channels = data->channels; + for (channel_id = 0; channels > 0; channel_id++) { + /* Check if it is right channel id */ + if ((channels & 0x01) != 0) { + fsp_err = R_ADC_Read(&data->adc, channel_id, &sample_buffer[data->buf_id]); + if (FSP_SUCCESS != fsp_err) { + break; + } + data->buf_id = data->buf_id + 1; + } + + channels = channels >> 1; + } + adc_scan_end_isr(); + adc_context_on_sampling_done(&data->ctx, dev); +} + +/** + * @brief Check if buffer in @p sequence is big enough to hold all ADC samples + * + * @param dev RA ADC device + * @param sequence ADC sequence description + * + * @return 0 on success + * @return -ENOMEM if buffer is not big enough + */ +static int adc_ra_check_buffer_size(const struct device *dev, const struct adc_sequence *sequence) +{ + const struct adc_ra_config *config = dev->config; + uint8_t channels = 0; + size_t needed; + uint32_t mask; + + for (mask = BIT(config->num_channels - 1); mask != 0; mask >>= 1) { + if (mask & sequence->channels) { + channels++; + } + } + + needed = channels * sizeof(uint16_t); + if (sequence->options) { + needed *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed) { + return -ENOMEM; + } + + return 0; +} + +/** + * @brief Start processing read request + * + * @param dev RA ADC device + * @param sequence ADC sequence description + * + * @return 0 on success + * @return -ENOTSUP if requested resolution or channel is out side of supported + * range + * @return -ENOMEM if buffer is not big enough + * (see @ref adc_ra_check_buffer_size) + * @return other error code returned by adc_context_wait_for_completion + */ +static int adc_ra_start_read(const struct device *dev, const struct adc_sequence *sequence) +{ + const struct adc_ra_config *config = dev->config; + struct adc_ra_data *data = dev->data; + int err; + + if (sequence->resolution > ADC_RA_MAX_RESOLUTION || sequence->resolution == 0) { + LOG_ERR("unsupported resolution %d", sequence->resolution); + return -ENOTSUP; + } + + if (find_msb_set(sequence->channels) > config->num_channels) { + LOG_ERR("unsupported channels in mask: 0x%08x", sequence->channels); + return -ENOTSUP; + } + + err = adc_ra_check_buffer_size(dev, sequence); + if (err) { + LOG_ERR("buffer size too small"); + return err; + } + + data->buf_id = 0; + data->buf = sequence->buffer; + adc_context_start_read(&data->ctx, sequence); + + adc_context_wait_for_completion(&data->ctx); + + return 0; +} + +/** + * @brief Start processing read request asynchronously + * + * @param dev RA ADC device + * @param sequence ADC sequence description + * @param async async pointer to asynchronous signal + * + * @return 0 on success + * @return -ENOTSUP if requested resolution or channel is out side of supported + * range + * @return -ENOMEM if buffer is not big enough + * (see @ref adc_ra_check_buffer_size) + * @return other error code returned by adc_context_wait_for_completion + */ +static int adc_ra_read_async(const struct device *dev, const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + struct adc_ra_data *data = dev->data; + int err; + + adc_context_lock(&data->ctx, async ? true : false, async); + err = adc_ra_start_read(dev, sequence); + adc_context_release(&data->ctx, err); + + return err; +} + +/** + * @brief Start processing read request synchronously + * + * @param dev RA ADC device + * @param sequence ADC sequence description + * + * @return 0 on success + * @return -ENOTSUP if requested resolution or channel is out side of supported + * range + * @return -ENOMEM if buffer is not big enough + * (see @ref adc_ra_check_buffer_size) + * @return other error code returned by adc_context_wait_for_completion + */ +static int adc_ra_read(const struct device *dev, const struct adc_sequence *sequence) +{ + return adc_ra_read_async(dev, sequence, NULL); +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_ra_data *data = CONTAINER_OF(ctx, struct adc_ra_data, ctx); + + data->channels = ctx->sequence.channels; + + R_ADC_ScanStart(&data->adc); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) +{ + struct adc_ra_data *data = CONTAINER_OF(ctx, struct adc_ra_data, ctx); + + if (repeat_sampling) { + data->buf_id = 0; + } +} + +/** + * @brief Function called on init for each RA ADC device. It setups all + * channels to return constant 0 mV and create acquisition thread. + * + * @param dev RA ADC device + * + * @return -EIO if error + * + * @return 0 on success + */ +static int adc_ra_init(const struct device *dev) +{ + const struct adc_ra_config *config = dev->config; + struct adc_ra_data *data = dev->data; + int ret; + fsp_err_t fsp_err = FSP_SUCCESS; + + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + + /* Open ADC module */ + fsp_err = R_ADC_Open(&data->adc, &data->f_config); + if (FSP_SUCCESS != fsp_err) { + return -EIO; + } + + config->irq_configure(); + + adc_context_unlock_unconditionally(&data->ctx); + return 0; +} + +const adc_extended_cfg_t g_adc_cfg_extend = { + .add_average_count = ADC_ADD_OFF, + .clearing = ADC_CLEAR_AFTER_READ_ON, + .trigger_group_b = ADC_START_SOURCE_DISABLED, + .double_trigger_mode = ADC_DOUBLE_TRIGGER_DISABLED, + .adc_vref_control = ADC_VREF_CONTROL_VREFH, + .enable_adbuf = 0, + .window_a_irq = FSP_INVALID_VECTOR, + .window_a_ipl = (1), + .window_b_irq = FSP_INVALID_VECTOR, + .window_b_ipl = (BSP_IRQ_DISABLED), + .trigger = ADC_START_SOURCE_DISABLED, /* Use Software trigger */ +}; + +#define IRQ_CONFIGURE_FUNC(idx) \ + static void adc_ra_configure_func_##idx(void) \ + { \ + R_ICU->IELSR[DT_INST_IRQ_BY_NAME(idx, scanend, irq)] = \ + ELC_EVENT_ADC##idx##_SCAN_END; \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, scanend, irq), \ + DT_INST_IRQ_BY_NAME(idx, scanend, priority), adc_ra_isr, \ + DEVICE_DT_INST_GET(idx), 0); \ + irq_enable(DT_INST_IRQ_BY_NAME(idx, scanend, irq)); \ + } + +#define IRQ_CONFIGURE_DEFINE(idx) .irq_configure = adc_ra_configure_func_##idx + +#define ADC_RA_INIT(idx) \ + IRQ_CONFIGURE_FUNC(idx) \ + PINCTRL_DT_INST_DEFINE(idx); \ + static struct adc_driver_api adc_ra_api_##idx = { \ + .channel_setup = adc_ra_channel_setup, \ + .read = adc_ra_read, \ + .ref_internal = DT_INST_PROP(idx, vref_mv), \ + IF_ENABLED(CONFIG_ADC_ASYNC, (.read_async = adc_ra_read_async))}; \ + static const struct adc_ra_config adc_ra_config_##idx = { \ + .num_channels = DT_INST_PROP(idx, channels_num), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ + IRQ_CONFIGURE_DEFINE(idx), \ + }; \ + static struct adc_ra_data adc_ra_data_##idx = { \ + ADC_CONTEXT_INIT_TIMER(adc_ra_data_##idx, ctx), \ + ADC_CONTEXT_INIT_LOCK(adc_ra_data_##idx, ctx), \ + ADC_CONTEXT_INIT_SYNC(adc_ra_data_##idx, ctx), \ + .dev = DEVICE_DT_INST_GET(idx), \ + .f_config = \ + { \ + .unit = idx, \ + .mode = ADC_MODE_SINGLE_SCAN, \ + .resolution = ADC_RESOLUTION_12_BIT, \ + .alignment = (adc_alignment_t)ADC_ALIGNMENT_RIGHT, \ + .trigger = 0, \ + .p_callback = NULL, \ + .p_context = NULL, \ + .p_extend = &g_adc_cfg_extend, \ + .scan_end_irq = DT_INST_IRQ_BY_NAME(idx, scanend, irq), \ + .scan_end_ipl = DT_INST_IRQ_BY_NAME(idx, scanend, priority), \ + .scan_end_b_irq = FSP_INVALID_VECTOR, \ + .scan_end_b_ipl = (BSP_IRQ_DISABLED), \ + }, \ + .f_channel_cfg = \ + { \ + .scan_mask = 0, \ + .scan_mask_group_b = 0, \ + .priority_group_a = ADC_GROUP_A_PRIORITY_OFF, \ + .add_mask = 0, \ + .sample_hold_mask = 0, \ + .sample_hold_states = 24, \ + .p_window_cfg = NULL, \ + }, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, adc_ra_init, NULL, &adc_ra_data_##idx, &adc_ra_config_##idx, \ + POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, &adc_ra_api_##idx) + +DT_INST_FOREACH_STATUS_OKAY(ADC_RA_INIT); diff --git a/dts/arm/renesas/ra/ra8/ra8x1.dtsi b/dts/arm/renesas/ra/ra8/ra8x1.dtsi index 3ae29ee7ab5..6285efac823 100644 --- a/dts/arm/renesas/ra/ra8/ra8x1.dtsi +++ b/dts/arm/renesas/ra/ra8/ra8x1.dtsi @@ -277,6 +277,28 @@ #size-cells = <1>; }; + adc0: adc@40332000 { + compatible = "renesas,ra-adc"; + interrupts = <38 1>; + interrupt-names = "scanend"; + reg = <0x40332000 0x100>; + #io-channel-cells = <1>; + vref-mv = <3300>; + channels-num = <12>; + status = "disabled"; + }; + + adc1: adc@40332200 { + compatible = "renesas,ra-adc"; + interrupts = <39 1>; + interrupt-names = "scanend"; + reg = <0x40332200 0x100>; + #io-channel-cells = <1>; + vref-mv = <3300>; + channels-num = <13>; + status = "disabled"; + }; + option_setting_ofs: option_setting_ofs@300a100 { compatible = "zephyr,memory-region"; reg = <0x0300a100 0x18>; diff --git a/dts/bindings/adc/renesas,ra-adc.yaml b/dts/bindings/adc/renesas,ra-adc.yaml new file mode 100644 index 00000000000..f760a6ab695 --- /dev/null +++ b/dts/bindings/adc/renesas,ra-adc.yaml @@ -0,0 +1,28 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA ADC node + +compatible: "renesas,ra-adc" + +include: [adc-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + vref-mv: + type: int + required: true + description: ADC reference voltage (Unit:mV) + + channels-num: + type: int + required: true + description: ADC channels number + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input diff --git a/dts/bindings/pinctrl/renesas,ra-pincrl-pfs.yaml b/dts/bindings/pinctrl/renesas,ra-pincrl-pfs.yaml index c3782162b84..2cbd6a6eadc 100644 --- a/dts/bindings/pinctrl/renesas,ra-pincrl-pfs.yaml +++ b/dts/bindings/pinctrl/renesas,ra-pincrl-pfs.yaml @@ -103,3 +103,6 @@ child-binding: description: | The drive strength of a pin. The default value is low, as this is the power on reset value. + renesas,analog-enable: + type: boolean + description: enable analog input diff --git a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h index a977c6ee4cb..5d88ea6eea8 100644 --- a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h +++ b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h @@ -42,6 +42,7 @@ #define RA_PSEL_ETH_RMII 0x17 #define RA_PSEL_GLCDC 0x19 #define RA_PSEL_OSPI 0x1c +#define RA_PSEL_ADC 0x00 #define RA_PSEL_POS 8 #define RA_PSEL_MASK 0x1f diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index c5f9b26bfe4..ed7f2acf478 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -27,3 +27,8 @@ config USE_RA_FSP_SCI_UART bool help Enable RA FSP SCI UART driver + +config USE_RA_FSP_ADC + bool + help + Enable RA FSP ADC driver diff --git a/soc/renesas/ra/common_fsp/pinctrl_soc.h b/soc/renesas/ra/common_fsp/pinctrl_soc.h index 1b347f2bd23..0df9ec39111 100644 --- a/soc/renesas/ra/common_fsp/pinctrl_soc.h +++ b/soc/renesas/ra/common_fsp/pinctrl_soc.h @@ -39,6 +39,7 @@ typedef struct ra_pinctrl_soc_pin pinctrl_soc_pin_t; .pin_num = RA_GET_PIN_NUM(DT_PROP_BY_IDX(node_id, prop, idx)), \ .cfg = (DT_PROP(node_id, bias_pull_up) << 4) | \ (DT_PROP(node_id, drive_open_drain) << 6) | \ + (DT_PROP(node_id, renesas_analog_enable) << 15) | \ (DT_ENUM_IDX(node_id, drive_strength) << 10) | \ (RA_GET_MODE(DT_PROP_BY_IDX(node_id, prop, idx)) << 16) | \ (RA_GET_PSEL(DT_PROP_BY_IDX(node_id, prop, idx)) << 24), \ diff --git a/tests/drivers/adc/adc_accuracy_test/boards/ek_ra8m1.overlay b/tests/drivers/adc/adc_accuracy_test/boards/ek_ra8m1.overlay new file mode 100644 index 00000000000..e3aced0a41b --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/ek_ra8m1.overlay @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + zephyr,user { + io-channels = <&adc0 0>; + reference_mv = <3300>; + }; +}; + +&adc0{ + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/testcase.yaml b/tests/drivers/adc/adc_accuracy_test/testcase.yaml index 07bf2e50119..e33f7237339 100644 --- a/tests/drivers/adc/adc_accuracy_test/testcase.yaml +++ b/tests/drivers/adc/adc_accuracy_test/testcase.yaml @@ -12,8 +12,10 @@ tests: fixture: dac_adc_loopback platform_allow: - frdm_k64f + - ek_ra8m1 drivers.adc.accuracy.ref_volt: harness_config: fixture: adc_ref_volt platform_allow: - frdm_kl25z + - ek_ra8m1 diff --git a/tests/drivers/adc/adc_api/boards/ek_ra8m1.overlay b/tests/drivers/adc/adc_api/boards/ek_ra8m1.overlay new file mode 100644 index 00000000000..07924ac5c84 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/ek_ra8m1.overlay @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/ { + zephyr,user { + io-channels = <&adc1 0>, <&adc1 2>; + }; +}; + +&pinctrl { + adc1_default: adc1_default { + group1 { + /* input */ + psels = ; + renesas,analog-enable; + }; + }; +}; + +&adc1 { + pinctrl-0 = <&adc1_default>; + pinctrl-names = "default"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <3300>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <3300>; + }; +};