Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

733 lines
16 KiB

/*
* Copyright (c) 2024 Linumiz
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/drivers/adc/ads131m02.h>
#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 DEVICE_API(adc, 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)