mirror of https://github.com/Qortal/Brooklyn
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.
953 lines
23 KiB
953 lines
23 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs |
|
* |
|
* Copyright (c) 2020 AVL DiTEST GmbH |
|
* Tomislav Denis <[email protected]> |
|
* |
|
* Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf |
|
*/ |
|
|
|
#include <linux/bitfield.h> |
|
#include <linux/clk.h> |
|
#include <linux/delay.h> |
|
#include <linux/module.h> |
|
|
|
#include <linux/iio/buffer.h> |
|
#include <linux/iio/iio.h> |
|
#include <linux/iio/sysfs.h> |
|
#include <linux/iio/trigger.h> |
|
#include <linux/iio/trigger_consumer.h> |
|
#include <linux/iio/triggered_buffer.h> |
|
|
|
#include <linux/regulator/consumer.h> |
|
#include <linux/spi/spi.h> |
|
|
|
#include <asm/unaligned.h> |
|
|
|
/* Commands */ |
|
#define ADS131E08_CMD_RESET 0x06 |
|
#define ADS131E08_CMD_START 0x08 |
|
#define ADS131E08_CMD_STOP 0x0A |
|
#define ADS131E08_CMD_OFFSETCAL 0x1A |
|
#define ADS131E08_CMD_SDATAC 0x11 |
|
#define ADS131E08_CMD_RDATA 0x12 |
|
#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0))) |
|
#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0))) |
|
|
|
/* Registers */ |
|
#define ADS131E08_ADR_CFG1R 0x01 |
|
#define ADS131E08_ADR_CFG3R 0x03 |
|
#define ADS131E08_ADR_CH0R 0x05 |
|
|
|
/* Configuration register 1 */ |
|
#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0) |
|
|
|
/* Configuration register 3 */ |
|
#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7) |
|
#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5) |
|
|
|
/* Channel settings register */ |
|
#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4) |
|
#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0) |
|
#define ADS131E08_CHR_PWD_MASK BIT(7) |
|
|
|
/* ADC misc */ |
|
#define ADS131E08_DEFAULT_DATA_RATE 1 |
|
#define ADS131E08_DEFAULT_PGA_GAIN 1 |
|
#define ADS131E08_DEFAULT_MUX 0 |
|
|
|
#define ADS131E08_VREF_2V4_mV 2400 |
|
#define ADS131E08_VREF_4V_mV 4000 |
|
|
|
#define ADS131E08_WAIT_RESET_CYCLES 18 |
|
#define ADS131E08_WAIT_SDECODE_CYCLES 4 |
|
#define ADS131E08_WAIT_OFFSETCAL_MS 153 |
|
#define ADS131E08_MAX_SETTLING_TIME_MS 6 |
|
|
|
#define ADS131E08_NUM_STATUS_BYTES 3 |
|
#define ADS131E08_NUM_DATA_BYTES_MAX 24 |
|
#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3) |
|
#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8) |
|
#define ADS131E08_NUM_STORAGE_BYTES 4 |
|
|
|
enum ads131e08_ids { |
|
ads131e04, |
|
ads131e06, |
|
ads131e08, |
|
}; |
|
|
|
struct ads131e08_info { |
|
unsigned int max_channels; |
|
const char *name; |
|
}; |
|
|
|
struct ads131e08_channel_config { |
|
unsigned int pga_gain; |
|
unsigned int mux; |
|
}; |
|
|
|
struct ads131e08_state { |
|
const struct ads131e08_info *info; |
|
struct spi_device *spi; |
|
struct iio_trigger *trig; |
|
struct clk *adc_clk; |
|
struct regulator *vref_reg; |
|
struct ads131e08_channel_config *channel_config; |
|
unsigned int data_rate; |
|
unsigned int vref_mv; |
|
unsigned int sdecode_delay_us; |
|
unsigned int reset_delay_us; |
|
unsigned int readback_len; |
|
struct completion completion; |
|
struct { |
|
u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; |
|
s64 ts __aligned(8); |
|
} tmp_buf; |
|
|
|
u8 tx_buf[3] ____cacheline_aligned; |
|
/* |
|
* Add extra one padding byte to be able to access the last channel |
|
* value using u32 pointer |
|
*/ |
|
u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + |
|
ADS131E08_NUM_DATA_BYTES_MAX + 1]; |
|
}; |
|
|
|
static const struct ads131e08_info ads131e08_info_tbl[] = { |
|
[ads131e04] = { |
|
.max_channels = 4, |
|
.name = "ads131e04", |
|
}, |
|
[ads131e06] = { |
|
.max_channels = 6, |
|
.name = "ads131e06", |
|
}, |
|
[ads131e08] = { |
|
.max_channels = 8, |
|
.name = "ads131e08", |
|
}, |
|
}; |
|
|
|
struct ads131e08_data_rate_desc { |
|
unsigned int rate; /* data rate in kSPS */ |
|
u8 reg; /* reg value */ |
|
}; |
|
|
|
static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = { |
|
{ .rate = 64, .reg = 0x00 }, |
|
{ .rate = 32, .reg = 0x01 }, |
|
{ .rate = 16, .reg = 0x02 }, |
|
{ .rate = 8, .reg = 0x03 }, |
|
{ .rate = 4, .reg = 0x04 }, |
|
{ .rate = 2, .reg = 0x05 }, |
|
{ .rate = 1, .reg = 0x06 }, |
|
}; |
|
|
|
struct ads131e08_pga_gain_desc { |
|
unsigned int gain; /* PGA gain value */ |
|
u8 reg; /* field value */ |
|
}; |
|
|
|
static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = { |
|
{ .gain = 1, .reg = 0x01 }, |
|
{ .gain = 2, .reg = 0x02 }, |
|
{ .gain = 4, .reg = 0x04 }, |
|
{ .gain = 8, .reg = 0x05 }, |
|
{ .gain = 12, .reg = 0x06 }, |
|
}; |
|
|
|
static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 }; |
|
|
|
static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) |
|
{ |
|
int ret; |
|
|
|
ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0); |
|
if (ret) |
|
dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd); |
|
|
|
return ret; |
|
} |
|
|
|
static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg) |
|
{ |
|
int ret; |
|
struct spi_transfer transfer[] = { |
|
{ |
|
.tx_buf = &st->tx_buf, |
|
.len = 2, |
|
.delay = { |
|
.value = st->sdecode_delay_us, |
|
.unit = SPI_DELAY_UNIT_USECS, |
|
}, |
|
}, { |
|
.rx_buf = &st->rx_buf, |
|
.len = 1, |
|
}, |
|
}; |
|
|
|
st->tx_buf[0] = ADS131E08_CMD_RREG(reg); |
|
st->tx_buf[1] = 0; |
|
|
|
ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
|
if (ret) { |
|
dev_err(&st->spi->dev, "Read register failed\n"); |
|
return ret; |
|
} |
|
|
|
return st->rx_buf[0]; |
|
} |
|
|
|
static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value) |
|
{ |
|
int ret; |
|
struct spi_transfer transfer[] = { |
|
{ |
|
.tx_buf = &st->tx_buf, |
|
.len = 3, |
|
.delay = { |
|
.value = st->sdecode_delay_us, |
|
.unit = SPI_DELAY_UNIT_USECS, |
|
}, |
|
} |
|
}; |
|
|
|
st->tx_buf[0] = ADS131E08_CMD_WREG(reg); |
|
st->tx_buf[1] = 0; |
|
st->tx_buf[2] = value; |
|
|
|
ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
|
if (ret) |
|
dev_err(&st->spi->dev, "Write register failed\n"); |
|
|
|
return ret; |
|
} |
|
|
|
static int ads131e08_read_data(struct ads131e08_state *st, int rx_len) |
|
{ |
|
int ret; |
|
struct spi_transfer transfer[] = { |
|
{ |
|
.tx_buf = &st->tx_buf, |
|
.len = 1, |
|
}, { |
|
.rx_buf = &st->rx_buf, |
|
.len = rx_len, |
|
}, |
|
}; |
|
|
|
st->tx_buf[0] = ADS131E08_CMD_RDATA; |
|
|
|
ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
|
if (ret) |
|
dev_err(&st->spi->dev, "Read data failed\n"); |
|
|
|
return ret; |
|
} |
|
|
|
static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate) |
|
{ |
|
int i, reg, ret; |
|
|
|
for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) { |
|
if (ads131e08_data_rate_tbl[i].rate == data_rate) |
|
break; |
|
} |
|
|
|
if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) { |
|
dev_err(&st->spi->dev, "invalid data rate value\n"); |
|
return -EINVAL; |
|
} |
|
|
|
reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R); |
|
if (reg < 0) |
|
return reg; |
|
|
|
reg &= ~ADS131E08_CFG1R_DR_MASK; |
|
reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK, |
|
ads131e08_data_rate_tbl[i].reg); |
|
|
|
ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg); |
|
if (ret) |
|
return ret; |
|
|
|
st->data_rate = data_rate; |
|
st->readback_len = ADS131E08_NUM_STATUS_BYTES + |
|
ADS131E08_NUM_DATA_BYTES(st->data_rate) * |
|
st->info->max_channels; |
|
|
|
return 0; |
|
} |
|
|
|
static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st, |
|
unsigned int pga_gain) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) { |
|
if (ads131e08_pga_gain_tbl[i].gain == pga_gain) |
|
break; |
|
} |
|
|
|
if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) { |
|
dev_err(&st->spi->dev, "invalid PGA gain value\n"); |
|
return -EINVAL; |
|
} |
|
|
|
return ads131e08_pga_gain_tbl[i].reg; |
|
} |
|
|
|
static int ads131e08_set_pga_gain(struct ads131e08_state *st, |
|
unsigned int channel, unsigned int pga_gain) |
|
{ |
|
int field_value, reg; |
|
|
|
field_value = ads131e08_pga_gain_to_field_value(st, pga_gain); |
|
if (field_value < 0) |
|
return field_value; |
|
|
|
reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
|
if (reg < 0) |
|
return reg; |
|
|
|
reg &= ~ADS131E08_CHR_GAIN_MASK; |
|
reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value); |
|
|
|
return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
|
} |
|
|
|
static int ads131e08_validate_channel_mux(struct ads131e08_state *st, |
|
unsigned int mux) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) { |
|
if (ads131e08_valid_channel_mux_values[i] == mux) |
|
break; |
|
} |
|
|
|
if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) { |
|
dev_err(&st->spi->dev, "invalid channel mux value\n"); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int ads131e08_set_channel_mux(struct ads131e08_state *st, |
|
unsigned int channel, unsigned int mux) |
|
{ |
|
int reg; |
|
|
|
reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
|
if (reg < 0) |
|
return reg; |
|
|
|
reg &= ~ADS131E08_CHR_MUX_MASK; |
|
reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux); |
|
|
|
return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
|
} |
|
|
|
static int ads131e08_power_down_channel(struct ads131e08_state *st, |
|
unsigned int channel, bool value) |
|
{ |
|
int reg; |
|
|
|
reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
|
if (reg < 0) |
|
return reg; |
|
|
|
reg &= ~ADS131E08_CHR_PWD_MASK; |
|
reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value); |
|
|
|
return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
|
} |
|
|
|
static int ads131e08_config_reference_voltage(struct ads131e08_state *st) |
|
{ |
|
int reg; |
|
|
|
reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R); |
|
if (reg < 0) |
|
return reg; |
|
|
|
reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK; |
|
if (!st->vref_reg) { |
|
reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1); |
|
reg &= ~ADS131E08_CFG3R_VREF_4V_MASK; |
|
reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK, |
|
st->vref_mv == ADS131E08_VREF_4V_mV); |
|
} |
|
|
|
return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg); |
|
} |
|
|
|
static int ads131e08_initial_config(struct iio_dev *indio_dev) |
|
{ |
|
const struct iio_chan_spec *channel = indio_dev->channels; |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
unsigned long active_channels = 0; |
|
int ret, i; |
|
|
|
ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET); |
|
if (ret) |
|
return ret; |
|
|
|
udelay(st->reset_delay_us); |
|
|
|
/* Disable read data in continuous mode (enabled by default) */ |
|
ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC); |
|
if (ret) |
|
return ret; |
|
|
|
ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE); |
|
if (ret) |
|
return ret; |
|
|
|
ret = ads131e08_config_reference_voltage(st); |
|
if (ret) |
|
return ret; |
|
|
|
for (i = 0; i < indio_dev->num_channels; i++) { |
|
ret = ads131e08_set_pga_gain(st, channel->channel, |
|
st->channel_config[i].pga_gain); |
|
if (ret) |
|
return ret; |
|
|
|
ret = ads131e08_set_channel_mux(st, channel->channel, |
|
st->channel_config[i].mux); |
|
if (ret) |
|
return ret; |
|
|
|
active_channels |= BIT(channel->channel); |
|
channel++; |
|
} |
|
|
|
/* Power down unused channels */ |
|
for_each_clear_bit(i, &active_channels, st->info->max_channels) { |
|
ret = ads131e08_power_down_channel(st, i, true); |
|
if (ret) |
|
return ret; |
|
} |
|
|
|
/* Request channel offset calibration */ |
|
ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL); |
|
if (ret) |
|
return ret; |
|
|
|
/* |
|
* Channel offset calibration is triggered with the first START |
|
* command. Since calibration takes more time than settling operation, |
|
* this causes timeout error when command START is sent first |
|
* time (e.g. first call of the ads131e08_read_direct method). |
|
* To avoid this problem offset calibration is triggered here. |
|
*/ |
|
ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); |
|
if (ret) |
|
return ret; |
|
|
|
msleep(ADS131E08_WAIT_OFFSETCAL_MS); |
|
|
|
return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); |
|
} |
|
|
|
static int ads131e08_pool_data(struct ads131e08_state *st) |
|
{ |
|
unsigned long timeout; |
|
int ret; |
|
|
|
reinit_completion(&st->completion); |
|
|
|
ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); |
|
if (ret) |
|
return ret; |
|
|
|
timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS); |
|
ret = wait_for_completion_timeout(&st->completion, timeout); |
|
if (!ret) |
|
return -ETIMEDOUT; |
|
|
|
ret = ads131e08_read_data(st, st->readback_len); |
|
if (ret) |
|
return ret; |
|
|
|
return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); |
|
} |
|
|
|
static int ads131e08_read_direct(struct iio_dev *indio_dev, |
|
struct iio_chan_spec const *channel, int *value) |
|
{ |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
u8 num_bits, *src; |
|
int ret; |
|
|
|
ret = ads131e08_pool_data(st); |
|
if (ret) |
|
return ret; |
|
|
|
src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + |
|
channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate); |
|
|
|
num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate); |
|
*value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1); |
|
|
|
return 0; |
|
} |
|
|
|
static int ads131e08_read_raw(struct iio_dev *indio_dev, |
|
struct iio_chan_spec const *channel, int *value, |
|
int *value2, long mask) |
|
{ |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
int ret; |
|
|
|
switch (mask) { |
|
case IIO_CHAN_INFO_RAW: |
|
ret = iio_device_claim_direct_mode(indio_dev); |
|
if (ret) |
|
return ret; |
|
|
|
ret = ads131e08_read_direct(indio_dev, channel, value); |
|
iio_device_release_direct_mode(indio_dev); |
|
if (ret) |
|
return ret; |
|
|
|
return IIO_VAL_INT; |
|
|
|
case IIO_CHAN_INFO_SCALE: |
|
if (st->vref_reg) { |
|
ret = regulator_get_voltage(st->vref_reg); |
|
if (ret < 0) |
|
return ret; |
|
|
|
*value = ret / 1000; |
|
} else { |
|
*value = st->vref_mv; |
|
} |
|
|
|
*value /= st->channel_config[channel->address].pga_gain; |
|
*value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1; |
|
|
|
return IIO_VAL_FRACTIONAL_LOG2; |
|
|
|
case IIO_CHAN_INFO_SAMP_FREQ: |
|
*value = st->data_rate; |
|
|
|
return IIO_VAL_INT; |
|
|
|
default: |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
static int ads131e08_write_raw(struct iio_dev *indio_dev, |
|
struct iio_chan_spec const *channel, int value, |
|
int value2, long mask) |
|
{ |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
int ret; |
|
|
|
switch (mask) { |
|
case IIO_CHAN_INFO_SAMP_FREQ: |
|
ret = iio_device_claim_direct_mode(indio_dev); |
|
if (ret) |
|
return ret; |
|
|
|
ret = ads131e08_set_data_rate(st, value); |
|
iio_device_release_direct_mode(indio_dev); |
|
return ret; |
|
|
|
default: |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64"); |
|
|
|
static struct attribute *ads131e08_attributes[] = { |
|
&iio_const_attr_sampling_frequency_available.dev_attr.attr, |
|
NULL |
|
}; |
|
|
|
static const struct attribute_group ads131e08_attribute_group = { |
|
.attrs = ads131e08_attributes, |
|
}; |
|
|
|
static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev, |
|
unsigned int reg, unsigned int writeval, unsigned int *readval) |
|
{ |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
|
|
if (readval) { |
|
int ret = ads131e08_read_reg(st, reg); |
|
*readval = ret; |
|
return ret; |
|
} |
|
|
|
return ads131e08_write_reg(st, reg, writeval); |
|
} |
|
|
|
static const struct iio_info ads131e08_iio_info = { |
|
.read_raw = ads131e08_read_raw, |
|
.write_raw = ads131e08_write_raw, |
|
.attrs = &ads131e08_attribute_group, |
|
.debugfs_reg_access = &ads131e08_debugfs_reg_access, |
|
}; |
|
|
|
static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state) |
|
{ |
|
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP; |
|
|
|
return ads131e08_exec_cmd(st, cmd); |
|
} |
|
|
|
static const struct iio_trigger_ops ads131e08_trigger_ops = { |
|
.set_trigger_state = &ads131e08_set_trigger_state, |
|
.validate_device = &iio_trigger_validate_own_device, |
|
}; |
|
|
|
static irqreturn_t ads131e08_trigger_handler(int irq, void *private) |
|
{ |
|
struct iio_poll_func *pf = private; |
|
struct iio_dev *indio_dev = pf->indio_dev; |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
unsigned int chn, i = 0; |
|
u8 *src, *dest; |
|
int ret; |
|
|
|
/* |
|
* The number of data bits per channel depends on the data rate. |
|
* For 32 and 64 ksps data rates, number of data bits per channel |
|
* is 16. This case is not compliant with used (fixed) scan element |
|
* type (be:s24/32>>8). So we use a little tweak to pack properly |
|
* 16 bits of data into the buffer. |
|
*/ |
|
unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); |
|
u8 tweek_offset = num_bytes == 2 ? 1 : 0; |
|
|
|
if (iio_trigger_using_own(indio_dev)) |
|
ret = ads131e08_read_data(st, st->readback_len); |
|
else |
|
ret = ads131e08_pool_data(st); |
|
|
|
if (ret) |
|
goto out; |
|
|
|
for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) { |
|
src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; |
|
dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; |
|
|
|
/* |
|
* Tweek offset is 0: |
|
* +---+---+---+---+ |
|
* |D0 |D1 |D2 | X | (3 data bytes) |
|
* +---+---+---+---+ |
|
* a+0 a+1 a+2 a+3 |
|
* |
|
* Tweek offset is 1: |
|
* +---+---+---+---+ |
|
* |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) |
|
* +---+---+---+---+ |
|
* a+0 a+1 a+2 a+3 |
|
*/ |
|
memcpy(dest + tweek_offset, src, num_bytes); |
|
|
|
/* |
|
* Data conversion from 16 bits of data to 24 bits of data |
|
* is done by sign extension (properly filling padding byte). |
|
*/ |
|
if (tweek_offset) |
|
*dest = *src & BIT(7) ? 0xff : 0x00; |
|
|
|
i++; |
|
} |
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data, |
|
iio_get_time_ns(indio_dev)); |
|
|
|
out: |
|
iio_trigger_notify_done(indio_dev->trig); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
static irqreturn_t ads131e08_interrupt(int irq, void *private) |
|
{ |
|
struct iio_dev *indio_dev = private; |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
|
|
if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) |
|
iio_trigger_poll(st->trig); |
|
else |
|
complete(&st->completion); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
static int ads131e08_alloc_channels(struct iio_dev *indio_dev) |
|
{ |
|
struct ads131e08_state *st = iio_priv(indio_dev); |
|
struct ads131e08_channel_config *channel_config; |
|
struct device *dev = &st->spi->dev; |
|
struct iio_chan_spec *channels; |
|
struct fwnode_handle *node; |
|
unsigned int channel, tmp; |
|
int num_channels, i, ret; |
|
|
|
ret = device_property_read_u32(dev, "ti,vref-internal", &tmp); |
|
if (ret) |
|
tmp = 0; |
|
|
|
switch (tmp) { |
|
case 0: |
|
st->vref_mv = ADS131E08_VREF_2V4_mV; |
|
break; |
|
case 1: |
|
st->vref_mv = ADS131E08_VREF_4V_mV; |
|
break; |
|
default: |
|
dev_err(&st->spi->dev, "invalid internal voltage reference\n"); |
|
return -EINVAL; |
|
} |
|
|
|
num_channels = device_get_child_node_count(dev); |
|
if (num_channels == 0) { |
|
dev_err(&st->spi->dev, "no channel children\n"); |
|
return -ENODEV; |
|
} |
|
|
|
if (num_channels > st->info->max_channels) { |
|
dev_err(&st->spi->dev, "num of channel children out of range\n"); |
|
return -EINVAL; |
|
} |
|
|
|
channels = devm_kcalloc(&st->spi->dev, num_channels, |
|
sizeof(*channels), GFP_KERNEL); |
|
if (!channels) |
|
return -ENOMEM; |
|
|
|
channel_config = devm_kcalloc(&st->spi->dev, num_channels, |
|
sizeof(*channel_config), GFP_KERNEL); |
|
if (!channel_config) |
|
return -ENOMEM; |
|
|
|
i = 0; |
|
device_for_each_child_node(dev, node) { |
|
ret = fwnode_property_read_u32(node, "reg", &channel); |
|
if (ret) |
|
return ret; |
|
|
|
ret = fwnode_property_read_u32(node, "ti,gain", &tmp); |
|
if (ret) { |
|
channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN; |
|
} else { |
|
ret = ads131e08_pga_gain_to_field_value(st, tmp); |
|
if (ret < 0) |
|
return ret; |
|
|
|
channel_config[i].pga_gain = tmp; |
|
} |
|
|
|
ret = fwnode_property_read_u32(node, "ti,mux", &tmp); |
|
if (ret) { |
|
channel_config[i].mux = ADS131E08_DEFAULT_MUX; |
|
} else { |
|
ret = ads131e08_validate_channel_mux(st, tmp); |
|
if (ret) |
|
return ret; |
|
|
|
channel_config[i].mux = tmp; |
|
} |
|
|
|
channels[i].type = IIO_VOLTAGE; |
|
channels[i].indexed = 1; |
|
channels[i].channel = channel; |
|
channels[i].address = i; |
|
channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
|
BIT(IIO_CHAN_INFO_SCALE); |
|
channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); |
|
channels[i].scan_index = channel; |
|
channels[i].scan_type.sign = 's'; |
|
channels[i].scan_type.realbits = 24; |
|
channels[i].scan_type.storagebits = 32; |
|
channels[i].scan_type.shift = 8; |
|
channels[i].scan_type.endianness = IIO_BE; |
|
i++; |
|
} |
|
|
|
indio_dev->channels = channels; |
|
indio_dev->num_channels = num_channels; |
|
st->channel_config = channel_config; |
|
|
|
return 0; |
|
} |
|
|
|
static void ads131e08_regulator_disable(void *data) |
|
{ |
|
struct ads131e08_state *st = data; |
|
|
|
regulator_disable(st->vref_reg); |
|
} |
|
|
|
static void ads131e08_clk_disable(void *data) |
|
{ |
|
struct ads131e08_state *st = data; |
|
|
|
clk_disable_unprepare(st->adc_clk); |
|
} |
|
|
|
static int ads131e08_probe(struct spi_device *spi) |
|
{ |
|
const struct ads131e08_info *info; |
|
struct ads131e08_state *st; |
|
struct iio_dev *indio_dev; |
|
unsigned long adc_clk_hz; |
|
unsigned long adc_clk_ns; |
|
int ret; |
|
|
|
info = device_get_match_data(&spi->dev); |
|
if (!info) { |
|
dev_err(&spi->dev, "failed to get match data\n"); |
|
return -ENODEV; |
|
} |
|
|
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
|
if (!indio_dev) { |
|
dev_err(&spi->dev, "failed to allocate IIO device\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
st = iio_priv(indio_dev); |
|
st->info = info; |
|
st->spi = spi; |
|
|
|
ret = ads131e08_alloc_channels(indio_dev); |
|
if (ret) |
|
return ret; |
|
|
|
indio_dev->name = st->info->name; |
|
indio_dev->info = &ads131e08_iio_info; |
|
indio_dev->modes = INDIO_DIRECT_MODE; |
|
|
|
init_completion(&st->completion); |
|
|
|
if (spi->irq) { |
|
ret = devm_request_irq(&spi->dev, spi->irq, |
|
ads131e08_interrupt, |
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
|
spi->dev.driver->name, indio_dev); |
|
if (ret) |
|
return dev_err_probe(&spi->dev, ret, |
|
"request irq failed\n"); |
|
} else { |
|
dev_err(&spi->dev, "data ready IRQ missing\n"); |
|
return -ENODEV; |
|
} |
|
|
|
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", |
|
indio_dev->name, iio_device_id(indio_dev)); |
|
if (!st->trig) { |
|
dev_err(&spi->dev, "failed to allocate IIO trigger\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
st->trig->ops = &ads131e08_trigger_ops; |
|
st->trig->dev.parent = &spi->dev; |
|
iio_trigger_set_drvdata(st->trig, indio_dev); |
|
ret = devm_iio_trigger_register(&spi->dev, st->trig); |
|
if (ret) { |
|
dev_err(&spi->dev, "failed to register IIO trigger\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
indio_dev->trig = iio_trigger_get(st->trig); |
|
|
|
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, |
|
NULL, &ads131e08_trigger_handler, NULL); |
|
if (ret) { |
|
dev_err(&spi->dev, "failed to setup IIO buffer\n"); |
|
return ret; |
|
} |
|
|
|
st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); |
|
if (!IS_ERR(st->vref_reg)) { |
|
ret = regulator_enable(st->vref_reg); |
|
if (ret) { |
|
dev_err(&spi->dev, |
|
"failed to enable external vref supply\n"); |
|
return ret; |
|
} |
|
|
|
ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st); |
|
if (ret) |
|
return ret; |
|
} else { |
|
if (PTR_ERR(st->vref_reg) != -ENODEV) |
|
return PTR_ERR(st->vref_reg); |
|
|
|
st->vref_reg = NULL; |
|
} |
|
|
|
st->adc_clk = devm_clk_get(&spi->dev, "adc-clk"); |
|
if (IS_ERR(st->adc_clk)) |
|
return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), |
|
"failed to get the ADC clock\n"); |
|
|
|
ret = clk_prepare_enable(st->adc_clk); |
|
if (ret) { |
|
dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n"); |
|
return ret; |
|
} |
|
|
|
ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st); |
|
if (ret) |
|
return ret; |
|
|
|
adc_clk_hz = clk_get_rate(st->adc_clk); |
|
if (!adc_clk_hz) { |
|
dev_err(&spi->dev, "failed to get the ADC clock rate\n"); |
|
return -EINVAL; |
|
} |
|
|
|
adc_clk_ns = NSEC_PER_SEC / adc_clk_hz; |
|
st->sdecode_delay_us = DIV_ROUND_UP( |
|
ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC); |
|
st->reset_delay_us = DIV_ROUND_UP( |
|
ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC); |
|
|
|
ret = ads131e08_initial_config(indio_dev); |
|
if (ret) { |
|
dev_err(&spi->dev, "initial configuration failed\n"); |
|
return ret; |
|
} |
|
|
|
return devm_iio_device_register(&spi->dev, indio_dev); |
|
} |
|
|
|
static const struct of_device_id ads131e08_of_match[] = { |
|
{ .compatible = "ti,ads131e04", |
|
.data = &ads131e08_info_tbl[ads131e04], }, |
|
{ .compatible = "ti,ads131e06", |
|
.data = &ads131e08_info_tbl[ads131e06], }, |
|
{ .compatible = "ti,ads131e08", |
|
.data = &ads131e08_info_tbl[ads131e08], }, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(of, ads131e08_of_match); |
|
|
|
static struct spi_driver ads131e08_driver = { |
|
.driver = { |
|
.name = "ads131e08", |
|
.of_match_table = ads131e08_of_match, |
|
}, |
|
.probe = ads131e08_probe, |
|
}; |
|
module_spi_driver(ads131e08_driver); |
|
|
|
MODULE_AUTHOR("Tomislav Denis <[email protected]>"); |
|
MODULE_DESCRIPTION("Driver for ADS131E0x ADC family"); |
|
MODULE_LICENSE("GPL v2");
|
|
|