From d00d6d82dff50992b8be9f401fa7d16da34aebf0 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Tue, 24 Jun 2025 11:27:58 +0200 Subject: [PATCH 1/8] [nrf fromtree] manifest: Update hal_nordic with updated cracen hal and rng driver Update with the latest nordic hal which includes an updated CRACEN driver which supports the 54LM20 and newer devices Signed-off-by: Alberto Escolar Piedras (cherry picked from commit ce0d65a35c0e0fdd89c7b697708615e09c04c70c) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 56b80391830..2a059662446 100644 --- a/west.yml +++ b/west.yml @@ -200,7 +200,7 @@ manifest: groups: - hal - name: hal_nordic - revision: 71308dc6d8c021887ce5d98a36cafe9517375a91 + revision: 9587b1dcb83d24ab74e89837843a5f7d573f7059 path: modules/hal/nordic groups: - hal From 7d99a734eb6008608d6c8f48d0c7a8e9e3494021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Mon, 16 Jun 2025 07:53:43 +0200 Subject: [PATCH 2/8] [nrf fromtree] drivers: adc_nrfx_saadc: Remove dead and misleading code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a long time (since version 3.3.0) nrfx contained an incorrectly defined symbol NRF_SAADC_8BIT_SAMPLE_WIDTH that was set to 8 for nRF54L and nRF54H Series SoCs, which was probably only true for very early engineering revisions of those. Based on this, the adc_nrfx_saadc driver was incorrectly writing consecutive 8-bit samples in supplied buffers, cutting off the highest 8 bits of the results. And for sequences with multiple channels, it was even causing that the results written as 16-bit words by hardware were partially overwritten in next iteration. In nrfx 3.12.0 (see commit f46798fa55881e6e24bb121d867c7753d4c6b775) this was finally corrected - the symbol is now deprecated and it is always set to 16. This commit is a follow-up to the above and removes parts of adc_nrfx_saadc that now became dead code to prevent further confusion regarding 8-bit sampling. Signed-off-by: Andrzej Głąbek (cherry picked from commit 616ec7522ccee368b1f6c5c1d27ad54a999279fe) --- drivers/adc/adc_nrfx_saadc.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index 986cca49af8..54719a1136d 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -141,12 +141,8 @@ static struct driver_data m_data = { }; /* Helper function to convert number of samples to the byte representation. */ -static uint32_t samples_to_bytes(const struct adc_sequence *sequence, uint16_t number_of_samples) +static uint32_t samples_to_bytes(uint16_t number_of_samples) { - if (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8 && sequence->resolution == 8) { - return number_of_samples; - } - return number_of_samples * 2; } @@ -410,11 +406,11 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx, if (!repeat) { #if defined(ADC_BUFFER_IN_RAM) m_data.user_buffer = (uint8_t *)m_data.user_buffer + - samples_to_bytes(&ctx->sequence, nrfy_saadc_amount_get(NRF_SAADC)); + samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); #else nrf_saadc_value_t *buffer = (uint8_t *)nrf_saadc_buffer_pointer_get(NRF_SAADC) + - samples_to_bytes(&ctx->sequence, nrfy_saadc_amount_get(NRF_SAADC)); + samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); nrfy_saadc_buffer_pointer_set(NRF_SAADC, buffer); #endif } @@ -501,7 +497,7 @@ static int check_buffer_size(const struct adc_sequence *sequence, { size_t needed_buffer_size; - needed_buffer_size = samples_to_bytes(sequence, active_channels); + needed_buffer_size = samples_to_bytes(active_channels); if (sequence->options) { needed_buffer_size *= (1 + sequence->options->extra_samplings); @@ -546,7 +542,6 @@ static int start_read(const struct device *dev, { int error; uint32_t selected_channels = sequence->channels; - uint8_t resolution = sequence->resolution; uint8_t active_channels; uint8_t channel_id; nrf_saadc_burst_t burst; @@ -576,22 +571,6 @@ static int start_read(const struct device *dev, channel_id); return -EINVAL; } - /* Signal an error if the channel is configured as - * single ended with a resolution which is identical - * to the sample bit size. The SAADC's "single ended" - * mode is really differential mode with the - * negative input tied to ground. We can therefore - * observe negative values if the positive input falls - * below ground. If the sample bitsize is larger than - * the resolution, we can detect negative values and - * correct them to 0 after the sequencen has ended. - */ - if ((m_data.single_ended_channels & BIT(channel_id)) && - (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8 && resolution == 8)) { - LOG_ERR("Channel %u invalid single ended resolution", - channel_id); - return -EINVAL; - } /* When oversampling is used, the burst mode needs to * be activated. Unfortunately, this mode cannot be * activated permanently in the channel setup, because @@ -713,7 +692,7 @@ static void saadc_irq_handler(const struct device *dev) #if defined(ADC_BUFFER_IN_RAM) memcpy(m_data.user_buffer, m_data.samples_buffer, - samples_to_bytes(&m_data.ctx.sequence, m_data.active_channels)); + samples_to_bytes(m_data.active_channels)); #endif adc_context_on_sampling_done(&m_data.ctx, dev); From ecc01654661202eadbf012540589102ff274f72c Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Mon, 16 Jun 2025 15:03:37 +0200 Subject: [PATCH 3/8] [nrf fromtree] modules: hal_nordic: nrfx: Update NRFX API version to 3.12 Update NRFX_CONFIG_API_VER_MINOR to 12, to be compatible with latest SAADC driver changes. Signed-off-by: Jakub Zymelka (cherry picked from commit be8c61ab809faa793609cabe2201edd38c2c77a4) --- modules/hal_nordic/nrfx/nrfx_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal_nordic/nrfx/nrfx_config.h b/modules/hal_nordic/nrfx/nrfx_config.h index 4a2d5af18b6..fac2341c1c5 100644 --- a/modules/hal_nordic/nrfx/nrfx_config.h +++ b/modules/hal_nordic/nrfx/nrfx_config.h @@ -9,7 +9,7 @@ /* Define nrfx API version used in Zephyr. */ #define NRFX_CONFIG_API_VER_MAJOR 3 -#define NRFX_CONFIG_API_VER_MINOR 8 +#define NRFX_CONFIG_API_VER_MINOR 12 #define NRFX_CONFIG_API_VER_MICRO 0 /* Macros used in zephyr-specific config files. */ From 0dee5d2716c22861b4bc900498f7b5728ca8bec2 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Tue, 27 May 2025 11:57:13 +0200 Subject: [PATCH 4/8] [nrf fromtree] drivers: adc: rework Nordic SAADC driver SHIM Nordic modification for the ADC driver controlling the SAADC peripheral. Replaced HAL based implementation in favor of nrfx driver. As a next step, it is planned to implement a feature that will allow the peripheral SAADC timer to be used for sampling, and for this it is necessary to use the nrfx driver in this SHIM. This will allow more accurate and faster sampling than the kernel mechanism currently provides. Signed-off-by: Jakub Zymelka (cherry picked from commit f3d250742377ac24af0bc4bf78dd61afaefc2479) --- drivers/adc/Kconfig.nrfx | 1 + drivers/adc/adc_nrfx_saadc.c | 518 +++++++++++++++-------------------- 2 files changed, 222 insertions(+), 297 deletions(-) diff --git a/drivers/adc/Kconfig.nrfx b/drivers/adc/Kconfig.nrfx index d411d5c99e1..0a602f5b97b 100644 --- a/drivers/adc/Kconfig.nrfx +++ b/drivers/adc/Kconfig.nrfx @@ -27,5 +27,6 @@ config ADC_NRFX_SAADC default y depends on DT_HAS_NORDIC_NRF_SAADC_ENABLED select ADC_CONFIGURABLE_INPUTS + select NRFX_SAADC help Enable support for nrfx SAADC driver. diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index 54719a1136d..f9f136f2509 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -6,18 +6,15 @@ #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" -#include +#include #include #include #include #include -#include -#include - -#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL #include #include -LOG_MODULE_REGISTER(adc_nrfx_saadc); + +LOG_MODULE_REGISTER(adc_nrfx_saadc, CONFIG_ADC_LOG_LEVEL); #define DT_DRV_COMPAT nordic_nrf_saadc @@ -103,16 +100,9 @@ BUILD_ASSERT((NRF_SAADC_AIN0 == NRF_SAADC_INPUT_AIN0) && #endif #if defined(CONFIG_NRF_PLATFORM_HALTIUM) - +#include /* Haltium devices always use bounce buffers in RAM */ - -#define SAADC_MEMORY_SECTION \ - COND_CODE_1(DT_NODE_HAS_PROP(DT_NODELABEL(adc), memory_regions), \ - (__attribute__((__section__(LINKER_DT_NODE_REGION_NAME( \ - DT_PHANDLE(DT_NODELABEL(adc), memory_regions)))))), \ - ()) - -static uint16_t adc_samples_buffer[SAADC_CH_NUM] SAADC_MEMORY_SECTION; +static uint16_t adc_samples_buffer[SAADC_CH_NUM] DMM_MEMORY_SECTION(DT_NODELABEL(adc)); #define ADC_BUFFER_IN_RAM @@ -120,14 +110,13 @@ static uint16_t adc_samples_buffer[SAADC_CH_NUM] SAADC_MEMORY_SECTION; struct driver_data { struct adc_context ctx; - - uint8_t positive_inputs[SAADC_CH_NUM]; uint8_t single_ended_channels; + nrf_saadc_value_t *buffer; /* Pointer to the buffer with converted samples. */ + uint8_t active_channel_cnt; #if defined(ADC_BUFFER_IN_RAM) void *samples_buffer; void *user_buffer; - uint8_t active_channels; #endif }; @@ -140,41 +129,37 @@ static struct driver_data m_data = { #endif }; -/* Helper function to convert number of samples to the byte representation. */ -static uint32_t samples_to_bytes(uint16_t number_of_samples) -{ - return number_of_samples * 2; -} +/* Forward declaration */ +static void event_handler(const nrfx_saadc_evt_t *event); /* Helper function to convert acquisition time to register TACQ value. */ -static int adc_convert_acq_time(uint16_t acquisition_time, nrf_saadc_acqtime_t *p_tacq_val) +static int acq_time_set(nrf_saadc_channel_config_t *ch_cfg, uint16_t acquisition_time) { - int result = 0; - #if NRF_SAADC_HAS_ACQTIME_ENUM switch (acquisition_time) { case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 3): - *p_tacq_val = NRF_SAADC_ACQTIME_3US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_3US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5): - *p_tacq_val = NRF_SAADC_ACQTIME_5US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_5US; break; case ADC_ACQ_TIME_DEFAULT: case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10): - *p_tacq_val = NRF_SAADC_ACQTIME_10US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_10US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 15): - *p_tacq_val = NRF_SAADC_ACQTIME_15US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_15US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20): - *p_tacq_val = NRF_SAADC_ACQTIME_20US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_20US; break; case ADC_ACQ_TIME_MAX: case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40): - *p_tacq_val = NRF_SAADC_ACQTIME_40US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_40US; break; default: - result = -EINVAL; + LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; } #else #define MINIMUM_ACQ_TIME_IN_NS 125 @@ -191,130 +176,138 @@ static int adc_convert_acq_time(uint16_t acquisition_time, nrf_saadc_acqtime_t * tacq = (nrf_saadc_acqtime_t)(acq_time / MINIMUM_ACQ_TIME_IN_NS) - 1; if ((tacq > NRF_SAADC_ACQTIME_MAX) || (acq_time < MINIMUM_ACQ_TIME_IN_NS)) { - result = -EINVAL; + LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; } else { - *p_tacq_val = tacq; + ch_cfg->acq_time = tacq; } #endif - return result; + LOG_DBG("ADC acquisition_time: %d", acquisition_time); + + return 0; } -static int saadc_pm_hook(const struct device *dev, enum pm_device_action action) +static int input_assign(nrf_saadc_input_t *pin_p, + nrf_saadc_input_t *pin_n, + const struct adc_channel_cfg *channel_cfg) { - ARG_UNUSED(dev); - - switch (action) { - case PM_DEVICE_ACTION_SUSPEND: - nrf_saadc_disable(NRF_SAADC); - return 0; +#if (NRF_SAADC_HAS_AIN_AS_PIN) + if (channel_cfg->input_positive > ARRAY_SIZE(saadc_psels) || + channel_cfg->input_positive < NRF_SAADC_AIN0) { + LOG_ERR("Invalid analog positive input number: %d", channel_cfg->input_positive); + return -EINVAL; + } - case PM_DEVICE_ACTION_RESUME: - nrf_saadc_enable(NRF_SAADC); - return 0; + *pin_p = saadc_psels[channel_cfg->input_positive]; - default: - break; + if (channel_cfg->differential) { + if (channel_cfg->input_negative > ARRAY_SIZE(saadc_psels) || + channel_cfg->input_negative < NRF_SAADC_AIN0 || + (IS_ENABLED(CONFIG_NRF_PLATFORM_HALTIUM) && + (channel_cfg->input_positive > NRF_SAADC_AIN7) != + (channel_cfg->input_negative > NRF_SAADC_AIN7))) { + LOG_ERR("Invalid analog negative input number: %d", + channel_cfg->input_negative); + return -EINVAL; + } + *pin_n = saadc_psels[channel_cfg->input_negative]; + } else { + *pin_n = NRF_SAADC_INPUT_DISABLED; } +#else + *pin_p = channel_cfg->input_positive; + *pin_n = channel_cfg->differential ? channel_cfg->input_negative + : NRF_SAADC_INPUT_DISABLED; +#endif + LOG_DBG("ADC positive input: %d", *pin_p); + LOG_DBG("ADC negative input: %d", *pin_n); - return -ENOTSUP; + return 0; } -/* Implementation of the ADC driver API function: adc_channel_setup. */ -static int adc_nrfx_channel_setup(const struct device *dev, - const struct adc_channel_cfg *channel_cfg) +static int gain_set(nrf_saadc_channel_config_t *ch_cfg, enum adc_gain gain) { - nrf_saadc_channel_config_t config = { -#if NRF_SAADC_HAS_CH_CONFIG_RES - .resistor_p = NRF_SAADC_RESISTOR_DISABLED, - .resistor_n = NRF_SAADC_RESISTOR_DISABLED, -#endif -#if NRF_SAADC_HAS_CH_BURST - .burst = NRF_SAADC_BURST_DISABLED, -#endif - }; - uint8_t channel_id = channel_cfg->channel_id; - uint32_t input_negative = channel_cfg->input_negative; - - if (channel_id >= SAADC_CH_NUM) { - return -EINVAL; - } - #if NRF_SAADC_HAS_CH_GAIN - switch (channel_cfg->gain) { + switch (gain) { #if defined(SAADC_CH_CONFIG_GAIN_Gain1_6) case ADC_GAIN_1_6: - config.gain = NRF_SAADC_GAIN1_6; + ch_cfg->gain = NRF_SAADC_GAIN1_6; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_5) case ADC_GAIN_1_5: - config.gain = NRF_SAADC_GAIN1_5; + ch_cfg->gain = NRF_SAADC_GAIN1_5; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_4) || defined(SAADC_CH_CONFIG_GAIN_Gain2_8) case ADC_GAIN_1_4: - config.gain = NRF_SAADC_GAIN1_4; + ch_cfg->gain = NRF_SAADC_GAIN1_4; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain2_7) case ADC_GAIN_2_7: - config.gain = NRF_SAADC_GAIN2_7; + ch_cfg->gain = NRF_SAADC_GAIN2_7; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_3) || defined(SAADC_CH_CONFIG_GAIN_Gain2_6) case ADC_GAIN_1_3: - config.gain = NRF_SAADC_GAIN1_3; + ch_cfg->gain = NRF_SAADC_GAIN1_3; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain2_5) case ADC_GAIN_2_5: - config.gain = NRF_SAADC_GAIN2_5; + ch_cfg->gain = NRF_SAADC_GAIN2_5; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_2) || defined(SAADC_CH_CONFIG_GAIN_Gain2_4) case ADC_GAIN_1_2: - config.gain = NRF_SAADC_GAIN1_2; + ch_cfg->gain = NRF_SAADC_GAIN1_2; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain2_3) case ADC_GAIN_2_3: - config.gain = NRF_SAADC_GAIN2_3; + ch_cfg->gain = NRF_SAADC_GAIN2_3; break; #endif case ADC_GAIN_1: - config.gain = NRF_SAADC_GAIN1; + ch_cfg->gain = NRF_SAADC_GAIN1; break; case ADC_GAIN_2: - config.gain = NRF_SAADC_GAIN2; + ch_cfg->gain = NRF_SAADC_GAIN2; break; #if defined(SAADC_CH_CONFIG_GAIN_Gain4) case ADC_GAIN_4: - config.gain = NRF_SAADC_GAIN4; + ch_cfg->gain = NRF_SAADC_GAIN4; break; #endif default: #else - if (channel_cfg->gain != ADC_GAIN_1) { + if (ch_cfg->gain != ADC_GAIN_1) { #endif /* defined(NRF_SAADC_HAS_CH_GAIN) */ LOG_ERR("Selected ADC gain is not valid"); return -EINVAL; } - switch (channel_cfg->reference) { + return 0; +} + +static int reference_set(nrf_saadc_channel_config_t *ch_cfg, enum adc_reference reference) +{ + switch (reference) { #if defined(SAADC_CH_CONFIG_REFSEL_Internal) case ADC_REF_INTERNAL: - config.reference = NRF_SAADC_REFERENCE_INTERNAL; + ch_cfg->reference = NRF_SAADC_REFERENCE_INTERNAL; break; #endif #if defined(SAADC_CH_CONFIG_REFSEL_VDD1_4) case ADC_REF_VDD_1_4: - config.reference = NRF_SAADC_REFERENCE_VDD4; + ch_cfg->reference = NRF_SAADC_REFERENCE_VDD4; break; #endif #if defined(SAADC_CH_CONFIG_REFSEL_External) case ADC_REF_EXTERNAL0: - config.reference = NRF_SAADC_REFERENCE_EXTERNAL; + ch_cfg->reference = NRF_SAADC_REFERENCE_EXTERNAL; break; #endif default: @@ -322,133 +315,130 @@ static int adc_nrfx_channel_setup(const struct device *dev, return -EINVAL; } - int ret = adc_convert_acq_time(channel_cfg->acquisition_time, &config.acq_time); + return 0; +} - if (ret) { - LOG_ERR("Selected ADC acquisition time is not valid"); +/* Implementation of the ADC driver API function: adc_channel_setup. */ +static int adc_nrfx_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + int err; + nrf_saadc_channel_config_t *ch_cfg; + nrfx_saadc_channel_t cfg = { + .channel_config = { +#if NRF_SAADC_HAS_CH_CONFIG_RES + .resistor_p = NRF_SAADC_RESISTOR_DISABLED, + .resistor_n = NRF_SAADC_RESISTOR_DISABLED, +#endif +#if NRF_SAADC_HAS_CH_BURST + .burst = NRF_SAADC_BURST_DISABLED, +#endif + }, + .channel_index = channel_cfg->channel_id, + }; + + if (channel_cfg->channel_id >= SAADC_CH_NUM) { + LOG_ERR("Invalid channel ID: %d", channel_cfg->channel_id); return -EINVAL; } + ch_cfg = &cfg.channel_config; + + err = input_assign(&cfg.pin_p, &cfg.pin_n, channel_cfg); + if (err != 0) { + return err; + } + + err = gain_set(ch_cfg, channel_cfg->gain); + if (err != 0) { + return err; + } + + err = reference_set(ch_cfg, channel_cfg->reference); + if (err != 0) { + return err; + } + + err = acq_time_set(ch_cfg, channel_cfg->acquisition_time); + if (err != 0) { + return err; + } + /* Store channel mode to allow correcting negative readings in single-ended mode * after ADC sequence ends. */ if (channel_cfg->differential) { - config.mode = NRF_SAADC_MODE_DIFFERENTIAL; + ch_cfg->mode = NRF_SAADC_MODE_DIFFERENTIAL; m_data.single_ended_channels &= ~BIT(channel_cfg->channel_id); } else { - config.mode = NRF_SAADC_MODE_SINGLE_ENDED; + ch_cfg->mode = NRF_SAADC_MODE_SINGLE_ENDED; m_data.single_ended_channels |= BIT(channel_cfg->channel_id); } -#if (NRF_SAADC_HAS_AIN_AS_PIN) - if ((channel_cfg->input_positive >= ARRAY_SIZE(saadc_psels)) || - (channel_cfg->input_positive < NRF_SAADC_AIN0)) { - return -EINVAL; - } - - if (config.mode == NRF_SAADC_MODE_DIFFERENTIAL) { -#if defined(CONFIG_NRF_PLATFORM_HALTIUM) - if ((input_negative > NRF_SAADC_AIN7) != - (channel_cfg->input_positive > NRF_SAADC_AIN7)) { -#else - if (input_negative > NRF_SAADC_AIN7 || - input_negative < NRF_SAADC_AIN0) { -#endif - return -EINVAL; - } + nrfx_err_t ret = nrfx_saadc_channel_config(&cfg); - input_negative = saadc_psels[input_negative]; - } else { - input_negative = NRF_SAADC_INPUT_DISABLED; + if (ret != NRFX_SUCCESS) { + LOG_ERR("Cannot configure channel %d: %d", channel_cfg->channel_id, ret); + return -EINVAL; } -#endif - /* Store the positive input selection in a dedicated array, - * to get it later when the channel is selected for a sampling - * and to mark the channel as configured (ready to be selected). - */ - m_data.positive_inputs[channel_id] = channel_cfg->input_positive; - - nrf_saadc_channel_init(NRF_SAADC, channel_id, &config); - /* Keep the channel disabled in hardware (set positive input to - * NRF_SAADC_INPUT_DISABLED) until it is selected to be included - * in a sampling sequence. - */ - nrf_saadc_channel_input_set(NRF_SAADC, - channel_id, - NRF_SAADC_INPUT_DISABLED, - input_negative); return 0; } static void adc_context_start_sampling(struct adc_context *ctx) { -#if defined(CONFIG_PM_DEVICE_RUNTIME) - pm_device_runtime_get(DEVICE_DT_INST_GET(0)); -#else - nrf_saadc_enable(NRF_SAADC); -#endif - if (ctx->sequence.calibrate) { - nrf_saadc_task_trigger(NRF_SAADC, - NRF_SAADC_TASK_CALIBRATEOFFSET); + nrfx_saadc_offset_calibrate(event_handler); } else { - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE); + nrfx_err_t ret = nrfx_saadc_mode_trigger(); + + if (ret != NRFX_SUCCESS) { + LOG_ERR("Cannot start sampling: 0x%08x", ret); + adc_context_complete(&m_data.ctx, -EIO); + } } } -static void adc_context_update_buffer_pointer(struct adc_context *ctx, - bool repeat) +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat) { - ARG_UNUSED(ctx); - if (!repeat) { #if defined(ADC_BUFFER_IN_RAM) - m_data.user_buffer = (uint8_t *)m_data.user_buffer + - samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); + m_data.user_buffer = (uint16_t *)m_data.user_buffer + m_data.active_channel_cnt; #else - nrf_saadc_value_t *buffer = - (uint8_t *)nrf_saadc_buffer_pointer_get(NRF_SAADC) + - samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); - nrfy_saadc_buffer_pointer_set(NRF_SAADC, buffer); + nrf_saadc_value_t *buffer = (uint16_t *)m_data.buffer + m_data.active_channel_cnt; + + nrfx_saadc_buffer_set(buffer, m_data.active_channel_cnt); #endif } } -static int set_resolution(const struct adc_sequence *sequence) +static int get_resolution(const struct adc_sequence *sequence, nrf_saadc_resolution_t *resolution) { - nrf_saadc_resolution_t nrf_resolution; - switch (sequence->resolution) { - case 8: - nrf_resolution = NRF_SAADC_RESOLUTION_8BIT; + case 8: + *resolution = NRF_SAADC_RESOLUTION_8BIT; break; case 10: - nrf_resolution = NRF_SAADC_RESOLUTION_10BIT; + *resolution = NRF_SAADC_RESOLUTION_10BIT; break; case 12: - nrf_resolution = NRF_SAADC_RESOLUTION_12BIT; + *resolution = NRF_SAADC_RESOLUTION_12BIT; break; case 14: - nrf_resolution = NRF_SAADC_RESOLUTION_14BIT; + *resolution = NRF_SAADC_RESOLUTION_14BIT; break; default: - LOG_ERR("ADC resolution value %d is not valid", - sequence->resolution); + LOG_ERR("ADC resolution value %d is not valid", sequence->resolution); return -EINVAL; } - nrf_saadc_resolution_set(NRF_SAADC, nrf_resolution); return 0; } -static int set_oversampling(const struct adc_sequence *sequence, - uint8_t active_channels) +static int get_oversampling(const struct adc_sequence *sequence, uint8_t active_channel_cnt, + nrf_saadc_oversample_t *oversampling) { - nrf_saadc_oversample_t nrf_oversampling; - - if ((active_channels > 1) && (sequence->oversampling > 0)) { + if ((active_channel_cnt > 1) && (sequence->oversampling > 0)) { LOG_ERR( "Oversampling is supported for single channel only"); return -EINVAL; @@ -456,48 +446,45 @@ static int set_oversampling(const struct adc_sequence *sequence, switch (sequence->oversampling) { case 0: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_DISABLED; + *oversampling = NRF_SAADC_OVERSAMPLE_DISABLED; break; case 1: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_2X; + *oversampling = NRF_SAADC_OVERSAMPLE_2X; break; case 2: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_4X; + *oversampling = NRF_SAADC_OVERSAMPLE_4X; break; case 3: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_8X; + *oversampling = NRF_SAADC_OVERSAMPLE_8X; break; case 4: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_16X; + *oversampling = NRF_SAADC_OVERSAMPLE_16X; break; case 5: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_32X; + *oversampling = NRF_SAADC_OVERSAMPLE_32X; break; case 6: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_64X; + *oversampling = NRF_SAADC_OVERSAMPLE_64X; break; case 7: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_128X; + *oversampling = NRF_SAADC_OVERSAMPLE_128X; break; case 8: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_256X; + *oversampling = NRF_SAADC_OVERSAMPLE_256X; break; default: - LOG_ERR("Oversampling value %d is not valid", - sequence->oversampling); + LOG_ERR("Oversampling value %d is not valid", sequence->oversampling); return -EINVAL; } - nrf_saadc_oversample_set(NRF_SAADC, nrf_oversampling); return 0; } -static int check_buffer_size(const struct adc_sequence *sequence, - uint8_t active_channels) +static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active_channel_cnt) { size_t needed_buffer_size; - needed_buffer_size = samples_to_bytes(active_channels); + needed_buffer_size = NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt); if (sequence->options) { needed_buffer_size *= (1 + sequence->options->extra_samplings); @@ -505,7 +492,7 @@ static int check_buffer_size(const struct adc_sequence *sequence, if (sequence->buffer_size < needed_buffer_size) { LOG_ERR("Provided buffer is too small (%u/%u)", - sequence->buffer_size, needed_buffer_size); + sequence->buffer_size, needed_buffer_size); return -ENOMEM; } @@ -522,7 +509,7 @@ static void correct_single_ended(const struct adc_sequence *sequence) uint16_t channel_bit = BIT(0); uint8_t selected_channels = sequence->channels; uint8_t single_ended_channels = m_data.single_ended_channels; - int16_t *sample = nrf_saadc_buffer_pointer_get(NRF_SAADC); + int16_t *sample = (int16_t *)m_data.buffer; while (channel_bit <= single_ended_channels) { if (channel_bit & selected_channels) { @@ -540,11 +527,13 @@ static void correct_single_ended(const struct adc_sequence *sequence) static int start_read(const struct device *dev, const struct adc_sequence *sequence) { + nrfx_err_t nrfx_err; int error; uint32_t selected_channels = sequence->channels; - uint8_t active_channels; - uint8_t channel_id; - nrf_saadc_burst_t burst; + nrf_saadc_resolution_t resolution; + nrf_saadc_oversample_t oversampling; + uint8_t active_channel_cnt = 0U; + uint8_t channel_id = 0U; /* Signal an error if channel selection is invalid (no channels or * a non-existing one is selected). @@ -555,88 +544,55 @@ static int start_read(const struct device *dev, return -EINVAL; } - active_channels = 0U; - - /* Enable only the channels selected for the pointed sequence. - * Disable all the rest. - */ - channel_id = 0U; do { if (selected_channels & BIT(channel_id)) { - /* Signal an error if a selected channel has not been - * configured yet. - */ - if (m_data.positive_inputs[channel_id] == 0U) { - LOG_ERR("Channel %u not configured", - channel_id); + /* Signal an error if a selected channel has not been configured yet. */ + if (0 == (nrfx_saadc_channels_configured_get() & BIT(channel_id))) { + LOG_ERR("Channel %u not configured", channel_id); return -EINVAL; } - /* When oversampling is used, the burst mode needs to - * be activated. Unfortunately, this mode cannot be - * activated permanently in the channel setup, because - * then the multiple channel sampling fails (the END - * event is not generated) after switching to a single - * channel sampling and back. Thus, when oversampling - * is not used (hence, the multiple channel sampling is - * possible), the burst mode have to be deactivated. - */ - burst = (sequence->oversampling != 0U ? - NRF_SAADC_BURST_ENABLED : NRF_SAADC_BURST_DISABLED); -#if NRF_SAADC_HAS_CH_BURST - nrf_saadc_channel_burst_set(NRF_SAADC, channel_id, burst); -#else - nrf_saadc_burst_set(NRF_SAADC, burst); -#endif - nrf_saadc_channel_pos_input_set( - NRF_SAADC, - channel_id, -#if NRF_SAADC_HAS_AIN_AS_PIN - saadc_psels[m_data.positive_inputs[channel_id]] -#else - m_data.positive_inputs[channel_id] -#endif - ); - ++active_channels; - } else { - burst = NRF_SAADC_BURST_DISABLED; -#if NRF_SAADC_HAS_CH_BURST - nrf_saadc_channel_burst_set(NRF_SAADC, channel_id, burst); -#else - nrf_saadc_burst_set(NRF_SAADC, burst); -#endif - nrf_saadc_channel_pos_input_set( - NRF_SAADC, - channel_id, - NRF_SAADC_INPUT_DISABLED); + ++active_channel_cnt; } } while (++channel_id < SAADC_CH_NUM); - error = set_resolution(sequence); + if (active_channel_cnt == 0) { + LOG_ERR("No channel configured"); + return -EINVAL; + } + + error = get_resolution(sequence, &resolution); if (error) { return error; } - error = set_oversampling(sequence, active_channels); + error = get_oversampling(sequence, active_channel_cnt, &oversampling); if (error) { return error; } - error = check_buffer_size(sequence, active_channels); + nrfx_err = nrfx_saadc_simple_mode_set(selected_channels, + resolution, + oversampling, + event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EINVAL; + } + + error = check_buffer_size(sequence, active_channel_cnt); if (error) { return error; } + m_data.active_channel_cnt = active_channel_cnt; #if defined(ADC_BUFFER_IN_RAM) m_data.user_buffer = sequence->buffer; - m_data.active_channels = active_channels; - nrf_saadc_buffer_init(NRF_SAADC, - (nrf_saadc_value_t *)m_data.samples_buffer, - active_channels); + nrfx_saadc_buffer_set(m_data.samples_buffer, active_channel_cnt); #else - nrf_saadc_buffer_init(NRF_SAADC, - (nrf_saadc_value_t *)sequence->buffer, - active_channels); + /* Buffer is filled in chunks, each chunk composed of number of samples equal to number + * of active channels. Buffer pointer is advanced and reloaded after each chunk. + */ + nrfx_saadc_buffer_set(sequence->buffer, active_channel_cnt); #endif adc_context_start_read(&m_data.ctx, sequence); @@ -673,18 +629,12 @@ static int adc_nrfx_read_async(const struct device *dev, } #endif /* CONFIG_ADC_ASYNC */ -static void saadc_irq_handler(const struct device *dev) +static void event_handler(const nrfx_saadc_evt_t *event) { - if (nrf_saadc_event_check(NRF_SAADC, NRF_SAADC_EVENT_END)) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); + nrfx_err_t err; - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP); - -#if defined(CONFIG_PM_DEVICE_RUNTIME) - pm_device_runtime_put(DEVICE_DT_INST_GET(0)); -#else - nrf_saadc_disable(NRF_SAADC); -#endif + if (event->type == NRFX_SAADC_EVT_DONE) { + m_data.buffer = event->data.done.p_buffer; if (has_single_ended(&m_data.ctx.sequence)) { correct_single_ended(&m_data.ctx.sequence); @@ -692,39 +642,35 @@ static void saadc_irq_handler(const struct device *dev) #if defined(ADC_BUFFER_IN_RAM) memcpy(m_data.user_buffer, m_data.samples_buffer, - samples_to_bytes(m_data.active_channels)); + NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt)); #endif - adc_context_on_sampling_done(&m_data.ctx, dev); - } else if (nrf_saadc_event_check(NRF_SAADC, - NRF_SAADC_EVENT_CALIBRATEDONE)) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE); - - /* - * The workaround for Nordic nRF52832 anomalies 86 and - * 178 is an explicit STOP after CALIBRATEOFFSET - * before issuing START. - */ - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE); + adc_context_on_sampling_done(&m_data.ctx, DEVICE_DT_INST_GET(0)); + } else if (event->type == NRFX_SAADC_EVT_CALIBRATEDONE) { + err = nrfx_saadc_mode_trigger(); + if (err != NRFX_SUCCESS) { + LOG_ERR("Cannot start sampling: 0x%08x", err); + adc_context_complete(&m_data.ctx, -EIO); + } } } static int init_saadc(const struct device *dev) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE); - nrf_saadc_int_enable(NRF_SAADC, - NRF_SAADC_INT_END | NRF_SAADC_INT_CALIBRATEDONE); - NRFX_IRQ_ENABLE(DT_INST_IRQN(0)); + nrfx_err_t err; + + /* The priority value passed here is ignored (see nrfx_glue.h). */ + err = nrfx_saadc_init(0); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize device: %s", dev->name); + return -EIO; + } - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), - saadc_irq_handler, DEVICE_DT_INST_GET(0), 0); + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), nrfx_isr, nrfx_saadc_irq_handler, 0); adc_context_unlock_unconditionally(&m_data.ctx); - return pm_device_driver_init(dev, saadc_pm_hook); + return 0; } static DEVICE_API(adc, adc_nrfx_driver_api) = { @@ -763,29 +709,7 @@ static DEVICE_API(adc, adc_nrfx_driver_api) = { "1v8 inputs cannot be mixed with 3v3 inputs"); /* Validate configuration of all channels. */ -#define VALIDATE_CHANNELS_CONFIG(inst) DT_FOREACH_CHILD(DT_DRV_INST(inst), VALIDATE_CHANNEL_CONFIG) +DT_FOREACH_CHILD(DT_DRV_INST(0), VALIDATE_CHANNEL_CONFIG) -/* - * There is only one instance on supported SoCs, so inst is guaranteed - * to be 0 if any instance is okay. (We use adc_0 above, so the driver - * is relying on the numeric instance value in a way that happens to - * be safe.) - * - * Just in case that assumption becomes invalid in the future, we use - * a BUILD_ASSERT(). - */ -#define SAADC_INIT(inst) \ - BUILD_ASSERT((inst) == 0, \ - "multiple instances not supported"); \ - VALIDATE_CHANNELS_CONFIG(inst) \ - PM_DEVICE_DT_INST_DEFINE(0, saadc_pm_hook, 1); \ - DEVICE_DT_INST_DEFINE(0, \ - init_saadc, \ - PM_DEVICE_DT_INST_GET(0), \ - NULL, \ - NULL, \ - POST_KERNEL, \ - CONFIG_ADC_INIT_PRIORITY, \ - &adc_nrfx_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(SAADC_INIT) +DEVICE_DT_INST_DEFINE(0, init_saadc, NULL, NULL, NULL, POST_KERNEL, + CONFIG_ADC_INIT_PRIORITY, &adc_nrfx_driver_api); From c939d71460fe55d7ff18514b3579a3e243437eb8 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Mon, 14 Jul 2025 15:04:00 +0200 Subject: [PATCH 5/8] [nrf fromtree] drivers: adc: nrfx_saadc: Fix SAADC shim for nRF54LV10A GAIN field is not present on LV10A, so instead we need to check the input value to the function. Signed-off-by: Jakub Zymelka (cherry picked from commit 35044b64e4878169d1bb95a5c560337b0ecb41a6) --- drivers/adc/adc_nrfx_saadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index f9f136f2509..bb0b4d5ab0a 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -283,7 +283,7 @@ static int gain_set(nrf_saadc_channel_config_t *ch_cfg, enum adc_gain gain) #endif default: #else - if (ch_cfg->gain != ADC_GAIN_1) { + if (gain != ADC_GAIN_1) { #endif /* defined(NRF_SAADC_HAS_CH_GAIN) */ LOG_ERR("Selected ADC gain is not valid"); return -EINVAL; From 657d122be2478b0400f4aaae6d3176c74e05bbcd Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Fri, 18 Jul 2025 12:33:00 +0200 Subject: [PATCH 6/8] [nrf fromlist] manifest: update hal_nordic revision to integrate nrfx 3.13.0 New nrfx release contains MDK 8.72.1 and various fixes for nRF devices. Upstream PR #: 93316 Signed-off-by: Nikodem Kastelik --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 2a059662446..31740627efb 100644 --- a/west.yml +++ b/west.yml @@ -200,7 +200,7 @@ manifest: groups: - hal - name: hal_nordic - revision: 9587b1dcb83d24ab74e89837843a5f7d573f7059 + revision: 2f5d4e5868ab573eac932fa4bc142565073c3c04 path: modules/hal/nordic groups: - hal From 25bdff4dbeaeb1e3224000c4bdf3780d328acbd6 Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Tue, 22 Jul 2025 23:39:44 +0200 Subject: [PATCH 7/8] [nrf fromlist] drivers: adc: nrfx_saadc: remove VDD and DVDD inputs for nRF54LV10A MDK 8.72.1 does not specify VDD and DVDD internal analog inputs for this SoC. Upstream PR #: 93316 Signed-off-by: Nikodem Kastelik --- drivers/adc/adc_nrfx_saadc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index bb0b4d5ab0a..274a43157e0 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -66,7 +66,7 @@ static const uint32_t saadc_psels[NRF_SAADC_DVDD + 1] = { [NRF_SAADC_DVDD] = NRF_SAADC_INPUT_DVDD, }; #elif defined(NRF54LV10A_ENGA_XXAA) -static const uint32_t saadc_psels[NRF_SAADC_DVDD + 1] = { +static const uint32_t saadc_psels[NRF_SAADC_AIN7 + 1] = { [NRF_SAADC_AIN0] = NRF_PIN_PORT_TO_PIN_NUMBER(0U, 1), [NRF_SAADC_AIN1] = NRF_PIN_PORT_TO_PIN_NUMBER(1U, 1), [NRF_SAADC_AIN2] = NRF_PIN_PORT_TO_PIN_NUMBER(2U, 1), @@ -75,8 +75,6 @@ static const uint32_t saadc_psels[NRF_SAADC_DVDD + 1] = { [NRF_SAADC_AIN5] = NRF_PIN_PORT_TO_PIN_NUMBER(10U, 1), [NRF_SAADC_AIN6] = NRF_PIN_PORT_TO_PIN_NUMBER(11U, 1), [NRF_SAADC_AIN7] = NRF_PIN_PORT_TO_PIN_NUMBER(12U, 1), - [NRF_SAADC_VDD] = NRF_SAADC_INPUT_VDD, - [NRF_SAADC_DVDD] = NRF_SAADC_INPUT_DVDD, }; #endif From 9fba9a69965a45120f7cace1ad2d37b606592de4 Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Wed, 23 Jul 2025 18:30:10 +0200 Subject: [PATCH 8/8] [nrf fromlist] tests: nrf: comp: fix VDD reference dependency Some SoCs might not have any VDD reference available. Use internal 1.2V reference derived from VDD in this case. Upstream PR #: 93316 Signed-off-by: Nikodem Kastelik --- tests/boards/nrf/comp/src/test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/boards/nrf/comp/src/test.c b/tests/boards/nrf/comp/src/test.c index 2195f0e1ea9..1d1876e76fa 100644 --- a/tests/boards/nrf/comp/src/test.c +++ b/tests/boards/nrf/comp/src/test.c @@ -93,8 +93,11 @@ ZTEST(comparator_runtime_configure, test_comp_config_se_vdd) #ifdef COMP_REFSEL_REFSEL_AVDDAO1V8 conf.refsel = COMP_NRF_COMP_REFSEL_AVDDAO1V8; -#else +#elif defined(COMP_REFSEL_REFSEL_VDD) conf.refsel = COMP_NRF_COMP_REFSEL_VDD; +#else + /* Use internal 1.2 V derived from VDD */ + conf.refsel = COMP_NRF_COMP_REFSEL_INT_1V2; #endif rc = comp_nrf_comp_configure_se(test_dev, &conf); zassert_equal(rc, 0, "Cannot configure comparator.");