From b0389ce5b5567c525a281f36bac0ea7d55b8262f Mon Sep 17 00:00:00 2001 From: Karthikeyan Krishnasamy Date: Mon, 9 Dec 2024 11:30:39 +0530 Subject: [PATCH] drivers: adc: add ads131m02 adc driver ADS131M02 is Texas Instruments 2-channel, 24-Bit differential input ADC which support wide range datarate. Driver add support for adc read, channel configure, adc sampling mode configuration and power management. [1]. https://www.ti.com/lit/ds/symlink/ads131m02.pdf Signed-off-by: Karthikeyan Krishnasamy --- drivers/adc/CMakeLists.txt | 1 + drivers/adc/Kconfig | 2 + drivers/adc/Kconfig.ads131m02 | 13 + drivers/adc/adc_ads131m02.c | 733 ++++++++++++++++++ include/zephyr/drivers/adc/ads131m02.h | 48 ++ .../build_all/adc/boards/native_sim.overlay | 9 + 6 files changed, 806 insertions(+) create mode 100644 drivers/adc/Kconfig.ads131m02 create mode 100644 drivers/adc/adc_ads131m02.c create mode 100644 include/zephyr/drivers/adc/ads131m02.h diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 6258566351190d..f0e5707e3ff5c9 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_ADC_ADS1112 adc_ads1112.c) zephyr_library_sources_ifdef(CONFIG_ADC_ADS1119 adc_ads1119.c) zephyr_library_sources_ifdef(CONFIG_ADC_ADS7052 adc_ads7052.c) zephyr_library_sources_ifdef(CONFIG_ADC_ADS114S0X adc_ads114s0x.c) +zephyr_library_sources_ifdef(CONFIG_ADC_ADS131M02 adc_ads131m02.c) zephyr_library_sources_ifdef(CONFIG_ADC_RPI_PICO adc_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_ADC_XMC4XXX adc_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_ADC_ESP32 adc_esp32.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 2d467a9399f69b..ceaa8611572136 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -102,6 +102,8 @@ source "drivers/adc/Kconfig.ads7052" source "drivers/adc/Kconfig.ads114s0x" +source "drivers/adc/Kconfig.ads131m02" + source "drivers/adc/Kconfig.rpi_pico" source "drivers/adc/Kconfig.xmc4xxx" diff --git a/drivers/adc/Kconfig.ads131m02 b/drivers/adc/Kconfig.ads131m02 new file mode 100644 index 00000000000000..23e44f4d53f591 --- /dev/null +++ b/drivers/adc/Kconfig.ads131m02 @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Linumiz +# +# SPDX-License-Identifier: Apache-2.0 + +config ADC_ADS131M02 + bool "Texas instruments ADS131M02" + default y + depends on DT_HAS_TI_ADS131M02_ENABLED + select ADC_CONFIGURABLE_INPUTS + select SPI + select GPIO + help + Enable the driver for ADS131M02 ADC. diff --git a/drivers/adc/adc_ads131m02.c b/drivers/adc/adc_ads131m02.c new file mode 100644 index 00000000000000..bda053840ef9bd --- /dev/null +++ b/drivers/adc/adc_ads131m02.c @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2024 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +LOG_MODULE_REGISTER(ads131m02, CONFIG_ADC_LOG_LEVEL); + +#define ADS131M02_DEVICE_ID 0x22 + +/* Device settings Registers */ +#define ADS131M02_ID_REG 0x00 +#define ADS131M02_STATUS_REG 0x01 + +/* Global settings Registers */ +#define ADS131M02_MODE_REG 0x02 +#define ADS131M02_CLOCK_REG 0x03 +#define ADS131M02_GAIN_REG 0x04 +#define ADS131M02_CFG_REG 0x06 +#define ADS131M02_THRESH_MSB_REG 0x07 +#define ADS131M02_THRESH_LSB_REG 0x08 + +/* Channel 0 settings Registers */ +#define ADS131M02_CH0_CFG_REG 0x09 +#define ADS131M02_CH0_OCAL_MSB_REG 0x0A +#define ADS131M02_CH0_OCAL_LSB_REG 0x0B +#define ADS131M02_CH0_GCAL_MSB_REG 0x0C +#define ADS131M02_CH0_GCAL_LSB_REG 0x0D + +/* Channel 1 settings Registers */ +#define ADS131M02_CH1_CFG_REG 0x0E +#define ADS131M02_CH1_OCAL_MSB_REG 0x0F +#define ADS131M02_CH1_OCAL_LSB_REG 0x10 +#define ADS131M02_CH1_GCAL_MSB_REG 0x11 +#define ADS131M02_CH1_GCAL_LSB_REG 0x12 + +/* Register Map CRC Registers */ +#define ADS131M02_REGMAP_CRC_REG 0x3E + +#define ADC_CHANNEL_0 0 +#define ADC_CHANNEL_1 1 +#define ADS131M02_REF_INTERNAL 1200 +#define ADS131M02_RESOLUTION 24 + +/* ADS131M02 cmds */ +#define ADS131M02_NULL_CMD 0x0000 +#define ADS131M02_RESET_CMD 0x0011 +#define ADS131M02_STANDBY_CMD 0x0022 +#define ADS131M02_WAKEUP_CMD 0x0033 +#define ADS131M02_LOCK_CMD 0x0555 +#define ADS131M02_UNLOCK_CMD 0x0655 +#define ADS131M02_RREG_CMD 0xA000 +#define ADS131M02_WREG_CMD 0x6000 + +#define ADS131M02_RESET_RSP 0xFF22 + +#define ADS131M02_GAIN0_MASK GENMASK(2, 0) +#define ADS131M02_GAIN1_MASK GENMASK(6, 4) +#define ADS131M02_CHANNEL0_ENABLE BIT(8) +#define ADS131M02_CHANNEL1_ENABLE BIT(9) +#define ADS131M02_DRDY_CH0_MASK BIT(0) +#define ADS131M02_DRDY_CH1_MASK BIT(1) +#define ADS131M02_OSR_256_MASK BIT(2) +#define ADS131M02_OSR_512_MASK BIT(3) +#define ADS131M02_OSR_1024_MASK BIT(3) | BIT(2) +#define ADS131M02_OSR_2048_MASK BIT(4) +#define ADS131M02_OSR_4096_MASK BIT(4) | BIT(2) +#define ADS131M02_OSR_8192_MASK BIT(4) | BIT(3) +#define ADS131M02_OSR_16384_MASK BIT(4) | BIT(3) | BIT(2) +#define ADS131M02_GC_MODE_MASK BIT(8) +#define ADS131M02_GC_DELAY_MASK GENMASK(12, 9) +#define ADS131M02_PWR_HR BIT(1) | BIT(0) +#define ADS131M02_PWR_LP BIT(0) + +#define ADS131M02_DISABLE_ADC 0x000E +#define ADS131M02_RESET_DELAY 100 + +#define ADS131M02_GAIN_1 0 +#define ADS131M02_GAIN_2 1 +#define ADS131M02_GAIN_4 2 +#define ADS131M02_GAIN_8 3 +#define ADS131M02_GAIN_16 4 +#define ADS131M02_GAIN_32 5 +#define ADS131M02_GAIN_64 6 +#define ADS131M02_GAIN_128 7 + +#define ADS131M02_GET_GAIN(channel_id, gain) \ + FIELD_PREP(channel_id == 0 ? ADS131M02_GAIN0_MASK : \ + ADS131M02_GAIN1_MASK, gain) + +enum ads131m02_data_rate { + /* SPS */ + ADS131M02_DR_250, + ADS131M02_DR_500, + ADS131M02_DR_1k, + ADS131M02_DR_2k, + ADS131M02_DR_4k, + ADS131M02_DR_8k, + ADS131M02_DR_16k, + ADS131M02_DR_32k, +}; + +struct ads131m02_config { + const struct spi_dt_spec spi; + const struct gpio_dt_spec gpio_drdy; +}; + +struct ads131m02_data { + struct adc_context ctx; + struct k_sem acq_sem; + struct k_sem drdy_sem; + struct gpio_callback callback_drdy; + int32_t *buffer; + int32_t *buffer_ptr; +}; + +static inline int ads131m02_transceive(const struct device *dev, + uint8_t *send_buf, size_t send_buf_len, + uint8_t *recv_buf, size_t recv_buf_len) +{ + int ret; + const struct ads131m02_config *cfg = dev->config; + + struct spi_buf tx_buf = { + .buf = send_buf, + .len = send_buf_len, + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + struct spi_buf rx_buf = { + .buf = recv_buf, + .len = recv_buf_len, + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1, + }; + + ret = spi_transceive_dt(&cfg->spi, &tx, NULL); + if (ret != 0) { + return ret; + } + + return spi_read_dt(&cfg->spi, &rx); +} + +static int ads131m02_reg_read(const struct device *dev, uint16_t addr, + uint8_t *read_buf, size_t read_buf_len) +{ + uint16_t temp; + uint8_t tx_buf[3] = {0}; + + temp = (uint16_t)(ADS131M02_RREG_CMD | (addr << 7)); + sys_put_be16(temp, tx_buf); + + return ads131m02_transceive(dev, tx_buf, sizeof(tx_buf), + read_buf, read_buf_len); +} + +static int ads131m02_reg_write(const struct device *dev, uint16_t addr, + uint16_t write_data) +{ + uint16_t temp; + uint8_t tx_buf[6] = {0}; + uint8_t rx_buf[3] = {0}; + + temp = (uint16_t)(ADS131M02_WREG_CMD | (addr << 7)); + sys_put_be16(temp, tx_buf); + sys_put_be16(write_data, &tx_buf[3]); + + return ads131m02_transceive(dev, tx_buf, sizeof(tx_buf), + rx_buf, sizeof(rx_buf)); + +} + +static inline int ads131m02_configure_gain(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + uint16_t gain_cfg; + + switch (channel_cfg->gain) { + case ADC_GAIN_1: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_1); + break; + case ADC_GAIN_2: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_2); + break; + case ADC_GAIN_4: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_4); + break; + case ADC_GAIN_8: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_8); + break; + case ADC_GAIN_16: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_16); + break; + case ADC_GAIN_32: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_32); + break; + case ADC_GAIN_64: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_64); + break; + case ADC_GAIN_128: + gain_cfg = ADS131M02_GET_GAIN(channel_cfg->channel_id, + ADS131M02_GAIN_128); + break; + default: + return -EINVAL; + } + + return ads131m02_reg_write(dev, ADS131M02_GAIN_REG, gain_cfg); +} + +static inline int ads131m02_acquistion_time(uint16_t acq_time, uint16_t *enable) +{ + uint16_t acq_value = ADC_ACQ_TIME_VALUE(acq_time); + + if (acq_time == ADC_ACQ_TIME_DEFAULT) { + *enable |= ADS131M02_OSR_1024_MASK; + return 0; + } + + if (ADC_ACQ_TIME_UNIT(acq_time) != ADC_ACQ_TIME_TICKS) { + return -EINVAL; + } + + if (acq_time == ADC_ACQ_TIME_MAX) { + *enable |= ADS131M02_OSR_16384_MASK; + return 0; + } + + switch (acq_value) { + case ADS131M02_DR_250: + *enable |= ADS131M02_OSR_16384_MASK; + break; + case ADS131M02_DR_500: + *enable |= ADS131M02_OSR_8192_MASK; + break; + case ADS131M02_DR_1k: + *enable |= ADS131M02_OSR_4096_MASK; + break; + case ADS131M02_DR_2k: + *enable |= ADS131M02_OSR_2048_MASK; + break; + case ADS131M02_DR_4k: + *enable |= ADS131M02_OSR_1024_MASK; + break; + case ADS131M02_DR_8k: + *enable |= ADS131M02_OSR_512_MASK; + break; + case ADS131M02_DR_16k: + *enable |= ADS131M02_OSR_256_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ads131m02_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + int ret; + uint16_t enable; + uint8_t read_data[3] = {0}; + + ret = ads131m02_reg_read(dev, ADS131M02_CLOCK_REG, read_data, + sizeof(read_data)); + if (ret != 0) { + return ret; + } + + enable = sys_get_be16(read_data); + switch (channel_cfg->channel_id) { + case ADC_CHANNEL_0: + enable |= ADS131M02_CHANNEL0_ENABLE; + break; + case ADC_CHANNEL_1: + enable |= ADS131M02_CHANNEL1_ENABLE; + break; + default: + return -EINVAL; + } + + enable &= ~(ADS131M02_OSR_16384_MASK); + ret = ads131m02_acquistion_time(channel_cfg->acquisition_time, &enable); + if (ret < 0) { + return ret; + } + + return ads131m02_reg_write(dev, ADS131M02_CLOCK_REG, enable); +} + +static int ads131m02_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + int ret; + + if (channel_cfg->channel_id != 0 && channel_cfg->channel_id != 1) { + return -EINVAL; + } + + if (channel_cfg->reference != ADC_REF_INTERNAL) { + LOG_DBG("Unsupported Reference Voltage"); + return -ENOTSUP; + } + + if (!channel_cfg->differential) { + return -EINVAL; + } + + ret = ads131m02_configure_gain(dev, channel_cfg); + if (ret != 0) { + return ret; + } + + ret = ads131m02_setup(dev, channel_cfg); + if (ret != 0) { + return ret; + } + + return 0; +} + +static int ads131m02_validate_buffer_size(const struct adc_sequence *sequence) +{ + size_t needed = sizeof(int32_t); + + if (sequence->options) { + needed *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed) { + return -ENOMEM; + } + + return 0; +} + +static int ads131m02_validate_sequence(const struct adc_sequence *sequence) +{ + if (sequence->resolution != ADS131M02_RESOLUTION) { + return -EINVAL; + } + + if (sequence->channels != BIT(0) && sequence->channels != BIT(1)) { + LOG_ERR("invalid channel"); + return -EINVAL; + } + + if (sequence->oversampling) { + return -EINVAL; + } + + return ads131m02_validate_buffer_size(sequence); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat_sampling) +{ + struct ads131m02_data *data = CONTAINER_OF(ctx, struct ads131m02_data, ctx); + + if (repeat_sampling) { + data->buffer = data->buffer_ptr; + } +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct ads131m02_data *data = CONTAINER_OF(ctx, struct ads131m02_data, ctx); + + data->buffer_ptr = data->buffer; + k_sem_give(&data->acq_sem); +} + +static int ads131m02_adc_start_read(const struct device *dev, + const struct adc_sequence *sequence, + bool wait) +{ + int ret; + struct ads131m02_data *data = dev->data; + + ret = ads131m02_validate_sequence(sequence); + if (ret != 0) { + LOG_ERR("sequence validation failed"); + return ret; + } + + data->buffer = sequence->buffer; + adc_context_start_read(&data->ctx, sequence); + if (wait) { + ret = adc_context_wait_for_completion(&data->ctx); + } + + return ret; +} + +static int ads131m02_wait_drdy(const struct device *dev) +{ + struct ads131m02_data *data = dev->data; + + return k_sem_take(&data->drdy_sem, + ADC_CONTEXT_WAIT_FOR_COMPLETION_TIMEOUT); +} + +static int ads131m02_read_sample(const struct device *dev, + uint32_t channels, uint32_t *buffer) +{ + int ret; + uint16_t int_status; + uint8_t tx_buf[4] = {0}; + uint8_t rx_buf[12] = {0}; + + ret = ads131m02_transceive(dev, tx_buf, sizeof(tx_buf), + rx_buf, sizeof(rx_buf)); + if (ret != 0) { + return ret; + } + + int_status = sys_get_be16(&rx_buf[0]); + if ((int_status & ADS131M02_DRDY_CH0_MASK) && (channels & BIT(0))) { + *buffer = sys_get_be24(&rx_buf[3]); + } else if ((int_status & ADS131M02_DRDY_CH1_MASK) && + (channels & BIT(1))) { + *buffer = sys_get_be24(&rx_buf[6]); + } else { + LOG_INF("No ADC Data Available"); + } + + return 0; +} + +static int ads131m02_perform_read(const struct device *dev, + const struct adc_sequence *sequence) +{ + int ret; + struct ads131m02_data *data = dev->data; + + k_sem_take(&data->acq_sem, K_FOREVER); + k_sem_reset(&data->drdy_sem); + + ret = ads131m02_wait_drdy(dev); + if (ret != 0) { + goto error; + } + + ret = ads131m02_read_sample(dev, sequence->channels, data->buffer); + if (ret != 0) { + goto error; + } + + data->buffer++; + adc_context_on_sampling_done(&data->ctx, dev); + + return 0; +error: + adc_context_complete(&data->ctx, ret); + return ret; +} + +static int ads131m02_read(const struct device *dev, + const struct adc_sequence *seq) +{ + int ret; + struct ads131m02_data *data = dev->data; + + adc_context_lock(&data->ctx, false, NULL); + ret = ads131m02_adc_start_read(dev, seq, false); + + while (ret == 0 && k_sem_take(&data->ctx.sync, K_NO_WAIT) != 0) { + ret = ads131m02_perform_read(dev, seq); + } + + adc_context_release(&data->ctx, ret); + + return ret; +} + +static void ads131m02_data_ready_handler(const struct device *dev, + struct gpio_callback *gpio_cb, + uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pins); + + struct ads131m02_data *data = CONTAINER_OF(gpio_cb, + struct ads131m02_data, callback_drdy); + + k_sem_give(&data->drdy_sem); +} + +static int ads131m02_configure_gpio(const struct device *dev) +{ + int ret; + const struct ads131m02_config *cfg = dev->config; + struct ads131m02_data *data = dev->data; + + ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT); + if (ret != 0) { + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, + GPIO_INT_EDGE_TO_ACTIVE); + if (ret != 0) { + return ret; + } + + gpio_init_callback(&data->callback_drdy, ads131m02_data_ready_handler, + BIT(cfg->gpio_drdy.pin)); + + return gpio_add_callback(cfg->gpio_drdy.port, &data->callback_drdy); +} + +static int ads131m02_device_reset(const struct device *dev) +{ + int ret; + uint8_t tx_buf[12] = {0}; + uint8_t rx_buf[3] = {0}; + + sys_put_be16(ADS131M02_RESET_CMD, tx_buf); + ret = ads131m02_transceive(dev, tx_buf, sizeof(tx_buf), + rx_buf, sizeof(rx_buf)); + + if (ret != 0) { + return ret; + } + + if (sys_get_be16(rx_buf) != ADS131M02_RESET_RSP) { + return -EIO; + } + + k_msleep(ADS131M02_RESET_DELAY); + + return 0; +} + +#if defined CONFIG_PM_DEVICE +static int ads131m02_pm(const struct device *dev, uint16_t cmd) +{ + int ret; + uint8_t tx_buf[3] = {0}; + uint8_t rx_buf[3] = {0}; + + sys_put_be16(cmd, tx_buf); + ret = ads131m02_transceive(dev, tx_buf, sizeof(tx_buf), + rx_buf, sizeof(rx_buf)); + if (ret != 0) { + return ret; + } + + if (rx_buf[1] != cmd) { + return -EIO; + } + + return 0; +} + +static int ads131m02_pm_action(const struct device *dev, + enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_RESUME: + return ads131m02_pm(dev, ADS131M02_WAKEUP_CMD); + case PM_DEVICE_ACTION_SUSPEND: + return ads131m02_pm(dev, ADS131M02_STANDBY_CMD); + default: + return -EINVAL; + } +} +#endif /* CONFIG_PM_DEVICE */ + +int ads131m02_set_adc_mode(const struct device *dev, + enum ads131m02_adc_mode mode, + enum ads131m02_gc_delay gc_delay) +{ + uint16_t temp = 0; + + switch (mode) { + case ADS131M02_CONTINUOUS_MODE: + break; + case ADS131M02_GLOBAL_CHOP_MODE: + temp |= ADS131M02_GC_MODE_MASK; + temp |= FIELD_PREP(ADS131M02_GC_DELAY_MASK, gc_delay); + break; + default: + return -EINVAL; + } + + return ads131m02_reg_write(dev, ADS131M02_CFG_REG, temp); +} + +int ads131m02_set_power_mode(const struct device *dev, + enum ads131m02_adc_power_mode mode) +{ + int ret; + uint16_t temp; + uint8_t buf[3] = {0}; + + ret = ads131m02_reg_read(dev, ADS131M02_CLOCK_REG, buf, sizeof(buf)); + if (ret != 0) { + return ret; + } + + temp = sys_get_be16(buf); + temp &= ~(ADS131M02_PWR_HR); + + switch (mode) { + case ADS131M02_VLP: + break; + case ADS131M02_LP: + temp |= ADS131M02_PWR_LP; + break; + case ADS131M02_HR: + temp |= ADS131M02_PWR_HR; + break; + default: + return -EINVAL; + } + + return ads131m02_reg_write(dev, ADS131M02_CLOCK_REG, temp); +} + +static const struct adc_driver_api ads131m02_api = { + .channel_setup = ads131m02_channel_setup, + .read = ads131m02_read, + .ref_internal = ADS131M02_REF_INTERNAL, +}; + +static int ads131m02_init(const struct device *dev) +{ + int ret; + uint8_t buf[3] = {0}; + const struct ads131m02_config *cfg = dev->config; + struct ads131m02_data *data = dev->data; + + if (!spi_is_ready_dt(&cfg->spi)) { + LOG_ERR("ADS131M02 is not ready"); + return -ENODEV; + } + + adc_context_init(&data->ctx); + k_sem_init(&data->acq_sem, 0, 1); + k_sem_init(&data->drdy_sem, 0, 1); + + ret = ads131m02_configure_gpio(dev); + if (ret != 0) { + LOG_ERR("GPIO config failed %d", ret); + return ret; + } + + ret = ads131m02_reg_read(dev, ADS131M02_ID_REG, buf, sizeof(buf)); + if (ret != 0) { + return ret; + } + + if (buf[0] != ADS131M02_DEVICE_ID) { + LOG_ERR("Device ID mismatch %d", buf[0]); + return -ENODEV; + } + + ret = ads131m02_device_reset(dev); + if (ret != 0) { + LOG_WRN("Device is not reset"); + } + + /* By default, adc is configured, so disabling it */ + ret = ads131m02_reg_write(dev, ADS131M02_CLOCK_REG, + ADS131M02_DISABLE_ADC); + if (ret != 0) { + return ret; + } + + adc_context_unlock_unconditionally(&data->ctx); + +#if defined CONFIG_PM_DEVICE + ret = ads131m02_pm(dev, ADS131M02_STANDBY_CMD); + if (ret != 0) { + return ret; + } + + pm_device_init_suspended(dev); +#endif /* CONFIG_PM_DEVICE */ + + LOG_INF("ADS131M02 Initialised"); + + return 0; +} + +#define DT_DRV_COMPAT ti_ads131m02 + +#define ADC_ADS131M02_INST_DEFINE(n) \ + PM_DEVICE_DT_INST_DEFINE(n, ads131m02_pm_action); \ + static const struct ads131m02_config config_##n = { \ + .spi = SPI_DT_SPEC_INST_GET( \ + n, SPI_OP_MODE_MASTER | SPI_MODE_CPHA | \ + SPI_WORD_SET(8), 0), \ + .gpio_drdy = GPIO_DT_SPEC_INST_GET(n, drdy_gpios), \ + }; \ + static struct ads131m02_data data_##n; \ + DEVICE_DT_INST_DEFINE(n, ads131m02_init, \ + PM_DEVICE_DT_INST_GET(n), \ + &data_##n, &config_##n, POST_KERNEL, \ + CONFIG_ADC_INIT_PRIORITY, &ads131m02_api); + +DT_INST_FOREACH_STATUS_OKAY(ADC_ADS131M02_INST_DEFINE) diff --git a/include/zephyr/drivers/adc/ads131m02.h b/include/zephyr/drivers/adc/ads131m02.h new file mode 100644 index 00000000000000..978684774025d9 --- /dev/null +++ b/include/zephyr/drivers/adc/ads131m02.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_ADC_ADS131M02_H_ +#define ZEPHYR_INCLUDE_DRIVERS_ADC_ADS131M02_H_ + +#include + +enum ads131m02_adc_mode { + ADS131M02_CONTINUOUS_MODE, /* Continuous conversion mode */ + ADS131M02_GLOBAL_CHOP_MODE /* Global chop mode */ +}; + +enum ads131m02_adc_power_mode { + ADS131M02_VLP, /* Very Low Power */ + ADS131M02_LP, /* Low Power */ + ADS131M02_HR /* High Resolution */ +}; + +enum ads131m02_gc_delay { + ADS131M02_GC_DELAY_2, + ADS131M02_GC_DELAY_4, + ADS131M02_GC_DELAY_8, + ADS131M02_GC_DELAY_16, + ADS131M02_GC_DELAY_32, + ADS131M02_GC_DELAY_64, + ADS131M02_GC_DELAY_128, + ADS131M02_GC_DELAY_256, + ADS131M02_GC_DELAY_512, + ADS131M02_GC_DELAY_1024, + ADS131M02_GC_DELAY_2048, + ADS131M02_GC_DELAY_4096, + ADS131M02_GC_DELAY_8192, + ADS131M02_GC_DELAY_16384, + ADS131M02_GC_DELAY_32768, + ADS131M02_GC_DELAY_65536 +}; + +int ads131m02_set_adc_mode(const struct device *dev, enum ads131m02_adc_mode mode, + enum ads131m02_gc_delay gc_delay); + +int ads131m02_set_power_mode(const struct device *dev, + enum ads131m02_adc_power_mode mode); + +#endif diff --git a/tests/drivers/build_all/adc/boards/native_sim.overlay b/tests/drivers/build_all/adc/boards/native_sim.overlay index 662b785f57a141..7c5f31455380ed 100644 --- a/tests/drivers/build_all/adc/boards/native_sim.overlay +++ b/tests/drivers/build_all/adc/boards/native_sim.overlay @@ -162,6 +162,7 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, + <&test_gpio 0 0>, <&test_gpio 0 0>; test_spi_mcp3204: mcp3204@0 { @@ -362,6 +363,14 @@ #io-channel-cells = <1>; }; }; + + test_spi_ads131m02: ads131m02@17 { + compatible = "ti,ads131m02"; + reg = <0x17>; + spi-max-frequency = <0>; + #io-channel-cells = <1>; + drdy-gpios = <&test_gpio 0 0>; + }; }; }; };