915 lines
22 KiB
C
915 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* AD7770, AD7771, AD7779 ADC
|
|
*
|
|
* Copyright 2023-2024 Analog Devices Inc.
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/crc8.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/math.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/unaligned.h>
|
|
#include <linux/units.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/trigger.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
|
|
#define AD7779_SPI_READ_CMD BIT(7)
|
|
|
|
#define AD7779_DISABLE_SD BIT(7)
|
|
|
|
#define AD7779_REG_CH_DISABLE 0x08
|
|
#define AD7779_REG_CH_SYNC_OFFSET(ch) (0x09 + (ch))
|
|
#define AD7779_REG_CH_CONFIG(ch) (0x00 + (ch))
|
|
#define AD7779_REG_GENERAL_USER_CONFIG_1 0x11
|
|
#define AD7779_REG_GENERAL_USER_CONFIG_2 0x12
|
|
#define AD7779_REG_GENERAL_USER_CONFIG_3 0x13
|
|
#define AD7779_REG_DOUT_FORMAT 0x14
|
|
#define AD7779_REG_ADC_MUX_CONFIG 0x15
|
|
#define AD7779_REG_GPIO_CONFIG 0x17
|
|
#define AD7779_REG_BUFFER_CONFIG_1 0x19
|
|
#define AD7779_REG_GLOBAL_MUX_CONFIG 0x16
|
|
#define AD7779_REG_BUFFER_CONFIG_2 0x1A
|
|
#define AD7779_REG_GPIO_DATA 0x18
|
|
#define AD7779_REG_CH_OFFSET_UPPER_BYTE(ch) (0x1C + (ch) * 6)
|
|
#define AD7779_REG_CH_OFFSET_LOWER_BYTE(ch) (0x1E + (ch) * 6)
|
|
#define AD7779_REG_CH_GAIN_UPPER_BYTE(ch) (0x1F + (ch) * 6)
|
|
#define AD7779_REG_CH_OFFSET_MID_BYTE(ch) (0x1D + (ch) * 6)
|
|
#define AD7779_REG_CH_GAIN_MID_BYTE(ch) (0x20 + (ch) * 6)
|
|
#define AD7779_REG_CH_ERR_REG(ch) (0x4C + (ch))
|
|
#define AD7779_REG_CH0_1_SAT_ERR 0x54
|
|
#define AD7779_REG_CH_GAIN_LOWER_BYTE(ch) (0x21 + (ch) * 6)
|
|
#define AD7779_REG_CH2_3_SAT_ERR 0x55
|
|
#define AD7779_REG_CH4_5_SAT_ERR 0x56
|
|
#define AD7779_REG_CH6_7_SAT_ERR 0x57
|
|
#define AD7779_REG_CHX_ERR_REG_EN 0x58
|
|
#define AD7779_REG_GEN_ERR_REG_1 0x59
|
|
#define AD7779_REG_GEN_ERR_REG_1_EN 0x5A
|
|
#define AD7779_REG_GEN_ERR_REG_2 0x5B
|
|
#define AD7779_REG_GEN_ERR_REG_2_EN 0x5C
|
|
#define AD7779_REG_STATUS_REG_1 0x5D
|
|
#define AD7779_REG_STATUS_REG_2 0x5E
|
|
#define AD7779_REG_STATUS_REG_3 0x5F
|
|
#define AD7779_REG_SRC_N_MSB 0x60
|
|
#define AD7779_REG_SRC_N_LSB 0x61
|
|
#define AD7779_REG_SRC_IF_MSB 0x62
|
|
#define AD7779_REG_SRC_IF_LSB 0x63
|
|
#define AD7779_REG_SRC_UPDATE 0x64
|
|
|
|
#define AD7779_FILTER_MSK BIT(6)
|
|
#define AD7779_MOD_POWERMODE_MSK BIT(6)
|
|
#define AD7779_MOD_PDB_REFOUT_MSK BIT(4)
|
|
#define AD7779_MOD_SPI_EN_MSK BIT(4)
|
|
#define AD7779_USRMOD_INIT_MSK GENMASK(6, 4)
|
|
|
|
/* AD7779_REG_DOUT_FORMAT */
|
|
#define AD7779_DOUT_FORMAT_MSK GENMASK(7, 6)
|
|
#define AD7779_DOUT_HEADER_FORMAT BIT(5)
|
|
#define AD7779_DCLK_CLK_DIV_MSK GENMASK(3, 1)
|
|
|
|
#define AD7779_REFMUX_CTRL_MSK GENMASK(7, 6)
|
|
#define AD7779_SPI_CRC_EN_MSK BIT(0)
|
|
|
|
#define AD7779_MAXCLK_LOWPOWER (4096 * HZ_PER_KHZ)
|
|
#define AD7779_NUM_CHANNELS 8
|
|
#define AD7779_RESET_BUF_SIZE 8
|
|
#define AD7779_CHAN_DATA_SIZE 4
|
|
|
|
#define AD7779_LOWPOWER_DIV 512
|
|
#define AD7779_HIGHPOWER_DIV 2048
|
|
|
|
#define AD7779_SINC3_MAXFREQ (16 * HZ_PER_KHZ)
|
|
#define AD7779_SINC5_MAXFREQ (128 * HZ_PER_KHZ)
|
|
|
|
#define AD7779_DEFAULT_SAMPLING_FREQ (8 * HZ_PER_KHZ)
|
|
#define AD7779_DEFAULT_SAMPLING_2LINE (4 * HZ_PER_KHZ)
|
|
#define AD7779_DEFAULT_SAMPLING_1LINE (2 * HZ_PER_KHZ)
|
|
|
|
#define AD7779_SPIMODE_MAX_SAMP_FREQ (16 * HZ_PER_KHZ)
|
|
|
|
#define GAIN_REL 0x555555
|
|
#define AD7779_FREQ_MSB_MSK GENMASK(15, 8)
|
|
#define AD7779_FREQ_LSB_MSK GENMASK(7, 0)
|
|
#define AD7779_UPPER GENMASK(23, 16)
|
|
#define AD7779_MID GENMASK(15, 8)
|
|
#define AD7779_LOWER GENMASK(7, 0)
|
|
|
|
#define AD7779_REG_MSK GENMASK(6, 0)
|
|
|
|
#define AD7779_CRC8_POLY 0x07
|
|
DECLARE_CRC8_TABLE(ad7779_crc8_table);
|
|
|
|
enum ad7779_filter {
|
|
AD7779_SINC3,
|
|
AD7779_SINC5,
|
|
};
|
|
|
|
enum ad7779_variant {
|
|
ad7770,
|
|
ad7771,
|
|
ad7779,
|
|
};
|
|
|
|
enum ad7779_power_mode {
|
|
AD7779_LOW_POWER,
|
|
AD7779_HIGH_POWER,
|
|
};
|
|
|
|
struct ad7779_chip_info {
|
|
const char *name;
|
|
struct iio_chan_spec const *channels;
|
|
};
|
|
|
|
struct ad7779_state {
|
|
struct spi_device *spi;
|
|
const struct ad7779_chip_info *chip_info;
|
|
struct clk *mclk;
|
|
struct iio_trigger *trig;
|
|
struct completion completion;
|
|
unsigned int sampling_freq;
|
|
enum ad7779_filter filter_enabled;
|
|
/*
|
|
* DMA (thus cache coherency maintenance) requires the
|
|
* transfer buffers to live in their own cache lines.
|
|
*/
|
|
struct {
|
|
u32 chans[8];
|
|
aligned_s64 timestamp;
|
|
} data __aligned(IIO_DMA_MINALIGN);
|
|
u32 spidata_tx[8];
|
|
u8 reg_rx_buf[3];
|
|
u8 reg_tx_buf[3];
|
|
u8 reset_buf[8];
|
|
};
|
|
|
|
static const char * const ad7779_filter_type[] = {
|
|
[AD7779_SINC3] = "sinc3",
|
|
[AD7779_SINC5] = "sinc5",
|
|
};
|
|
|
|
static const char * const ad7779_power_supplies[] = {
|
|
"avdd1", "avdd2", "avdd4",
|
|
};
|
|
|
|
static int ad7779_spi_read(struct ad7779_state *st, u8 reg, u8 *rbuf)
|
|
{
|
|
int ret;
|
|
u8 crc_buf[2];
|
|
u8 exp_crc;
|
|
struct spi_transfer t = {
|
|
.tx_buf = st->reg_tx_buf,
|
|
.rx_buf = st->reg_rx_buf,
|
|
};
|
|
|
|
st->reg_tx_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg);
|
|
st->reg_tx_buf[1] = 0;
|
|
|
|
if (reg == AD7779_REG_GEN_ERR_REG_1_EN) {
|
|
t.len = 2;
|
|
} else {
|
|
t.len = 3;
|
|
st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf,
|
|
t.len - 1, 0);
|
|
}
|
|
|
|
ret = spi_sync_transfer(st->spi, &t, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
crc_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg);
|
|
crc_buf[1] = st->reg_rx_buf[1];
|
|
exp_crc = crc8(ad7779_crc8_table, crc_buf, ARRAY_SIZE(crc_buf), 0);
|
|
if (reg != AD7779_REG_GEN_ERR_REG_1_EN && exp_crc != st->reg_rx_buf[2]) {
|
|
dev_err(&st->spi->dev, "Bad CRC %x, expected %x",
|
|
st->reg_rx_buf[2], exp_crc);
|
|
return -EINVAL;
|
|
}
|
|
*rbuf = st->reg_rx_buf[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad7779_spi_write(struct ad7779_state *st, u8 reg, u8 val)
|
|
{
|
|
u8 length = 3;
|
|
|
|
st->reg_tx_buf[0] = FIELD_GET(AD7779_REG_MSK, reg);
|
|
st->reg_tx_buf[1] = val;
|
|
if (reg == AD7779_REG_GEN_ERR_REG_1_EN)
|
|
length = 2;
|
|
else
|
|
st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf,
|
|
length - 1, 0);
|
|
|
|
return spi_write(st->spi, st->reg_tx_buf, length);
|
|
}
|
|
|
|
static int ad7779_spi_write_mask(struct ad7779_state *st, u8 reg, u8 mask,
|
|
u8 val)
|
|
{
|
|
int ret;
|
|
u8 regval, data;
|
|
|
|
ret = ad7779_spi_read(st, reg, &data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
regval = (data & ~mask) | (val & mask);
|
|
|
|
if (regval == data)
|
|
return 0;
|
|
|
|
return ad7779_spi_write(st, reg, regval);
|
|
}
|
|
|
|
static int ad7779_reg_access(struct iio_dev *indio_dev,
|
|
unsigned int reg,
|
|
unsigned int writeval,
|
|
unsigned int *readval)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
u8 rval;
|
|
int ret;
|
|
|
|
if (readval) {
|
|
ret = ad7779_spi_read(st, reg, &rval);
|
|
*readval = rval;
|
|
return ret;
|
|
}
|
|
|
|
return ad7779_spi_write(st, reg, writeval);
|
|
}
|
|
|
|
static int ad7779_set_sampling_frequency(struct ad7779_state *st,
|
|
unsigned int sampling_freq)
|
|
{
|
|
int ret;
|
|
unsigned int dec;
|
|
unsigned int frac;
|
|
unsigned int div;
|
|
unsigned int decimal;
|
|
unsigned int freq_khz;
|
|
|
|
if (st->filter_enabled == AD7779_SINC3 &&
|
|
sampling_freq > AD7779_SINC3_MAXFREQ)
|
|
return -EINVAL;
|
|
|
|
if (st->filter_enabled == AD7779_SINC5 &&
|
|
sampling_freq > AD7779_SINC5_MAXFREQ)
|
|
return -EINVAL;
|
|
|
|
if (sampling_freq > AD7779_SPIMODE_MAX_SAMP_FREQ)
|
|
return -EINVAL;
|
|
|
|
div = AD7779_HIGHPOWER_DIV;
|
|
|
|
freq_khz = sampling_freq / HZ_PER_KHZ;
|
|
dec = div / freq_khz;
|
|
frac = div % freq_khz;
|
|
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
|
|
FIELD_GET(AD7779_FREQ_MSB_MSK, dec));
|
|
if (ret)
|
|
return ret;
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
|
|
FIELD_GET(AD7779_FREQ_LSB_MSK, dec));
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (frac) {
|
|
/*
|
|
* In order to obtain the first three decimals of the decimation
|
|
* the initial number is multiplied with 10^3 prior to the
|
|
* division, then the original division result is subtracted and
|
|
* the number is divided by 10^3.
|
|
*/
|
|
decimal = ((mult_frac(div, KILO, freq_khz) - dec * KILO) << 16)
|
|
/ KILO;
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
|
|
FIELD_GET(AD7779_FREQ_MSB_MSK, decimal));
|
|
if (ret)
|
|
return ret;
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
|
|
FIELD_GET(AD7779_FREQ_LSB_MSK, decimal));
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
|
|
FIELD_GET(AD7779_FREQ_MSB_MSK, 0x0));
|
|
if (ret)
|
|
return ret;
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
|
|
FIELD_GET(AD7779_FREQ_LSB_MSK, 0x0));
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, BIT(0));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* SRC update settling time */
|
|
fsleep(15);
|
|
|
|
ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* SRC update settling time */
|
|
fsleep(15);
|
|
|
|
st->sampling_freq = sampling_freq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad7779_get_filter(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
u8 temp;
|
|
int ret;
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_GENERAL_USER_CONFIG_2, &temp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return FIELD_GET(AD7779_FILTER_MSK, temp);
|
|
}
|
|
|
|
static int ad7779_set_filter(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
unsigned int mode)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
ret = ad7779_spi_write_mask(st,
|
|
AD7779_REG_GENERAL_USER_CONFIG_2,
|
|
AD7779_FILTER_MSK,
|
|
FIELD_PREP(AD7779_FILTER_MSK, mode));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_set_sampling_frequency(st, st->sampling_freq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
st->filter_enabled = mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad7779_get_calibscale(struct ad7779_state *st, int channel)
|
|
{
|
|
int ret;
|
|
u8 calibscale[3];
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel),
|
|
&calibscale[0]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_MID_BYTE(channel),
|
|
&calibscale[1]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel),
|
|
&calibscale[2]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return get_unaligned_be24(calibscale);
|
|
}
|
|
|
|
static int ad7779_set_calibscale(struct ad7779_state *st, int channel, int val)
|
|
{
|
|
int ret;
|
|
unsigned int gain;
|
|
u8 gain_bytes[3];
|
|
|
|
/*
|
|
* The gain value is relative to 0x555555, which represents a gain of 1
|
|
*/
|
|
gain = DIV_ROUND_CLOSEST_ULL((u64)val * 5592405LL, MEGA);
|
|
put_unaligned_be24(gain, gain_bytes);
|
|
ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel),
|
|
gain_bytes[0]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_MID_BYTE(channel),
|
|
gain_bytes[1]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return ad7779_spi_write(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel),
|
|
gain_bytes[2]);
|
|
}
|
|
|
|
static int ad7779_get_calibbias(struct ad7779_state *st, int channel)
|
|
{
|
|
int ret;
|
|
u8 calibbias[3];
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel),
|
|
&calibbias[0]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel),
|
|
&calibbias[1]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel),
|
|
&calibbias[2]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return get_unaligned_be24(calibbias);
|
|
}
|
|
|
|
static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val)
|
|
{
|
|
int ret;
|
|
u8 calibbias[3];
|
|
|
|
put_unaligned_be24(val, calibbias);
|
|
ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel),
|
|
calibbias[0]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel),
|
|
calibbias[1]);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return ad7779_spi_write(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel),
|
|
calibbias[2]);
|
|
}
|
|
|
|
static int ad7779_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, int *val,
|
|
int *val2, long mask)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_CALIBSCALE:
|
|
ret = ad7779_get_calibscale(st, chan->channel);
|
|
if (ret < 0)
|
|
return ret;
|
|
*val = ret;
|
|
*val2 = GAIN_REL;
|
|
return IIO_VAL_FRACTIONAL;
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
ret = ad7779_get_calibbias(st, chan->channel);
|
|
if (ret < 0)
|
|
return ret;
|
|
*val = ret;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
*val = st->sampling_freq;
|
|
if (*val < 0)
|
|
return -EINVAL;
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
unreachable();
|
|
}
|
|
|
|
static int ad7779_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, int val, int val2,
|
|
long mask)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_CALIBSCALE:
|
|
return ad7779_set_calibscale(st, chan->channel, val2);
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
return ad7779_set_calibbias(st, chan->channel, val);
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
return ad7779_set_sampling_frequency(st, val);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
unreachable();
|
|
}
|
|
|
|
static int ad7779_buffer_preenable(struct iio_dev *indio_dev)
|
|
{
|
|
int ret;
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
|
|
ret = ad7779_spi_write_mask(st,
|
|
AD7779_REG_GENERAL_USER_CONFIG_3,
|
|
AD7779_MOD_SPI_EN_MSK,
|
|
FIELD_PREP(AD7779_MOD_SPI_EN_MSK, 1));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* DRDY output cannot be disabled at device level therefore we mask
|
|
* the irq at host end.
|
|
*/
|
|
enable_irq(st->spi->irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad7779_buffer_postdisable(struct iio_dev *indio_dev)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
|
|
disable_irq(st->spi->irq);
|
|
|
|
return ad7779_spi_write(st, AD7779_REG_GENERAL_USER_CONFIG_3,
|
|
AD7779_DISABLE_SD);
|
|
}
|
|
|
|
static irqreturn_t ad7779_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
struct spi_transfer t = {
|
|
.rx_buf = st->data.chans,
|
|
.tx_buf = st->spidata_tx,
|
|
.len = AD7779_NUM_CHANNELS * AD7779_CHAN_DATA_SIZE,
|
|
};
|
|
|
|
st->spidata_tx[0] = AD7779_SPI_READ_CMD;
|
|
ret = spi_sync_transfer(st->spi, &t, 1);
|
|
if (ret) {
|
|
dev_err(&st->spi->dev, "SPI transfer error in IRQ handler");
|
|
goto exit_handler;
|
|
}
|
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, &st->data, pf->timestamp);
|
|
|
|
exit_handler:
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int ad7779_reset(struct iio_dev *indio_dev, struct gpio_desc *reset_gpio)
|
|
{
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
struct spi_transfer t = {
|
|
.tx_buf = st->reset_buf,
|
|
.len = 8,
|
|
};
|
|
|
|
if (reset_gpio) {
|
|
gpiod_set_value(reset_gpio, 1);
|
|
/* Delay for reset to occur is 225 microseconds */
|
|
fsleep(230);
|
|
ret = 0;
|
|
} else {
|
|
memset(st->reset_buf, 0xff, sizeof(st->reset_buf));
|
|
ret = spi_sync_transfer(st->spi, &t, 1);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* Delay for reset to occur is 225 microseconds */
|
|
fsleep(230);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct iio_info ad7779_info = {
|
|
.read_raw = ad7779_read_raw,
|
|
.write_raw = ad7779_write_raw,
|
|
.debugfs_reg_access = &ad7779_reg_access,
|
|
};
|
|
|
|
static const struct iio_enum ad7779_filter_enum = {
|
|
.items = ad7779_filter_type,
|
|
.num_items = ARRAY_SIZE(ad7779_filter_type),
|
|
.get = ad7779_get_filter,
|
|
.set = ad7779_set_filter,
|
|
};
|
|
|
|
static const struct iio_chan_spec_ext_info ad7779_ext_filter[] = {
|
|
IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7779_filter_enum),
|
|
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL,
|
|
&ad7779_filter_enum),
|
|
{ }
|
|
};
|
|
|
|
#define AD777x_CHAN_S(index, _ext_info) \
|
|
{ \
|
|
.type = IIO_VOLTAGE, \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
|
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
|
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
|
|
.address = (index), \
|
|
.indexed = 1, \
|
|
.channel = (index), \
|
|
.scan_index = (index), \
|
|
.ext_info = (_ext_info), \
|
|
.scan_type = { \
|
|
.sign = 's', \
|
|
.realbits = 24, \
|
|
.storagebits = 32, \
|
|
.endianness = IIO_BE, \
|
|
}, \
|
|
}
|
|
|
|
#define AD777x_CHAN_NO_FILTER_S(index) \
|
|
AD777x_CHAN_S(index, NULL)
|
|
|
|
#define AD777x_CHAN_FILTER_S(index) \
|
|
AD777x_CHAN_S(index, ad7779_ext_filter)
|
|
static const struct iio_chan_spec ad7779_channels[] = {
|
|
AD777x_CHAN_NO_FILTER_S(0),
|
|
AD777x_CHAN_NO_FILTER_S(1),
|
|
AD777x_CHAN_NO_FILTER_S(2),
|
|
AD777x_CHAN_NO_FILTER_S(3),
|
|
AD777x_CHAN_NO_FILTER_S(4),
|
|
AD777x_CHAN_NO_FILTER_S(5),
|
|
AD777x_CHAN_NO_FILTER_S(6),
|
|
AD777x_CHAN_NO_FILTER_S(7),
|
|
IIO_CHAN_SOFT_TIMESTAMP(8),
|
|
};
|
|
|
|
static const struct iio_chan_spec ad7779_channels_filter[] = {
|
|
AD777x_CHAN_FILTER_S(0),
|
|
AD777x_CHAN_FILTER_S(1),
|
|
AD777x_CHAN_FILTER_S(2),
|
|
AD777x_CHAN_FILTER_S(3),
|
|
AD777x_CHAN_FILTER_S(4),
|
|
AD777x_CHAN_FILTER_S(5),
|
|
AD777x_CHAN_FILTER_S(6),
|
|
AD777x_CHAN_FILTER_S(7),
|
|
IIO_CHAN_SOFT_TIMESTAMP(8),
|
|
};
|
|
|
|
static const struct iio_buffer_setup_ops ad7779_buffer_setup_ops = {
|
|
.preenable = ad7779_buffer_preenable,
|
|
.postdisable = ad7779_buffer_postdisable,
|
|
};
|
|
|
|
static const struct iio_trigger_ops ad7779_trigger_ops = {
|
|
.validate_device = iio_trigger_validate_own_device,
|
|
};
|
|
|
|
static int ad7779_conf(struct ad7779_state *st, struct gpio_desc *start_gpio)
|
|
{
|
|
int ret;
|
|
|
|
ret = ad7779_spi_write_mask(st, AD7779_REG_GEN_ERR_REG_1_EN,
|
|
AD7779_SPI_CRC_EN_MSK,
|
|
FIELD_PREP(AD7779_SPI_CRC_EN_MSK, 1));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1,
|
|
AD7779_USRMOD_INIT_MSK,
|
|
FIELD_PREP(AD7779_USRMOD_INIT_MSK, 5));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT,
|
|
AD7779_DCLK_CLK_DIV_MSK,
|
|
FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 1));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write_mask(st, AD7779_REG_ADC_MUX_CONFIG,
|
|
AD7779_REFMUX_CTRL_MSK,
|
|
FIELD_PREP(AD7779_REFMUX_CTRL_MSK, 1));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_set_sampling_frequency(st, AD7779_DEFAULT_SAMPLING_FREQ);
|
|
if (ret)
|
|
return ret;
|
|
|
|
gpiod_set_value(start_gpio, 0);
|
|
/* Start setup time */
|
|
fsleep(15);
|
|
gpiod_set_value(start_gpio, 1);
|
|
/* Start setup time */
|
|
fsleep(15);
|
|
gpiod_set_value(start_gpio, 0);
|
|
/* Start setup time */
|
|
fsleep(15);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad7779_probe(struct spi_device *spi)
|
|
{
|
|
struct iio_dev *indio_dev;
|
|
struct ad7779_state *st;
|
|
struct gpio_desc *reset_gpio, *start_gpio;
|
|
struct device *dev = &spi->dev;
|
|
int ret = -EINVAL;
|
|
|
|
if (!spi->irq)
|
|
return dev_err_probe(dev, ret, "DRDY irq not present\n");
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
st = iio_priv(indio_dev);
|
|
|
|
ret = devm_regulator_bulk_get_enable(dev,
|
|
ARRAY_SIZE(ad7779_power_supplies),
|
|
ad7779_power_supplies);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"failed to get and enable supplies\n");
|
|
|
|
st->mclk = devm_clk_get_enabled(dev, "mclk");
|
|
if (IS_ERR(st->mclk))
|
|
return PTR_ERR(st->mclk);
|
|
|
|
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
|
if (IS_ERR(reset_gpio))
|
|
return PTR_ERR(reset_gpio);
|
|
|
|
start_gpio = devm_gpiod_get(dev, "start", GPIOD_OUT_HIGH);
|
|
if (IS_ERR(start_gpio))
|
|
return PTR_ERR(start_gpio);
|
|
|
|
crc8_populate_msb(ad7779_crc8_table, AD7779_CRC8_POLY);
|
|
st->spi = spi;
|
|
|
|
st->chip_info = spi_get_device_match_data(spi);
|
|
if (!st->chip_info)
|
|
return -ENODEV;
|
|
|
|
ret = ad7779_reset(indio_dev, reset_gpio);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_conf(st, start_gpio);
|
|
if (ret)
|
|
return ret;
|
|
|
|
indio_dev->name = st->chip_info->name;
|
|
indio_dev->info = &ad7779_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->channels = st->chip_info->channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(ad7779_channels);
|
|
|
|
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
|
|
iio_device_id(indio_dev));
|
|
if (!st->trig)
|
|
return -ENOMEM;
|
|
|
|
st->trig->ops = &ad7779_trigger_ops;
|
|
|
|
iio_trigger_set_drvdata(st->trig, st);
|
|
|
|
ret = devm_request_irq(dev, spi->irq, iio_trigger_generic_data_rdy_poll,
|
|
IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
|
|
st->trig);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "request IRQ %d failed\n",
|
|
st->spi->irq);
|
|
|
|
ret = devm_iio_trigger_register(dev, st->trig);
|
|
if (ret)
|
|
return ret;
|
|
|
|
indio_dev->trig = iio_trigger_get(st->trig);
|
|
|
|
init_completion(&st->completion);
|
|
|
|
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
|
&iio_pollfunc_store_time,
|
|
&ad7779_trigger_handler,
|
|
&ad7779_buffer_setup_ops);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT,
|
|
AD7779_DCLK_CLK_DIV_MSK,
|
|
FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return devm_iio_device_register(dev, indio_dev);
|
|
}
|
|
|
|
static int ad7779_suspend(struct device *dev)
|
|
{
|
|
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
|
|
return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1,
|
|
AD7779_MOD_POWERMODE_MSK,
|
|
FIELD_PREP(AD7779_MOD_POWERMODE_MSK,
|
|
AD7779_LOW_POWER));
|
|
}
|
|
|
|
static int ad7779_resume(struct device *dev)
|
|
{
|
|
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
|
struct ad7779_state *st = iio_priv(indio_dev);
|
|
|
|
return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1,
|
|
AD7779_MOD_POWERMODE_MSK,
|
|
FIELD_PREP(AD7779_MOD_POWERMODE_MSK,
|
|
AD7779_HIGH_POWER));
|
|
}
|
|
|
|
static DEFINE_SIMPLE_DEV_PM_OPS(ad7779_pm_ops, ad7779_suspend, ad7779_resume);
|
|
|
|
static const struct ad7779_chip_info ad7770_chip_info = {
|
|
.name = "ad7770",
|
|
.channels = ad7779_channels,
|
|
};
|
|
|
|
static const struct ad7779_chip_info ad7771_chip_info = {
|
|
.name = "ad7771",
|
|
.channels = ad7779_channels_filter,
|
|
};
|
|
|
|
static const struct ad7779_chip_info ad7779_chip_info = {
|
|
.name = "ad7779",
|
|
.channels = ad7779_channels,
|
|
};
|
|
|
|
static const struct spi_device_id ad7779_id[] = {
|
|
{
|
|
.name = "ad7770",
|
|
.driver_data = (kernel_ulong_t)&ad7770_chip_info,
|
|
},
|
|
{
|
|
.name = "ad7771",
|
|
.driver_data = (kernel_ulong_t)&ad7771_chip_info,
|
|
},
|
|
{
|
|
.name = "ad7779",
|
|
.driver_data = (kernel_ulong_t)&ad7779_chip_info,
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, ad7779_id);
|
|
|
|
static const struct of_device_id ad7779_of_table[] = {
|
|
{
|
|
.compatible = "adi,ad7770",
|
|
.data = &ad7770_chip_info,
|
|
},
|
|
{
|
|
.compatible = "adi,ad7771",
|
|
.data = &ad7771_chip_info,
|
|
},
|
|
{
|
|
.compatible = "adi,ad7779",
|
|
.data = &ad7779_chip_info,
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ad7779_of_table);
|
|
|
|
static struct spi_driver ad7779_driver = {
|
|
.driver = {
|
|
.name = "ad7779",
|
|
.pm = pm_sleep_ptr(&ad7779_pm_ops),
|
|
.of_match_table = ad7779_of_table,
|
|
},
|
|
.probe = ad7779_probe,
|
|
.id_table = ad7779_id,
|
|
};
|
|
module_spi_driver(ad7779_driver);
|
|
|
|
MODULE_AUTHOR("Ramona Alexandra Nechita <ramona.nechita@analog.com>");
|
|
MODULE_DESCRIPTION("Analog Devices AD7779 ADC");
|
|
MODULE_LICENSE("GPL");
|