mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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>
749 lines
19 KiB
C
749 lines
19 KiB
C
// 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");
|