iio: adc: Add driver for ADS7128 / ADS7138
Add driver for ADS7128 and ADS7138 12-bit, 8-channel analog-to-digital converters. These ADCs have a wide operating range and a wide feature set. Communication is based on the I2C interface. ADS7128 differs in the addition of further hardware features, like a root-mean-square (RMS) and a zero-crossing-detect (ZCD) module. Signed-off-by: Tobias Sperling <tobias.sperling@softing.com> Link: https://patch.msgid.link/20250213-adc_ml-v4-2-66b68f8fdb8c@softing.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
committed by
Jonathan Cameron
parent
f3255b0e1c
commit
024b08fee3
@@ -1499,6 +1499,16 @@ config TI_ADS1119
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads1119.
|
||||
|
||||
config TI_ADS7138
|
||||
tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADS7128 and
|
||||
ADS7138 8-channel A/D converters with 12-bit resolution.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads7138.
|
||||
|
||||
config TI_ADS7924
|
||||
tristate "Texas Instruments ADS7924 ADC"
|
||||
depends on I2C
|
||||
|
||||
@@ -135,6 +135,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
|
||||
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
||||
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
|
||||
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
|
||||
obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
|
||||
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
|
||||
|
||||
749
drivers/iio/adc/ti-ads7138.c
Normal file
749
drivers/iio/adc/ti-ads7138.c
Normal file
@@ -0,0 +1,749 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* ADS7138 - Texas Instruments Analog-to-Digital Converter
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
||||
/*
|
||||
* Always assume 16 bits resolution as HW registers are aligned like that and
|
||||
* with enabled oversampling/averaging it actually corresponds to 16 bits.
|
||||
*/
|
||||
#define ADS7138_RES_BITS 16
|
||||
|
||||
/* ADS7138 operation codes */
|
||||
#define ADS7138_OPCODE_SINGLE_WRITE 0x08
|
||||
#define ADS7138_OPCODE_SET_BIT 0x18
|
||||
#define ADS7138_OPCODE_CLEAR_BIT 0x20
|
||||
#define ADS7138_OPCODE_BLOCK_WRITE 0x28
|
||||
#define ADS7138_OPCODE_BLOCK_READ 0x30
|
||||
|
||||
/* ADS7138 registers */
|
||||
#define ADS7138_REG_GENERAL_CFG 0x01
|
||||
#define ADS7138_REG_OSR_CFG 0x03
|
||||
#define ADS7138_REG_OPMODE_CFG 0x04
|
||||
#define ADS7138_REG_SEQUENCE_CFG 0x10
|
||||
#define ADS7138_REG_AUTO_SEQ_CH_SEL 0x12
|
||||
#define ADS7138_REG_ALERT_CH_SEL 0x14
|
||||
#define ADS7138_REG_EVENT_FLAG 0x18
|
||||
#define ADS7138_REG_EVENT_HIGH_FLAG 0x1A
|
||||
#define ADS7138_REG_EVENT_LOW_FLAG 0x1C
|
||||
#define ADS7138_REG_HIGH_TH_HYS_CH(x) ((x) * 4 + 0x20)
|
||||
#define ADS7138_REG_LOW_TH_CNT_CH(x) ((x) * 4 + 0x22)
|
||||
#define ADS7138_REG_MAX_LSB_CH(x) ((x) * 2 + 0x60)
|
||||
#define ADS7138_REG_MIN_LSB_CH(x) ((x) * 2 + 0x80)
|
||||
#define ADS7138_REG_RECENT_LSB_CH(x) ((x) * 2 + 0xA0)
|
||||
|
||||
#define ADS7138_GENERAL_CFG_RST BIT(0)
|
||||
#define ADS7138_GENERAL_CFG_DWC_EN BIT(4)
|
||||
#define ADS7138_GENERAL_CFG_STATS_EN BIT(5)
|
||||
#define ADS7138_OSR_CFG_MASK GENMASK(2, 0)
|
||||
#define ADS7138_OPMODE_CFG_CONV_MODE BIT(5)
|
||||
#define ADS7138_OPMODE_CFG_FREQ_MASK GENMASK(4, 0)
|
||||
#define ADS7138_SEQUENCE_CFG_SEQ_MODE BIT(0)
|
||||
#define ADS7138_SEQUENCE_CFG_SEQ_START BIT(4)
|
||||
#define ADS7138_THRESHOLD_LSB_MASK GENMASK(7, 4)
|
||||
|
||||
enum ads7138_modes {
|
||||
ADS7138_MODE_MANUAL,
|
||||
ADS7138_MODE_AUTO,
|
||||
};
|
||||
|
||||
struct ads7138_chip_data {
|
||||
const char *name;
|
||||
const int channel_num;
|
||||
};
|
||||
|
||||
struct ads7138_data {
|
||||
/* Protects RMW access to the I2C interface */
|
||||
struct mutex lock;
|
||||
struct i2c_client *client;
|
||||
struct regulator *vref_regu;
|
||||
const struct ads7138_chip_data *chip_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* 2D array of available sampling frequencies and the corresponding register
|
||||
* values. Structured like this to be easily usable in read_avail function.
|
||||
*/
|
||||
static const int ads7138_samp_freqs_bits[2][26] = {
|
||||
{
|
||||
163, 244, 326, 488, 651, 977, 1302, 1953,
|
||||
2604, 3906, 5208, 7813, 10417, 15625, 20833, 31250,
|
||||
41667, 62500, 83333, 125000, 166667, 250000, 333333, 500000,
|
||||
666667, 1000000
|
||||
}, {
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
/* Here is a hole, due to duplicate frequencies */
|
||||
0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
|
||||
0x01, 0x00
|
||||
}
|
||||
};
|
||||
|
||||
static const int ads7138_oversampling_ratios[] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128
|
||||
};
|
||||
|
||||
static int ads7138_i2c_write_block(const struct i2c_client *client, u8 reg,
|
||||
u8 *values, u8 length)
|
||||
{
|
||||
int ret;
|
||||
int len = length + 2; /* "+ 2" for OPCODE and reg */
|
||||
|
||||
u8 *buf __free(kfree) = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
buf[0] = ADS7138_OPCODE_BLOCK_WRITE;
|
||||
buf[1] = reg;
|
||||
memcpy(&buf[2], values, length);
|
||||
|
||||
ret = i2c_master_send(client, buf, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != len)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads7138_i2c_write_with_opcode(const struct i2c_client *client,
|
||||
u8 reg, u8 regval, u8 opcode)
|
||||
{
|
||||
u8 buf[3] = { opcode, reg, regval };
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(buf))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads7138_i2c_write(const struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return ads7138_i2c_write_with_opcode(client, reg, value,
|
||||
ADS7138_OPCODE_SINGLE_WRITE);
|
||||
}
|
||||
|
||||
static int ads7138_i2c_set_bit(const struct i2c_client *client, u8 reg, u8 bits)
|
||||
{
|
||||
return ads7138_i2c_write_with_opcode(client, reg, bits,
|
||||
ADS7138_OPCODE_SET_BIT);
|
||||
}
|
||||
|
||||
static int ads7138_i2c_clear_bit(const struct i2c_client *client, u8 reg, u8 bits)
|
||||
{
|
||||
return ads7138_i2c_write_with_opcode(client, reg, bits,
|
||||
ADS7138_OPCODE_CLEAR_BIT);
|
||||
}
|
||||
|
||||
static int ads7138_i2c_read_block(const struct i2c_client *client, u8 reg,
|
||||
u8 *out_values, u8 length)
|
||||
{
|
||||
u8 buf[2] = { ADS7138_OPCODE_BLOCK_READ, reg };
|
||||
int ret;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = ARRAY_SIZE(buf),
|
||||
.buf = buf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = length,
|
||||
.buf = out_values,
|
||||
},
|
||||
};
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads7138_i2c_read(const struct i2c_client *client, u8 reg)
|
||||
{
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
ret = ads7138_i2c_read_block(client, reg, &value, sizeof(value));
|
||||
if (ret)
|
||||
return ret;
|
||||
return value;
|
||||
}
|
||||
|
||||
static int ads7138_freq_to_bits(int freq)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[0]); i++)
|
||||
if (freq == ads7138_samp_freqs_bits[0][i])
|
||||
return ads7138_samp_freqs_bits[1][i];
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ads7138_bits_to_freq(int bits)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[1]); i++)
|
||||
if (bits == ads7138_samp_freqs_bits[1][i])
|
||||
return ads7138_samp_freqs_bits[0][i];
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ads7138_osr_to_bits(int osr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads7138_oversampling_ratios); i++)
|
||||
if (osr == ads7138_oversampling_ratios[i])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ads7138_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
int ret, vref, bits;
|
||||
u8 values[2];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ads7138_i2c_read_block(data->client,
|
||||
ADS7138_REG_RECENT_LSB_CH(chan->channel),
|
||||
values, ARRAY_SIZE(values));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = get_unaligned_le16(values);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_PEAK:
|
||||
ret = ads7138_i2c_read_block(data->client,
|
||||
ADS7138_REG_MAX_LSB_CH(chan->channel),
|
||||
values, ARRAY_SIZE(values));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = get_unaligned_le16(values);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_TROUGH:
|
||||
ret = ads7138_i2c_read_block(data->client,
|
||||
ADS7138_REG_MIN_LSB_CH(chan->channel),
|
||||
values, ARRAY_SIZE(values));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = get_unaligned_le16(values);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bits = FIELD_GET(ADS7138_OPMODE_CFG_FREQ_MASK, ret);
|
||||
*val = ads7138_bits_to_freq(bits);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref = regulator_get_voltage(data->vref_regu);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
*val = vref / 1000;
|
||||
*val2 = ADS7138_RES_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_OSR_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bits = FIELD_GET(ADS7138_OSR_CFG_MASK, ret);
|
||||
*val = ads7138_oversampling_ratios[bits];
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ads7138_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
int bits, ret;
|
||||
u8 value;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ: {
|
||||
bits = ads7138_freq_to_bits(val);
|
||||
if (bits < 0)
|
||||
return bits;
|
||||
|
||||
guard(mutex)(&data->lock);
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
value = ret & ~ADS7138_OPMODE_CFG_FREQ_MASK;
|
||||
value |= FIELD_PREP(ADS7138_OPMODE_CFG_FREQ_MASK, bits);
|
||||
return ads7138_i2c_write(data->client, ADS7138_REG_OPMODE_CFG,
|
||||
value);
|
||||
}
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
bits = ads7138_osr_to_bits(val);
|
||||
if (bits < 0)
|
||||
return bits;
|
||||
|
||||
return ads7138_i2c_write(data->client, ADS7138_REG_OSR_CFG,
|
||||
bits);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ads7138_read_event(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info, int *val, int *val2)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
u8 reg, values[2];
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
reg = (dir == IIO_EV_DIR_RISING) ?
|
||||
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
|
||||
ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
|
||||
ret = ads7138_i2c_read_block(data->client, reg, values,
|
||||
ARRAY_SIZE(values));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = ((values[1] << 4) | (values[0] >> 4));
|
||||
return IIO_VAL_INT;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
ret = ads7138_i2c_read(data->client,
|
||||
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret & ~ADS7138_THRESHOLD_LSB_MASK;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ads7138_write_event(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info, int val, int val2)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
u8 reg, values[2];
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE: {
|
||||
if (val >= BIT(12) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
reg = (dir == IIO_EV_DIR_RISING) ?
|
||||
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
|
||||
ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
|
||||
|
||||
guard(mutex)(&data->lock);
|
||||
ret = ads7138_i2c_read(data->client, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
values[0] = ret & ~ADS7138_THRESHOLD_LSB_MASK;
|
||||
values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, val);
|
||||
values[1] = (val >> 4);
|
||||
return ads7138_i2c_write_block(data->client, reg, values,
|
||||
ARRAY_SIZE(values));
|
||||
}
|
||||
case IIO_EV_INFO_HYSTERESIS: {
|
||||
if (val >= BIT(4) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
reg = ADS7138_REG_HIGH_TH_HYS_CH(chan->channel);
|
||||
|
||||
guard(mutex)(&data->lock);
|
||||
ret = ads7138_i2c_read(data->client, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
values[0] = val & ~ADS7138_THRESHOLD_LSB_MASK;
|
||||
values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, ret >> 4);
|
||||
return ads7138_i2c_write(data->client, reg, values[0]);
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ads7138_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (dir != IIO_EV_DIR_EITHER)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_ALERT_CH_SEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret & BIT(chan->channel)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int ads7138_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir, bool state)
|
||||
{
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (dir != IIO_EV_DIR_EITHER)
|
||||
return -EINVAL;
|
||||
|
||||
if (state)
|
||||
return ads7138_i2c_set_bit(data->client,
|
||||
ADS7138_REG_ALERT_CH_SEL,
|
||||
BIT(chan->channel));
|
||||
else
|
||||
return ads7138_i2c_clear_bit(data->client,
|
||||
ADS7138_REG_ALERT_CH_SEL,
|
||||
BIT(chan->channel));
|
||||
}
|
||||
|
||||
static int ads7138_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*vals = ads7138_samp_freqs_bits[0];
|
||||
*length = ARRAY_SIZE(ads7138_samp_freqs_bits[0]);
|
||||
*type = IIO_VAL_INT;
|
||||
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*vals = ads7138_oversampling_ratios;
|
||||
*length = ARRAY_SIZE(ads7138_oversampling_ratios);
|
||||
*type = IIO_VAL_INT;
|
||||
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ti_ads7138_info = {
|
||||
.read_raw = &ads7138_read_raw,
|
||||
.read_avail = &ads7138_read_avail,
|
||||
.write_raw = &ads7138_write_raw,
|
||||
.read_event_value = &ads7138_read_event,
|
||||
.write_event_value = &ads7138_write_event,
|
||||
.read_event_config = &ads7138_read_event_config,
|
||||
.write_event_config = &ads7138_write_event_config,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ads7138_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE)
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
#define ADS7138_V_CHAN(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_PEAK) | \
|
||||
BIT(IIO_CHAN_INFO_TROUGH), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.info_mask_shared_by_type_available = \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.datasheet_name = "AIN"#_chan, \
|
||||
.event_spec = ads7138_events, \
|
||||
.num_event_specs = ARRAY_SIZE(ads7138_events), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ads7138_channels[] = {
|
||||
ADS7138_V_CHAN(0),
|
||||
ADS7138_V_CHAN(1),
|
||||
ADS7138_V_CHAN(2),
|
||||
ADS7138_V_CHAN(3),
|
||||
ADS7138_V_CHAN(4),
|
||||
ADS7138_V_CHAN(5),
|
||||
ADS7138_V_CHAN(6),
|
||||
ADS7138_V_CHAN(7),
|
||||
};
|
||||
|
||||
static irqreturn_t ads7138_event_handler(int irq, void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = priv;
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = &data->client->dev;
|
||||
u8 i, events_high, events_low;
|
||||
u64 code;
|
||||
int ret;
|
||||
|
||||
/* Check if interrupt was trigger by us */
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_FLAG);
|
||||
if (ret <= 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_HIGH_FLAG);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "Failed to read event high flags: %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
events_high = ret;
|
||||
|
||||
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_LOW_FLAG);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "Failed to read event low flags: %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
events_low = ret;
|
||||
|
||||
for (i = 0; i < data->chip_data->channel_num; i++) {
|
||||
if (events_high & BIT(i)) {
|
||||
code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(indio_dev, code,
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
if (events_low & BIT(i)) {
|
||||
code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING);
|
||||
iio_push_event(indio_dev, code,
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to clear all interrupt flags */
|
||||
ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_HIGH_FLAG, 0xFF);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to clear event high flags: %d\n", ret);
|
||||
|
||||
ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_LOW_FLAG, 0xFF);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to clear event low flags: %d\n", ret);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ads7138_set_conv_mode(struct ads7138_data *data,
|
||||
enum ads7138_modes mode)
|
||||
{
|
||||
if (mode == ADS7138_MODE_AUTO)
|
||||
return ads7138_i2c_set_bit(data->client, ADS7138_REG_OPMODE_CFG,
|
||||
ADS7138_OPMODE_CFG_CONV_MODE);
|
||||
return ads7138_i2c_clear_bit(data->client, ADS7138_REG_OPMODE_CFG,
|
||||
ADS7138_OPMODE_CFG_CONV_MODE);
|
||||
}
|
||||
|
||||
static int ads7138_init_hw(struct ads7138_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
|
||||
data->vref_regu = devm_regulator_get(dev, "avdd");
|
||||
if (IS_ERR(data->vref_regu))
|
||||
return dev_err_probe(dev, PTR_ERR(data->vref_regu),
|
||||
"Failed to get avdd regulator\n");
|
||||
|
||||
ret = regulator_get_voltage(data->vref_regu);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to get avdd voltage\n");
|
||||
|
||||
/* Reset the chip to get a defined starting configuration */
|
||||
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
|
||||
ADS7138_GENERAL_CFG_RST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable statistics and digital window comparator */
|
||||
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
|
||||
ADS7138_GENERAL_CFG_STATS_EN |
|
||||
ADS7138_GENERAL_CFG_DWC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable all channels for auto sequencing */
|
||||
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_AUTO_SEQ_CH_SEL, 0xFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set auto sequence mode and start sequencing */
|
||||
return ads7138_i2c_set_bit(data->client, ADS7138_REG_SEQUENCE_CFG,
|
||||
ADS7138_SEQUENCE_CFG_SEQ_START |
|
||||
ADS7138_SEQUENCE_CFG_SEQ_MODE);
|
||||
}
|
||||
|
||||
static int ads7138_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ads7138_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->chip_data = i2c_get_match_data(client);
|
||||
if (!data->chip_data)
|
||||
return -ENODEV;
|
||||
|
||||
ret = devm_mutex_init(dev, &data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->name = data->chip_data->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ads7138_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads7138_channels);
|
||||
indio_dev->info = &ti_ads7138_info;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, ads7138_event_handler,
|
||||
IRQF_TRIGGER_LOW |
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
client->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ads7138_init_hw(data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to initialize device\n");
|
||||
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register iio device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads7138_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
|
||||
return ads7138_set_conv_mode(data, ADS7138_MODE_MANUAL);
|
||||
}
|
||||
|
||||
static int ads7138_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ads7138_data *data = iio_priv(indio_dev);
|
||||
|
||||
return ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
|
||||
}
|
||||
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(ads7138_pm_ops,
|
||||
ads7138_runtime_suspend,
|
||||
ads7138_runtime_resume,
|
||||
NULL);
|
||||
|
||||
static const struct ads7138_chip_data ads7128_data = {
|
||||
.name = "ads7128",
|
||||
.channel_num = 8,
|
||||
};
|
||||
|
||||
static const struct ads7138_chip_data ads7138_data = {
|
||||
.name = "ads7138",
|
||||
.channel_num = 8,
|
||||
};
|
||||
|
||||
static const struct of_device_id ads7138_of_match[] = {
|
||||
{ .compatible = "ti,ads7128", .data = &ads7128_data },
|
||||
{ .compatible = "ti,ads7138", .data = &ads7138_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ads7138_of_match);
|
||||
|
||||
static const struct i2c_device_id ads7138_device_ids[] = {
|
||||
{ "ads7128", (kernel_ulong_t)&ads7128_data },
|
||||
{ "ads7138", (kernel_ulong_t)&ads7138_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads7138_device_ids);
|
||||
|
||||
static struct i2c_driver ads7138_driver = {
|
||||
.driver = {
|
||||
.name = "ads7138",
|
||||
.of_match_table = ads7138_of_match,
|
||||
.pm = pm_ptr(&ads7138_pm_ops),
|
||||
},
|
||||
.id_table = ads7138_device_ids,
|
||||
.probe = ads7138_probe,
|
||||
};
|
||||
module_i2c_driver(ads7138_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Tobias Sperling <tobias.sperling@softing.com>");
|
||||
MODULE_DESCRIPTION("Driver for TI ADS7138 ADCs");
|
||||
Reference in New Issue
Block a user