diff --git a/boards/silabs/radio_boards/slwrb4180a/slwrb4180a-pinctrl.dtsi b/boards/silabs/radio_boards/slwrb4180a/slwrb4180a-pinctrl.dtsi index 35e2eb1a77b78..897a2602ae75b 100644 --- a/boards/silabs/radio_boards/slwrb4180a/slwrb4180a-pinctrl.dtsi +++ b/boards/silabs/radio_boards/slwrb4180a/slwrb4180a-pinctrl.dtsi @@ -21,4 +21,10 @@ silabs,input-filter; }; }; + + iadc0_default: iadc0_default { + group0 { + silabs,analog-bus = ; + }; + }; }; diff --git a/boards/silabs/radio_boards/slwrb4180a/slwrb4180a.dts b/boards/silabs/radio_boards/slwrb4180a/slwrb4180a.dts index 65c40eac720c0..383cbed36fdd1 100644 --- a/boards/silabs/radio_boards/slwrb4180a/slwrb4180a.dts +++ b/boards/silabs/radio_boards/slwrb4180a/slwrb4180a.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include #include "slwrb4180a-pinctrl.dtsi" @@ -30,6 +31,7 @@ sw0 = &button0; sw1 = &button1; watchdog0 = &wdog0; + adc0 = &adc0; }; leds { @@ -64,6 +66,37 @@ }; }; + joystick { + compatible = "adc-keys"; + io-channels = <&adc0 0>; + keyup-threshold-mv = <3300>; + + select-key { + press-thresholds-mv = <33>; + zephyr,code = ; + }; + + left-key { + press-thresholds-mv = <1980>; + zephyr,code = ; + }; + + down-key { + press-thresholds-mv = <1650>; + zephyr,code = ; + }; + + up-key { + press-thresholds-mv = <2831>; + zephyr,code = ; + }; + + right-key { + press-thresholds-mv = <2533>; + zephyr,code = ; + }; + }; + exp_header: exp-header { compatible = "silabs,exp-header"; #gpio-cells = <2>; @@ -80,6 +113,10 @@ gpio-map-mask = <0xffffffff 0x0>; gpio-map-pass-thru = <0x0 GPIO_DT_FLAGS_MASK>; }; + + zephyr,user { + io-channels = <&adc0 0>; + }; }; &cpu0 { @@ -128,6 +165,24 @@ clocks = <&lfxo>; }; +&adc0 { + pinctrl-0 = <&iadc0_default>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + reg = <0>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,input-positive = ; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; + }; +}; + &usart0 { current-speed = <115200>; pinctrl-0 = <&usart0_default>; diff --git a/boards/silabs/radio_boards/slwrb4180b/slwrb4180b-pinctrl.dtsi b/boards/silabs/radio_boards/slwrb4180b/slwrb4180b-pinctrl.dtsi index 92871f3b80338..54c9d56c8147a 100644 --- a/boards/silabs/radio_boards/slwrb4180b/slwrb4180b-pinctrl.dtsi +++ b/boards/silabs/radio_boards/slwrb4180b/slwrb4180b-pinctrl.dtsi @@ -20,4 +20,10 @@ silabs,input-filter; }; }; + + iadc0_default: iadc0_default { + group0 { + silabs,analog-bus = ; + }; + }; }; diff --git a/boards/silabs/radio_boards/slwrb4180b/slwrb4180b.dts b/boards/silabs/radio_boards/slwrb4180b/slwrb4180b.dts index 906e380ea9ed7..7003aa42f6d03 100644 --- a/boards/silabs/radio_boards/slwrb4180b/slwrb4180b.dts +++ b/boards/silabs/radio_boards/slwrb4180b/slwrb4180b.dts @@ -7,6 +7,7 @@ /dts-v1/; #include +#include #include #include "slwrb4180b-pinctrl.dtsi" @@ -30,6 +31,7 @@ sw0 = &button0; sw1 = &button1; watchdog0 = &wdog0; + adc0 = &adc0; }; leds { @@ -62,6 +64,37 @@ }; }; + joystick { + compatible = "adc-keys"; + io-channels = <&adc0 0>; + keyup-threshold-mv = <3300>; + + select-key { + press-thresholds-mv = <33>; + zephyr,code = ; + }; + + left-key { + press-thresholds-mv = <1980>; + zephyr,code = ; + }; + + down-key { + press-thresholds-mv = <1650>; + zephyr,code = ; + }; + + up-key { + press-thresholds-mv = <2831>; + zephyr,code = ; + }; + + right-key { + press-thresholds-mv = <2533>; + zephyr,code = ; + }; + }; + exp_header: exp-header { compatible = "silabs,exp-header"; #gpio-cells = <2>; @@ -78,6 +111,10 @@ gpio-map-mask = <0xffffffff 0x0>; gpio-map-pass-thru = <0x0 GPIO_DT_FLAGS_MASK>; }; + + zephyr,user { + io-channels = <&adc0 0>; + }; }; &cpu0 { @@ -126,6 +163,24 @@ clocks = <&lfxo>; }; +&adc0 { + pinctrl-0 = <&iadc0_default>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + reg = <0>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,input-positive = ; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; + }; +}; + &usart0 { current-speed = <115200>; pinctrl-0 = <&usart0_default>; diff --git a/boards/silabs/radio_boards/xg27/xg27_rb4194a.dts b/boards/silabs/radio_boards/xg27/xg27_rb4194a.dts index 95ff987362198..104e33ac3c968 100644 --- a/boards/silabs/radio_boards/xg27/xg27_rb4194a.dts +++ b/boards/silabs/radio_boards/xg27/xg27_rb4194a.dts @@ -36,6 +36,7 @@ sw1 = &button1; watchdog0 = &wdog0; dht0 = &si7021; + adc0 = &adc0; }; leds { diff --git a/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a-pinctrl.dtsi b/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a-pinctrl.dtsi index 417226304ad62..6318bfa592511 100644 --- a/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a-pinctrl.dtsi +++ b/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a-pinctrl.dtsi @@ -21,6 +21,12 @@ }; }; + iadc0_default: iadc0_default { + group0 { + silabs,analog-bus = ; + }; + }; + itm_default: itm_default { group0 { pins = ; diff --git a/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a.dts b/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a.dts index 15beeca3a2eca..d23eb5153d83e 100644 --- a/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a.dts +++ b/boards/silabs/radio_boards/xg29_rb4412a/xg29_rb4412a.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include #include #include @@ -35,6 +36,7 @@ sw1 = &button1; watchdog0 = &wdog0; dht0 = &si7021; + adc0 = &adc0; }; leds { @@ -75,6 +77,37 @@ }; }; + joystick { + compatible = "adc-keys"; + io-channels = <&adc0 0>; + keyup-threshold-mv = <3300>; + + select-key { + press-thresholds-mv = <33>; + zephyr,code = ; + }; + + left-key { + press-thresholds-mv = <1980>; + zephyr,code = ; + }; + + down-key { + press-thresholds-mv = <1650>; + zephyr,code = ; + }; + + up-key { + press-thresholds-mv = <2831>; + zephyr,code = ; + }; + + right-key { + press-thresholds-mv = <2533>; + zephyr,code = ; + }; + }; + display_enable: sensor_enable: gpio_switch_0 { compatible = "regulator-fixed"; enable-gpios = <&gpioc 7 GPIO_ACTIVE_HIGH>; @@ -98,6 +131,10 @@ gpio-map-mask = <0xffffffff 0x0>; gpio-map-pass-thru = <0x0 GPIO_DT_FLAGS_MASK>; }; + + zephyr,user { + io-channels = <&adc0 0>; + }; }; &cpu0 { @@ -210,6 +247,24 @@ }; }; +&adc0 { + pinctrl-0 = <&iadc0_default>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + reg = <0>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,input-positive = ; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; + }; +}; + &gpio { status = "okay"; }; diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index 00a529067cda0..fc602127265d5 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -58,6 +58,11 @@ Device Drivers and Devicetree .. zephyr-keep-sorted-start re(^\w) +ADC +=== + +* :dtcompatible:`silabs,gecko-iadc` has been replaced by :dtcompatible:`silabs,iadc`. + MFD === diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 08b8cc88bf344..ffde000aaca41 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -45,6 +45,7 @@ zephyr_library_sources_ifdef(CONFIG_ADC_XMC4XXX adc_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_ADC_ESP32 adc_esp32.c) zephyr_library_sources_ifdef(CONFIG_ADC_GECKO_ADC adc_gecko.c) zephyr_library_sources_ifdef(CONFIG_ADC_GECKO_IADC iadc_gecko.c) +zephyr_library_sources_ifdef(CONFIG_ADC_SILABS_IADC adc_silabs_iadc.c) zephyr_library_sources_ifdef(CONFIG_ADC_SILABS_SIWX91X adc_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_ADC_INFINEON_CAT1 adc_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_ADC_SMARTBOND_GPADC adc_smartbond_gpadc.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index b73d9f963b7fa..dfee7c23348e6 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -136,6 +136,8 @@ source "drivers/adc/Kconfig.gecko" source "drivers/adc/Kconfig.siwx91x" +source "drivers/adc/Kconfig.silabs" + source "drivers/adc/Kconfig.ifx_cat1" source "drivers/adc/Kconfig.smartbond" diff --git a/drivers/adc/Kconfig.silabs b/drivers/adc/Kconfig.silabs new file mode 100644 index 0000000000000..2d954a3c22d9c --- /dev/null +++ b/drivers/adc/Kconfig.silabs @@ -0,0 +1,20 @@ +# Copyright (c) 2025 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +config ADC_SILABS_IADC + bool "Silabs Incremental ADC driver" + default y + depends on DT_HAS_SILABS_IADC_ENABLED + select SILABS_SISDK_IADC + select ADC_CONFIGURABLE_INPUTS + help + Enable the driver implementation for Silabs Incremental ADC + +config ADC_SILABS_IADC_DMA + bool "Silabs IADC async DMA support" + depends on ADC_SILABS_IADC + depends on DMA + help + Enable DMA support with the Silabs IADC driver. + This allows ADC conversions to be performed (asynchronously or not) + using DMA for improved performance. diff --git a/drivers/adc/adc_silabs_iadc.c b/drivers/adc/adc_silabs_iadc.c new file mode 100644 index 0000000000000..fe731c2db63cc --- /dev/null +++ b/drivers/adc/adc_silabs_iadc.c @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_iadc + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(iadc, CONFIG_ADC_LOG_LEVEL); + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +/* Comptibility section for IADC IP version*/ +#if (_IADC_IPVERSION_RESETVALUE == 0x00000000UL) + +#define IADC_NO_DIGAVG 1 + +#define _IADC_CFG_DIGAVG_AVG1 -1 +#define _IADC_CFG_DIGAVG_AVG2 -1 +#define _IADC_CFG_DIGAVG_AVG4 -1 +#define _IADC_CFG_DIGAVG_AVG8 -1 +#define _IADC_CFG_DIGAVG_AVG16 -1 + +#define IADC_NO_EXTENDED_ALIGN 1 + +#define _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT16 -1 +#define _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20 -1 +#define _IADC_SCANFIFOCFG_ALIGNMENT_LEFT16 -1 +#define _IADC_SCANFIFOCFG_ALIGNMENT_LEFT20 -1 + +#define IADC_EXPLICIT_NEG_PIN 1 + +#endif + +#define IADC_PORT_MASK 0xF0 +#define IADC_PIN_MASK 0x0F + +struct iadc_dma_channel { + const struct device *dma_dev; + struct dma_block_config blk_cfg; + struct dma_config dma_cfg; + int dma_channel; + bool enabled; +}; + +struct iadc_chan_conf { + sl_hal_iadc_analog_gain_t gain; + sl_hal_iadc_voltage_reference_t reference; + sl_hal_iadc_positive_port_input_t pos_port; + uint8_t pos_pin; + sl_hal_iadc_negative_port_input_t neg_port; + uint8_t neg_pin; + uint8_t iadc_conf_id; + bool initialized; +}; + +struct iadc_data { + const struct device *dev; + struct adc_context ctx; + struct iadc_chan_conf chan_conf[SL_HAL_IADC_CHANNEL_ID_MAX]; + struct iadc_dma_channel dma; + uint8_t adc_config_count; /* Number of ADC configs created (max 2) */ + uint32_t clock_rate; + uint32_t channels; + uint16_t active_channels; + uint8_t alignment; + uint8_t oversampling; + uint8_t digital_averaging; + size_t data_size; + uint8_t *buffer; +}; + +struct iadc_config { + sl_hal_iadc_config_t config; + IADC_TypeDef *base; + const struct pinctrl_dev_config *pcfg; + const struct device *clock_dev; + struct silabs_clock_control_cmu_config clock_cfg; + void (*irq_cfg_func)(void); +}; + +static int iadc_find_or_create_adc_config(struct iadc_data *data, sl_hal_iadc_init_t *init, + const struct iadc_chan_conf *chan_conf) +{ + int iadc_conf_id; + + /* Check if we can reuse existing ADC configs */ + for (int i = 0; i < data->adc_config_count; i++) { + if (chan_conf->gain == init->configs[i].analog_gain && + chan_conf->reference == init->configs[i].reference) { + return i; + } + } + + if (data->adc_config_count >= ARRAY_SIZE(init->configs)) { + LOG_ERR("Maximum of 2 different ADC configs supported"); + return -EINVAL; + } + + iadc_conf_id = data->adc_config_count; + init->configs[iadc_conf_id].analog_gain = chan_conf->gain; + init->configs[iadc_conf_id].reference = chan_conf->reference; + data->adc_config_count++; + + return iadc_conf_id; +} + +static void iadc_configure_scan_table_entry(sl_hal_iadc_scan_table_entry_t *entry, + const struct iadc_chan_conf *chan_conf) +{ + *entry = (sl_hal_iadc_scan_table_entry_t){ + .positive_port = chan_conf->pos_port, + .positive_pin = chan_conf->pos_pin, + .negative_port = chan_conf->neg_port, + .negative_pin = chan_conf->neg_pin, + .config_id = chan_conf->iadc_conf_id, + .include_in_scan = true, + }; +} + +#ifdef CONFIG_ADC_SILABS_IADC_DMA +static int iadc_dma_init(const struct device *dev) +{ + const struct iadc_config *config = dev->config; + struct iadc_data *data = dev->data; + struct iadc_dma_channel *dma = &data->dma; + + if (!dma->dma_dev) { + return 0; + } + + if (!device_is_ready(dma->dma_dev)) { + LOG_ERR("DMA device not ready"); + return -ENODEV; + } + + dma->dma_channel = dma_request_channel(dma->dma_dev, NULL); + if (dma->dma_channel < 0) { + LOG_ERR("Failed to request DMA channel"); + return -ENODEV; + } + + memset(&dma->blk_cfg, 0, sizeof(dma->blk_cfg)); + dma->blk_cfg.source_address = (uintptr_t)&(config->base)->SCANFIFODATA; + dma->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + dma->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + dma->dma_cfg.complete_callback_en = 1; + dma->dma_cfg.channel_priority = 3; + dma->dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY; + dma->dma_cfg.head_block = &dma->blk_cfg; + dma->dma_cfg.user_data = data; + + return 0; +} + +static int iadc_dma_start(const struct device *dev) +{ + struct iadc_data *data = dev->data; + struct iadc_dma_channel *dma = &data->dma; + int ret; + + if (!dma->dma_dev) { + return -ENODEV; + } + + if (dma->enabled) { + return -EBUSY; + } + + ret = dma_config(dma->dma_dev, dma->dma_channel, &dma->dma_cfg); + if (ret) { + LOG_ERR("DMA config error: %d", ret); + return ret; + } + + dma->enabled = true; + + ret = dma_start(dma->dma_dev, dma->dma_channel); + if (ret) { + LOG_ERR("DMA start error: %d", ret); + dma->enabled = false; + return ret; + } + + return 0; +} + +static void iadc_dma_stop(const struct device *dev) +{ + struct iadc_data *data = dev->data; + struct iadc_dma_channel *dma = &data->dma; + + if (!dma->enabled) { + return; + } + + dma_stop(dma->dma_dev, dma->dma_channel); + + dma->enabled = false; +} + +static void iadc_dma_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status) +{ + struct iadc_data *data = user_data; + const struct device *dev = data->dev; + + if (status < 0) { + LOG_ERR("DMA transfer error: %d", status); + adc_context_complete(&data->ctx, status); + return; + } + + iadc_dma_stop(dev); + + adc_context_on_sampling_done(&data->ctx, dev); +} +#endif /* CONFIG_ADC_SILABS_IADC_DMA */ + +/* Oversampling and resolution are common for both ADC configs + * because they are not configurable per channel inside a ADC + * sequence and are common for a sequence. + */ +static int iadc_set_config(const struct device *dev) +{ + const struct iadc_config *config = dev->config; + IADC_TypeDef *iadc = config->base; + struct iadc_data *data = dev->data; + sl_hal_iadc_scan_table_t scan_table = {}; + sl_hal_iadc_init_t adc_init_config = { + .configs[0].analog_gain = _IADC_CFG_ANALOGGAIN_ANAGAIN1, + .configs[1].analog_gain = _IADC_CFG_ANALOGGAIN_ANAGAIN1, + .configs[0].vref = SL_HAL_IADC_DEFAULT_VREF, + .configs[1].vref = SL_HAL_IADC_DEFAULT_VREF, + .configs[0].osr_high_speed = data->oversampling, + .configs[1].osr_high_speed = data->oversampling, +#ifndef IADC_NO_DIGAVG + .configs[0].dig_avg = data->digital_averaging, + .configs[1].dig_avg = data->digital_averaging, +#endif + }; + sl_hal_iadc_init_scan_t scan_init = { + .data_valid_level = _IADC_SCANFIFOCFG_DVL_VALID4, + .alignment = data->alignment, + }; + struct iadc_chan_conf *chan_conf; + uint32_t channels; + int res; + + if (data->dma.dma_dev) { + scan_init.data_valid_level = _IADC_SCANFIFOCFG_DVL_VALID1; + /* Only needed to wake up DMA if EM is 2/3 */ + scan_init.fifo_dma_wakeup = true; + } + + data->adc_config_count = 0; + + if (data->dma.dma_dev) { + if (data->alignment == _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20) { + data->dma.dma_cfg.source_data_size = 4; + data->dma.dma_cfg.dest_data_size = 4; + data->dma.dma_cfg.source_burst_length = 4; + data->dma.dma_cfg.dest_burst_length = 4; + } else { + data->dma.dma_cfg.source_data_size = 2; + data->dma.dma_cfg.dest_data_size = 2; + data->dma.dma_cfg.source_burst_length = 2; + data->dma.dma_cfg.dest_burst_length = 2; + } + } + + channels = data->channels; + + /* + * Process each channel configuration and set up ADC scan sequence. + * The IADC hardware supports only 2 different ADC configurations + * (gain + reference combinations + oversampling), so we need to map + * multiple channel configs to these 2 available ADC configs. + */ + ARRAY_FOR_EACH(data->chan_conf, i) { + chan_conf = &data->chan_conf[i]; + + if (!chan_conf->initialized || (i != find_lsb_set(channels) - 1)) { + continue; + } + + res = iadc_find_or_create_adc_config(data, &adc_init_config, chan_conf); + if (res < 0) { + LOG_DBG("IADC: too many different ADC configurations"); + return res; + } + + chan_conf->iadc_conf_id = res; + + iadc_configure_scan_table_entry(&scan_table.entries[i], chan_conf); + + channels &= ~BIT(i); + } + + sl_hal_iadc_init(iadc, &adc_init_config, data->clock_rate); + sl_hal_iadc_init_scan(iadc, &scan_init, &scan_table); + sl_hal_iadc_set_scan_mask_multiple_entries(iadc, &scan_table); + + return 0; +} + +static int iadc_check_buffer_size(const struct adc_sequence *sequence, uint16_t active_channels, + size_t data_size) +{ + size_t needed_buffer_size = active_channels * data_size; + + if (sequence->options) { + needed_buffer_size *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed_buffer_size) { + LOG_DBG("Provided buffer is too small (%u/%u)", sequence->buffer_size, + needed_buffer_size); + return -ENOMEM; + } + + return 0; +} + +/* + * Goal of this function is to have concensius between wanted resolution, oversampling, + * the IADC Alignment Table, analog oversampling and digital averaging. + * + * Formulas: + * Output Resolution = 11 + log2(OversamplingRatio × DigitalAveraging) + * +------------+-------------+---------------+---------------+-------------+ + * | Alignment | Oversample | Digital Avg | Num Samples | Output Res | + * | Setting | Ratio | | Averaged | | + * +------------+-------------+---------------+---------------+-------------+ + * | 16-bit | 2x | 1x | 2 | 12 bits | + * | 16-bit | 8x | 2x | 16 | 15 bits | + * | 20-bit | 2x | 1x | 2 | 12 bits | + * | 20-bit | 16x | 4x | 64 | 17 bits | + * +------------+-------------+---------------+---------------+-------------+ + */ +static int iadc_check_oversampling_and_resolution(const struct adc_sequence *sequence, + struct iadc_data *data) +{ + int res = sequence->resolution; + int ospl; + + const static struct oversampling_table { + uint8_t analog_oversampling; + uint8_t digital_averaging; + } ospl_table[] = { + [0] = { _IADC_CFG_OSRHS_HISPD2, _IADC_CFG_DIGAVG_AVG1 }, /* 2x oversampling */ + [1] = { _IADC_CFG_OSRHS_HISPD2, _IADC_CFG_DIGAVG_AVG1 }, /* 2x oversampling */ + [2] = { _IADC_CFG_OSRHS_HISPD4, _IADC_CFG_DIGAVG_AVG1 }, /* 4x oversampling */ + [3] = { _IADC_CFG_OSRHS_HISPD8, _IADC_CFG_DIGAVG_AVG1 }, /* 8x oversampling */ + [4] = { _IADC_CFG_OSRHS_HISPD16, _IADC_CFG_DIGAVG_AVG1 }, /* 16x oversampling */ + [5] = { _IADC_CFG_OSRHS_HISPD32, _IADC_CFG_DIGAVG_AVG1 }, /* 32x oversampling */ + [6] = { _IADC_CFG_OSRHS_HISPD64, _IADC_CFG_DIGAVG_AVG1 }, /* 64x oversampling */ + [7] = { _IADC_CFG_OSRHS_HISPD64, _IADC_CFG_DIGAVG_AVG2 }, /* 128x oversampling */ + [8] = { _IADC_CFG_OSRHS_HISPD64, _IADC_CFG_DIGAVG_AVG4 }, /* 256x oversampling */ + [9] = { _IADC_CFG_OSRHS_HISPD64, _IADC_CFG_DIGAVG_AVG8 }, /* 512x oversampling */ + [10] = { _IADC_CFG_OSRHS_HISPD64, _IADC_CFG_DIGAVG_AVG16 }, /* 1024x oversampling */ + }; + + if (!sequence->oversampling) { + ospl = 1; + } else { + ospl = sequence->oversampling; + } + + if (ospl > ARRAY_SIZE(ospl_table) - 1) { + LOG_ERR("Unsupported oversampling %d", sequence->oversampling); + return -EINVAL; + } + + if (ospl > 6 && IS_ENABLED(IADC_NO_DIGAVG)) { + LOG_ERR("Unsupported oversampling %d", ospl); + return -EINVAL; + } + + if (res > 12 && IS_ENABLED(IADC_NO_EXTENDED_ALIGN)) { + LOG_ERR("Unsupported resolution %d", res); + return -EINVAL; + } + + switch (res) { + case 12: + data->alignment = _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT12; + break; + case 16: + data->alignment = _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT16; + break; + case 20: + data->alignment = _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20; + break; + default: + LOG_ERR("Unsupported resolution %d", res); + return -EINVAL; + } + + data->oversampling = ospl_table[ospl].analog_oversampling; + data->digital_averaging = ospl_table[ospl].digital_averaging; + + return 0; +} + +static int start_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct iadc_data *data = dev->data; + uint32_t channels; + uint16_t channel_count; + uint16_t index; + int res; + + if (sequence->channels == 0) { + LOG_DBG("No channel requested"); + return -EINVAL; + } + + res = iadc_check_oversampling_and_resolution(sequence, data); + if (res < 0) { + return res; + } + + if (data->alignment == _IADC_SCANFIFOCFG_ALIGNMENT_RIGHT20) { + data->data_size = sizeof(uint32_t); + } else { + data->data_size = sizeof(uint16_t); + } + + if (sequence->calibrate) { + /* TODO: Implement runtime calibration */ + LOG_DBG("Hardware have hardcoded calibration value but runtime calibration is not " + "supported"); + } + + channels = sequence->channels; + channel_count = 0; + while (channels) { + index = find_lsb_set(channels) - 1; + if (index >= SL_HAL_IADC_CHANNEL_ID_MAX) { + LOG_DBG("Requested channel index not available: %d", index); + return -EINVAL; + } + + if (!data->chan_conf[index].initialized) { + LOG_DBG("Channel not initialized"); + return -EINVAL; + } + channel_count++; + channels &= ~BIT(index); + } + + res = iadc_check_buffer_size(sequence, channel_count, data->data_size); + if (res < 0) { + return res; + } + + data->buffer = sequence->buffer; + data->active_channels = channel_count; + + if (data->dma.dma_dev) { + data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer; + data->dma.blk_cfg.block_size = channel_count * data->data_size; + } + + data->channels = sequence->channels; + + res = iadc_set_config(data->dev); + if (res < 0) { + return res; + } + + adc_context_start_read(&data->ctx, sequence); + + res = adc_context_wait_for_completion(&data->ctx); + + return res; +} + +static void iadc_start_scan(const struct device *dev) +{ + const struct iadc_config *config = dev->config; + __maybe_unused struct iadc_data *data = dev->data; + IADC_TypeDef *iadc = (IADC_TypeDef *)config->base; + +#ifdef CONFIG_ADC_SILABS_IADC_DMA + if (data->dma.dma_dev) { + data->dma.blk_cfg.dest_address = (uintptr_t)data->buffer; + iadc_dma_start(dev); + } else { + sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE); + } +#else + sl_hal_iadc_enable_interrupts(iadc, IADC_IEN_SCANTABLEDONE); +#endif + + sl_hal_iadc_start_scan(iadc); +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct iadc_data *data = CONTAINER_OF(ctx, struct iadc_data, ctx); + + iadc_start_scan(data->dev); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) +{ + struct iadc_data *data = CONTAINER_OF(ctx, struct iadc_data, ctx); + + if (!repeat_sampling) { + data->buffer += data->active_channels * data->data_size; + } +} + +static void iadc_isr(void *arg) +{ + const struct device *dev = (const struct device *)arg; + const struct iadc_config *config = dev->config; + struct iadc_data *data = dev->data; + uint8_t *sample_ptr = data->buffer; + IADC_TypeDef *iadc = config->base; + sl_hal_iadc_result_t sample; + uint32_t flags, err; + + flags = sl_hal_iadc_get_pending_interrupts(iadc); + sl_hal_iadc_clear_interrupts(iadc, flags); + + err = flags & (IADC_IF_PORTALLOCERR | IADC_IF_POLARITYERR | IADC_IF_EM23ABORTERROR | + IADC_IF_SCANFIFOOF | IADC_IF_SCANFIFOUF); + + if (flags & IADC_IF_SCANTABLEDONE) { + while (sl_hal_iadc_get_scan_fifo_cnt(iadc) > 0) { + sample = sl_hal_iadc_pull_scan_fifo_result(iadc); + memcpy(sample_ptr, &sample.data, data->data_size); + sample_ptr += data->data_size; + } + + adc_context_on_sampling_done(&data->ctx, dev); + } + + if (err) { + LOG_ERR("IADC error, flags=%08x", err); + adc_context_complete(&data->ctx, -EIO); + } +} + +static int iadc_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct iadc_data *data = dev->data; + int error; + + adc_context_lock(&data->ctx, false, NULL); + error = start_read(dev, sequence); + adc_context_release(&data->ctx, error); + + return error; +} + +#ifdef CONFIG_ADC_ASYNC +static int iadc_read_async(const struct device *dev, const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + struct iadc_data *data = dev->data; + int error; + + adc_context_lock(&data->ctx, true, async); + error = start_read(dev, sequence); + adc_context_release(&data->ctx, error); + + return error; +} +#endif + +static int iadc_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) +{ + struct iadc_data *data = dev->data; + struct iadc_chan_conf *chan_conf = NULL; + + if (channel_cfg->channel_id < SL_HAL_IADC_CHANNEL_ID_MAX) { + chan_conf = &data->chan_conf[channel_cfg->channel_id]; + } else { + LOG_DBG("Requested channel index not available: %d", channel_cfg->channel_id); + return -EINVAL; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; + } + + chan_conf->initialized = false; + + chan_conf->pos_port = (channel_cfg->input_positive & IADC_PORT_MASK) >> 4; + chan_conf->pos_pin = channel_cfg->input_positive & IADC_PIN_MASK; + + if (channel_cfg->differential) { + chan_conf->neg_port = (channel_cfg->input_negative & IADC_PORT_MASK) >> 4; + chan_conf->neg_pin = channel_cfg->input_negative & IADC_PIN_MASK; + } else { + chan_conf->neg_port = _IADC_SCAN_PORTNEG_GND; + if (chan_conf->pos_port == _IADC_SCAN_PORTPOS_SUPPLY && + IS_ENABLED(IADC_EXPLICIT_NEG_PIN)) { + chan_conf->neg_pin = 1; + } + } + + switch (channel_cfg->gain) { + case ADC_GAIN_1_2: + chan_conf->gain = _IADC_CFG_ANALOGGAIN_ANAGAIN0P5; + break; + case ADC_GAIN_1: + chan_conf->gain = _IADC_CFG_ANALOGGAIN_ANAGAIN1; + break; + case ADC_GAIN_2: + chan_conf->gain = _IADC_CFG_ANALOGGAIN_ANAGAIN2; + break; + case ADC_GAIN_3: + chan_conf->gain = _IADC_CFG_ANALOGGAIN_ANAGAIN3; + break; + case ADC_GAIN_4: + chan_conf->gain = _IADC_CFG_ANALOGGAIN_ANAGAIN4; + break; + default: + LOG_ERR("unsupported channel gain '%d'", channel_cfg->gain); + return -EINVAL; + } + + /* Setup reference */ + switch (channel_cfg->reference) { + case ADC_REF_VDD_1: + chan_conf->reference = _IADC_CFG_REFSEL_VDDX; + break; + case ADC_REF_INTERNAL: + chan_conf->reference = _IADC_CFG_REFSEL_VBGR; + break; + case ADC_REF_EXTERNAL0: + chan_conf->reference = _IADC_CFG_REFSEL_VREF; + break; + default: + LOG_ERR("unsupported channel reference type '%d'", channel_cfg->reference); + return -EINVAL; + } + + chan_conf->initialized = true; + + LOG_DBG("Channel setup succeeded!"); + + return 0; +} + +static int iadc_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct iadc_config *config = dev->config; + int err; + + if (action == PM_DEVICE_ACTION_RESUME) { + err = clock_control_on(config->clock_dev, + (clock_control_subsys_t)&config->clock_cfg); + if (err < 0 && err != -EALREADY) { + return err; + } + + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0 && err != -ENOENT) { + return err; + } + } else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) { + err = clock_control_off(config->clock_dev, + (clock_control_subsys_t)&config->clock_cfg); + if (err < 0) { + return err; + } + + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); + if (err < 0 && err != -ENOENT) { + return err; + } + } else { + return -ENOTSUP; + } + + return 0; +} + +static int iadc_init(const struct device *dev) +{ + const struct iadc_config *config = dev->config; + struct iadc_data *data = dev->data; + int ret; + + data->dev = dev; + + ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_cfg); + if (ret < 0 && ret != -EALREADY) { + return ret; + } + + ret = clock_control_get_rate(config->clock_dev, (clock_control_subsys_t)&config->clock_cfg, + &data->clock_rate); + if (ret < 0) { + return ret; + } + +#ifdef CONFIG_ADC_SILABS_IADC_DMA + ret = iadc_dma_init(dev); + if (ret < 0) { + data->dma.dma_dev = NULL; + } +#endif + + config->irq_cfg_func(); + + adc_context_unlock_unconditionally(&data->ctx); + + return pm_device_driver_init(dev, iadc_pm_action); +} + +static DEVICE_API(adc, iadc_api) = { + .channel_setup = iadc_channel_setup, + .read = iadc_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = iadc_read_async, +#endif + .ref_internal = SL_HAL_IADC_DEFAULT_VREF, +}; + +#ifdef CONFIG_ADC_SILABS_IADC_DMA +#define IADC_DMA_CHANNEL_INIT(n) \ + .dma.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR(n)), \ + .dma.dma_cfg.dma_slot = SILABS_LDMA_REQSEL_TO_SLOT(DT_INST_DMAS_CELL_BY_IDX(n, 0, slot)), \ + .dma.dma_cfg.dma_callback = iadc_dma_cb, +#define IADC_DMA_CHANNEL(n) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), (IADC_DMA_CHANNEL_INIT(n)), ()) +#else +#define IADC_DMA_CHANNEL(n) +#endif + +#define IADC_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + PM_DEVICE_DT_INST_DEFINE(n, iadc_pm_action); \ + \ + static void iadc_config_func_##n(void); \ + \ + const static struct iadc_config iadc_config_##n = { \ + .base = (IADC_TypeDef *)DT_INST_REG_ADDR(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_cfg = SILABS_DT_INST_CLOCK_CFG(n), \ + .irq_cfg_func = iadc_config_func_##n, \ + }; \ + \ + static struct iadc_data iadc_data_##n = { \ + ADC_CONTEXT_INIT_TIMER(iadc_data_##n, ctx), \ + ADC_CONTEXT_INIT_LOCK(iadc_data_##n, ctx), \ + ADC_CONTEXT_INIT_SYNC(iadc_data_##n, ctx), \ + IADC_DMA_CHANNEL(n) \ + }; \ + \ + static void iadc_config_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), iadc_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + }; \ + DEVICE_DT_INST_DEFINE(n, &iadc_init, PM_DEVICE_DT_INST_GET(n), &iadc_data_##n, \ + &iadc_config_##n, POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, &iadc_api); + +DT_INST_FOREACH_STATUS_OKAY(IADC_INIT) diff --git a/dts/arm/silabs/xg21/xg21.dtsi b/dts/arm/silabs/xg21/xg21.dtsi index 6705ddc299265..15a07b76f4e70 100644 --- a/dts/arm/silabs/xg21/xg21.dtsi +++ b/dts/arm/silabs/xg21/xg21.dtsi @@ -469,7 +469,7 @@ }; adc0: adc@5a004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x5a004000 0x4000>; #io-channel-cells = <1>; clocks = <&cmu CLOCK_AUTO CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg22/xg22.dtsi b/dts/arm/silabs/xg22/xg22.dtsi index 3932f06e53f78..adf14d02b739c 100644 --- a/dts/arm/silabs/xg22/xg22.dtsi +++ b/dts/arm/silabs/xg22/xg22.dtsi @@ -510,7 +510,7 @@ }; adc0: adc@5a004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x5a004000 0x4000>; interrupts = <48 2>; clocks = <&cmu CLOCK_IADC0 CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg23/xg23.dtsi b/dts/arm/silabs/xg23/xg23.dtsi index b41ed312c9b4d..36c88b4e926b1 100644 --- a/dts/arm/silabs/xg23/xg23.dtsi +++ b/dts/arm/silabs/xg23/xg23.dtsi @@ -576,7 +576,7 @@ }; adc0: adc@59004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x59004000 0x4000>; interrupts = <50 2>; clocks = <&cmu CLOCK_IADC0 CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg24/xg24.dtsi b/dts/arm/silabs/xg24/xg24.dtsi index 987ed2591e3c8..16a798f9e9451 100644 --- a/dts/arm/silabs/xg24/xg24.dtsi +++ b/dts/arm/silabs/xg24/xg24.dtsi @@ -544,7 +544,7 @@ }; adc0: adc@59004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x59004000 0x4000>; interrupts = <49 2>; clocks = <&cmu CLOCK_IADC0 CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg27/xg27.dtsi b/dts/arm/silabs/xg27/xg27.dtsi index ac7f0451734d4..03d3cf613599c 100644 --- a/dts/arm/silabs/xg27/xg27.dtsi +++ b/dts/arm/silabs/xg27/xg27.dtsi @@ -511,7 +511,7 @@ }; adc0: adc@5a004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x5a004000 0x4000>; #io-channel-cells = <1>; clocks = <&cmu CLOCK_IADC0 CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg28/xg28.dtsi b/dts/arm/silabs/xg28/xg28.dtsi index 4155f2760746e..273ba91b7d1ad 100644 --- a/dts/arm/silabs/xg28/xg28.dtsi +++ b/dts/arm/silabs/xg28/xg28.dtsi @@ -569,7 +569,7 @@ }; adc0: adc@59004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x59004000 0x4000>; interrupts = <50 2>; clocks = <&cmu CLOCK_IADC0 CLOCK_BRANCH_IADCCLK>; diff --git a/dts/arm/silabs/xg29/xg29.dtsi b/dts/arm/silabs/xg29/xg29.dtsi index a474686126804..a9f386a631361 100644 --- a/dts/arm/silabs/xg29/xg29.dtsi +++ b/dts/arm/silabs/xg29/xg29.dtsi @@ -548,7 +548,7 @@ }; adc0: adc@5a004000 { - compatible = "silabs,gecko-iadc"; + compatible = "silabs,iadc"; reg = <0x5a004000 0x4000>; interrupts = <54 2>; interrupt-names = "iadc0"; diff --git a/dts/bindings/adc/silabs,iadc.yaml b/dts/bindings/adc/silabs,iadc.yaml new file mode 100644 index 0000000000000..bb7df0c9b356a --- /dev/null +++ b/dts/bindings/adc/silabs,iadc.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2025 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +title: Silicon Labs Series 2 IADC + +description: | + Incremental ADC peripheral for Silicon Labs Series 2 SoCs. + +compatible: "silabs,iadc" + +include: [adc-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input diff --git a/include/zephyr/drivers/adc.h b/include/zephyr/drivers/adc.h index a0ef4c916f7c4..8334f504ba147 100644 --- a/include/zephyr/drivers/adc.h +++ b/include/zephyr/drivers/adc.h @@ -1155,11 +1155,16 @@ typedef int (*adc_raw_to_x_fn)(int32_t ref_mv, enum adc_gain gain, uint8_t resol static inline int adc_raw_to_millivolts(int32_t ref_mv, enum adc_gain gain, uint8_t resolution, int32_t *valp) { - int32_t adc_mv = *valp * ref_mv; - int ret = adc_gain_invert(gain, &adc_mv); + int64_t adc_mv = (int64_t)*valp * (int64_t)ref_mv; + int ret = adc_gain_invert_64(gain, &adc_mv); if (ret == 0) { - *valp = (adc_mv >> resolution); + adc_mv = adc_mv >> resolution; + if (adc_mv > INT32_MAX || adc_mv < INT32_MIN) { + __ASSERT_MSG_INFO("conversion result is out of range"); + } + + *valp = (int32_t)adc_mv; } return ret; diff --git a/modules/hal_silabs/simplicity_sdk/CMakeLists.txt b/modules/hal_silabs/simplicity_sdk/CMakeLists.txt index 4d45bff3d248a..1aff96a754271 100644 --- a/modules/hal_silabs/simplicity_sdk/CMakeLists.txt +++ b/modules/hal_silabs/simplicity_sdk/CMakeLists.txt @@ -216,7 +216,12 @@ if(CONFIG_SOC_SILABS_SLEEPTIMER) ) endif() -zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_IADC ${EMLIB_DIR}/src/em_iadc.c) +zephyr_library_sources_ifdef(CONFIG_SILABS_SISDK_SYSTEM ${PERIPHERAL_DIR}/src/sl_hal_system.c) + +#Keep em_iadc.c for compatibility for now. Not used anymore. +zephyr_library_sources_ifdef(CONFIG_SILABS_SISDK_IADC ${EMLIB_DIR}/src/em_iadc.c) +zephyr_library_sources_ifdef(CONFIG_SILABS_SISDK_IADC ${PERIPHERAL_DIR}/src/sl_hal_iadc.c) + zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_BURTC ${EMLIB_DIR}/src/em_burtc.c) zephyr_library_sources_ifdef(CONFIG_SOC_GECKO_CMU ${EMLIB_DIR}/src/em_cmu.c) diff --git a/modules/hal_silabs/simplicity_sdk/Kconfig b/modules/hal_silabs/simplicity_sdk/Kconfig index 2fcaa9666f11d..c1f13d8295d42 100644 --- a/modules/hal_silabs/simplicity_sdk/Kconfig +++ b/modules/hal_silabs/simplicity_sdk/Kconfig @@ -7,9 +7,16 @@ menu "SiSDK configuration" config SILABS_SISDK_GPIO bool "Peripheral HAL for GPIO" +config SILABS_SISDK_SYSTEM + bool "Peripheral HAL for SYSTEM (device info)" + config SILABS_SISDK_I2C bool "Peripheral HAL for I2C" +config SILABS_SISDK_IADC + bool "Peripheral HAL for IADC" + select SILABS_SISDK_SYSTEM + config SILABS_SISDK_LETIMER bool "Peripheral HAL for LETIMER" diff --git a/samples/drivers/adc/adc_dt/sample.yaml b/samples/drivers/adc/adc_dt/sample.yaml index d7831ed37055a..b793c69d6cad9 100644 --- a/samples/drivers/adc/adc_dt/sample.yaml +++ b/samples/drivers/adc/adc_dt/sample.yaml @@ -38,7 +38,8 @@ tests: - mcx_n9xx_evk/mcxn947/cpu0 - frdm_mcxc242 - ucans32k1sic - - xg24_rb4187c + - slwrb4180a + - xg27_rb4194a - xg29_rb4412a - raytac_an54l15q_db/nrf54l15/cpuapp - frdm_mcxa346 diff --git a/samples/drivers/adc/adc_sequence/Kconfig b/samples/drivers/adc/adc_sequence/Kconfig index e2d97244d4f6f..d4374b8003dcb 100644 --- a/samples/drivers/adc/adc_sequence/Kconfig +++ b/samples/drivers/adc/adc_sequence/Kconfig @@ -9,6 +9,10 @@ config SEQUENCE_RESOLUTION int "Set the resolution of the sequence readings." default 12 +config SEQUENCE_OVERSAMPLING + int "Set the oversampling of the sequence readings." + default 0 + config SEQUENCE_32BITS_REGISTERS bool "ADC data sequences are on 32bits" default n diff --git a/samples/drivers/adc/adc_sequence/sample.yaml b/samples/drivers/adc/adc_sequence/sample.yaml index d176e2593d4b2..cce6640a72215 100644 --- a/samples/drivers/adc/adc_sequence/sample.yaml +++ b/samples/drivers/adc/adc_sequence/sample.yaml @@ -27,6 +27,9 @@ tests: - s32k148_evb - frdm_mcxc242 - stm32f3_disco + - slwrb4180a + - xg27_rb4194a + - xg29_rb4412a integration_platforms: - nrf52840dk/nrf52840 sample.drivers.adc.adc_sequence.8bit: diff --git a/samples/drivers/adc/adc_sequence/src/main.c b/samples/drivers/adc/adc_sequence/src/main.c index aa6d64d7a72fd..e4889e2f9630d 100644 --- a/samples/drivers/adc/adc_sequence/src/main.c +++ b/samples/drivers/adc/adc_sequence/src/main.c @@ -48,6 +48,7 @@ int main(void) /* buffer size in bytes, not number of samples */ .buffer_size = sizeof(channel_reading), .resolution = CONFIG_SEQUENCE_RESOLUTION, + .oversampling = CONFIG_SEQUENCE_OVERSAMPLING, .options = &options, }; @@ -91,13 +92,30 @@ int main(void) CONFIG_SEQUENCE_SAMPLES); for (size_t sample_index = 0U; sample_index < CONFIG_SEQUENCE_SAMPLES; sample_index++) { - - val_mv = channel_reading[sample_index][channel_index]; - + uint8_t res = CONFIG_SEQUENCE_RESOLUTION; + + /* + * If using differential mode, the 16/32 bit value + * in the ADC sample buffer should be a signed 2's + * complement value. + * Also reduce the resolution by 1 for the conversion + */ + if (channel_cfgs[channel_index].differential) { +#ifdef CONFIG_SEQUENCE_32BITS_REGISTERS + val_mv = (int32_t) + channel_reading[sample_index][channel_index]; +#else + val_mv = (int32_t)((int16_t)channel_reading[sample_index] + [channel_index]); +#endif + res -= 1; + } else { + val_mv = channel_reading[sample_index][channel_index]; + } printf("- - %" PRId32, val_mv); err = adc_raw_to_millivolts(vrefs_mv[channel_index], channel_cfgs[channel_index].gain, - CONFIG_SEQUENCE_RESOLUTION, &val_mv); + res, &val_mv); /* conversion to mV may not be supported, skip if not */ if ((err < 0) || vrefs_mv[channel_index] == 0) { diff --git a/tests/drivers/adc/adc_accuracy_test/boards/sltb010a.overlay b/tests/drivers/adc/adc_accuracy_test/boards/sltb010a.overlay new file mode 100644 index 0000000000000..5dd01b80daa2e --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/sltb010a.overlay @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Silicon Laboratories Inc. + */ + +#include + +/ { + zephyr,user { + io-channels = <&adc0 3>; + reference-mv = <(3000 / 4)>; + expected-accuracy = <32>; + }; +}; + +&adc0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/boards/slwrb4180a.overlay b/tests/drivers/adc/adc_accuracy_test/boards/slwrb4180a.overlay new file mode 100644 index 0000000000000..927dd476a667a --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/slwrb4180a.overlay @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Silicon Laboratories Inc. + */ + +#include + +/ { + zephyr,user { + io-channels = <&adc0 3>; + reference-mv = <(3300 / 4)>; + expected-accuracy = <32>; + }; +}; + +&adc0 { + + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/boards/xg23_rb4210a.overlay b/tests/drivers/adc/adc_accuracy_test/boards/xg23_rb4210a.overlay new file mode 100644 index 0000000000000..2537261f2fd09 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/xg23_rb4210a.overlay @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Silicon Laboratories Inc. + */ + +#include + +/ { + zephyr,user { + io-channels = <&adc0 4>; + reference-mv = <(3300 / 4)>; + expected-accuracy = <32>; + }; +}; + +&adc0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@4 { + reg = <4>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/testcase.yaml b/tests/drivers/adc/adc_accuracy_test/testcase.yaml index 03f122f9cdaae..e76f4be820132 100644 --- a/tests/drivers/adc/adc_accuracy_test/testcase.yaml +++ b/tests/drivers/adc/adc_accuracy_test/testcase.yaml @@ -39,11 +39,14 @@ tests: - ek_ra4m2 - ek_ra4m3 - ek_ra4w1 + - sltb010a + - xg23_rb4210a - xg24_dk2601b - xg24_rb4187c - xg27_dk2602a - xg29_rb4412a - bg29_rb4420a + - slwrb4180a integration_platforms: - frdm_kl25z - nrf54l15dk/nrf54l15/cpuapp diff --git a/tests/drivers/adc/adc_api/boards/bg29_rb4420a.overlay b/tests/drivers/adc/adc_api/boards/bg29_rb4420a.overlay index 76f95bb2ae849..3b62758ee6562 100644 --- a/tests/drivers/adc/adc_api/boards/bg29_rb4420a.overlay +++ b/tests/drivers/adc/adc_api/boards/bg29_rb4420a.overlay @@ -16,6 +16,7 @@ status = "okay"; #address-cells = <1>; #size-cells = <0>; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; channel@0 { reg = <0>; @@ -35,3 +36,7 @@ zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_api/boards/sltb010a.overlay b/tests/drivers/adc/adc_api/boards/sltb010a.overlay index baf8886de9946..b7f0228b044d8 100644 --- a/tests/drivers/adc/adc_api/boards/sltb010a.overlay +++ b/tests/drivers/adc/adc_api/boards/sltb010a.overlay @@ -2,33 +2,55 @@ * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2023 Antmicro + * Copyright (c) 2025 Silicon Laboratories Inc. + * */ +#include + / { zephyr,user { - io-channels = <&adc0 0>, <&adc0 1>; + io-channels = <&adc0 3>, <&adc0 4>; + }; +}; + +&pinctrl { + adc0_default: adc0_default { + group0 { + /* Allocate odd bus 0 on GPIO port B to IADC for access to pin PB1 */ + silabs,analog-bus = ; + }; }; }; &adc0 { + pinctrl-0 = <&adc0_default>; + pinctrl-names = "default"; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; #address-cells = <1>; #size-cells = <0>; + status = "okay"; - channel@0 { - reg = <0>; + channel@3 { + reg = <3>; zephyr,gain = "ADC_GAIN_1"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = ; zephyr,resolution = <12>; - zephyr,input-positive = <0x11>; + zephyr,input-positive = ; }; - channel@1 { - reg = <1>; + channel@4 { + reg = <4>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,vref-mv = <3000>; zephyr,acquisition-time = ; zephyr,resolution = <12>; - zephyr,input-positive = <0x01>; + zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/samples/drivers/adc/adc_dt/boards/xg29_rb4412a.overlay b/tests/drivers/adc/adc_api/boards/slwrb4180a.overlay similarity index 93% rename from samples/drivers/adc/adc_dt/boards/xg29_rb4412a.overlay rename to tests/drivers/adc/adc_api/boards/slwrb4180a.overlay index 93ff3ab1dd52f..6ebddd3065bad 100644 --- a/samples/drivers/adc/adc_dt/boards/xg29_rb4412a.overlay +++ b/tests/drivers/adc/adc_api/boards/slwrb4180a.overlay @@ -4,7 +4,6 @@ * Copyright (c) 2025 Silicon Laboratories Inc. */ -#include #include / { @@ -25,6 +24,7 @@ &adc0 { pinctrl-0 = <&adc0_default>; pinctrl-names = "default"; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; #address-cells = <1>; #size-cells = <0>; status = "okay"; @@ -48,3 +48,7 @@ zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/samples/drivers/adc/adc_dt/boards/xg24_rb4187c.overlay b/tests/drivers/adc/adc_api/boards/xg23_rb4210a.overlay similarity index 89% rename from samples/drivers/adc/adc_dt/boards/xg24_rb4187c.overlay rename to tests/drivers/adc/adc_api/boards/xg23_rb4210a.overlay index 6ab6c25219f25..e265783b27719 100644 --- a/samples/drivers/adc/adc_dt/boards/xg24_rb4187c.overlay +++ b/tests/drivers/adc/adc_api/boards/xg23_rb4210a.overlay @@ -4,7 +4,6 @@ * Copyright (c) 2025 Silicon Laboratories Inc. */ -#include #include / { @@ -25,6 +24,7 @@ &adc0 { pinctrl-0 = <&adc0_default>; pinctrl-names = "default"; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; #address-cells = <1>; #size-cells = <0>; status = "okay"; @@ -45,6 +45,10 @@ zephyr,vref-mv = <3300>; zephyr,acquisition-time = ; zephyr,resolution = <12>; - zephyr,input-positive = ; + zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_api/boards/xg24_rb4187c.overlay b/tests/drivers/adc/adc_api/boards/xg24_rb4187c.overlay index b164be5e20e75..02c089e3710fa 100644 --- a/tests/drivers/adc/adc_api/boards/xg24_rb4187c.overlay +++ b/tests/drivers/adc/adc_api/boards/xg24_rb4187c.overlay @@ -16,6 +16,7 @@ #address-cells = <1>; #size-cells = <0>; status = "okay"; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; channel@0 { reg = <0>; @@ -35,3 +36,7 @@ zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_api/boards/xg27_dk2602a.overlay b/tests/drivers/adc/adc_api/boards/xg27_dk2602a.overlay index 9f648dfeb97a8..006a206963bfe 100644 --- a/tests/drivers/adc/adc_api/boards/xg27_dk2602a.overlay +++ b/tests/drivers/adc/adc_api/boards/xg27_dk2602a.overlay @@ -8,29 +8,47 @@ / { zephyr,user { - io-channels = <&adc0 0>, <&adc0 1>; + io-channels = <&adc0 3>, <&adc0 4>; + }; +}; + +&pinctrl { + adc0_default: adc0_default { + group0 { + /* Allocate odd bus 0 on GPIO port B to IADC for access to pin PB1 */ + silabs,analog-bus = ; + }; }; }; &adc0 { + pinctrl-0 = <&adc0_default>; + pinctrl-names = "default"; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; #address-cells = <1>; #size-cells = <0>; + status = "okay"; - channel@0 { - reg = <0>; + channel@3 { + reg = <3>; zephyr,gain = "ADC_GAIN_1"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = ; zephyr,resolution = <12>; - zephyr,input-positive = ; + zephyr,input-positive = ; }; - channel@1 { - reg = <1>; + channel@4 { + reg = <4>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,vref-mv = <3300>; zephyr,acquisition-time = ; zephyr,resolution = <12>; - zephyr,input-positive = ; + zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_api/boards/xg29_rb4412a.overlay b/tests/drivers/adc/adc_api/boards/xg29_rb4412a.overlay index 76f95bb2ae849..3b62758ee6562 100644 --- a/tests/drivers/adc/adc_api/boards/xg29_rb4412a.overlay +++ b/tests/drivers/adc/adc_api/boards/xg29_rb4412a.overlay @@ -16,6 +16,7 @@ status = "okay"; #address-cells = <1>; #size-cells = <0>; + dmas = <&dma0 DMA_REQSEL_IADC0IADC_SCAN>; channel@0 { reg = <0>; @@ -35,3 +36,7 @@ zephyr,input-positive = ; }; }; + +&dma0 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_api/overlay-dma-silabs.conf b/tests/drivers/adc/adc_api/overlay-dma-silabs.conf new file mode 100644 index 0000000000000..a28c72dbdb476 --- /dev/null +++ b/tests/drivers/adc/adc_api/overlay-dma-silabs.conf @@ -0,0 +1,9 @@ +# +# Copyright (c) 2025 Silicon Laboratories Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_TEST_USERSPACE=n +CONFIG_DMA=y +CONFIG_ADC_SILABS_IADC_DMA=y diff --git a/tests/drivers/adc/adc_api/testcase.yaml b/tests/drivers/adc/adc_api/testcase.yaml index fa59a01dd5cb0..e6482a065239c 100644 --- a/tests/drivers/adc/adc_api/testcase.yaml +++ b/tests/drivers/adc/adc_api/testcase.yaml @@ -89,3 +89,32 @@ tests: - dma platform_allow: - esp32s3_devkitc/esp32s3/procpu + drivers.adc.dma_silabs: + extra_args: + - EXTRA_CONF_FILE="overlay-dma-silabs.conf" + depends_on: + - adc + - dma + platform_allow: + - slwrb4180a + - sltb010a + - xg23_rb4210a + - xg24_rb4187c + - xg27_dk2602a + - bg29_rb4420a + - xg29_rb4412a + drivers.adc.dma_silabs_async: + extra_args: + - EXTRA_CONF_FILE="overlay-dma-silabs.conf" + - CONFIG_ADC_ASYNC=y + depends_on: + - adc + - dma + platform_allow: + - slwrb4180a + - sltb010a + - xg23_rb4210a + - xg24_rb4187c + - xg27_dk2602a + - bg29_rb4420a + - xg29_rb4412a diff --git a/tests/drivers/adc/adc_error_cases/boards/sltb010a.overlay b/tests/drivers/adc/adc_error_cases/boards/sltb010a.overlay new file mode 100644 index 0000000000000..707d618084d3d --- /dev/null +++ b/tests/drivers/adc/adc_error_cases/boards/sltb010a.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + adc = &adc0; + }; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/tests/drivers/adc/adc_error_cases/boards/slwrb4180a.overlay b/tests/drivers/adc/adc_error_cases/boards/slwrb4180a.overlay new file mode 100644 index 0000000000000..707d618084d3d --- /dev/null +++ b/tests/drivers/adc/adc_error_cases/boards/slwrb4180a.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + adc = &adc0; + }; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/tests/drivers/adc/adc_error_cases/boards/xg23_rb4210a.overlay b/tests/drivers/adc/adc_error_cases/boards/xg23_rb4210a.overlay new file mode 100644 index 0000000000000..038d8dd5f89bc --- /dev/null +++ b/tests/drivers/adc/adc_error_cases/boards/xg23_rb4210a.overlay @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Silicon Laboratories Inc. + */ + +/ { + aliases { + adc = &adc0; + }; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/tests/drivers/adc/adc_error_cases/testcase.yaml b/tests/drivers/adc/adc_error_cases/testcase.yaml index 57513c78e156b..dce0725217d85 100644 --- a/tests/drivers/adc/adc_error_cases/testcase.yaml +++ b/tests/drivers/adc/adc_error_cases/testcase.yaml @@ -12,7 +12,10 @@ tests: - nrf54lm20dk/nrf54lm20a/cpuapp - nrf54h20dk/nrf54h20/cpuapp - ophelia4ev/nrf54l15/cpuapp + - sltb010a + - xg23_rb4210a - xg24_rb4187c - xg27_dk2602a - xg29_rb4412a - bg29_rb4420a + - slwrb4180a