// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD4851 DAS driver
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#define AD4851_REG_INTERFACE_CONFIG_A 0 x00
#define AD4851_REG_INTERFACE_CONFIG_B 0 x01
#define AD4851_REG_PRODUCT_ID_L 0 x04
#define AD4851_REG_PRODUCT_ID_H 0 x05
#define AD4851_REG_DEVICE_CTRL 0 x25
#define AD4851_REG_PACKET 0 x26
#define AD4851_REG_OVERSAMPLE 0 x27
#define AD4851_REG_CH_CONFIG_BASE 0 x2A
#define AD4851_REG_CHX_SOFTSPAN(ch) ((0 x12 * (ch)) + AD4851_REG_CH_CONFIG_BASE)
#define AD4851_REG_CHX_OFFSET(ch) (AD4851_REG_CHX_SOFTSPAN(ch) + 0 x01)
#define AD4851_REG_CHX_OFFSET_LSB(ch) AD4851_REG_CHX_OFFSET(ch)
#define AD4851_REG_CHX_OFFSET_MID(ch) (AD4851_REG_CHX_OFFSET_LSB(ch) + 0 x01)
#define AD4851_REG_CHX_OFFSET_MSB(ch) (AD4851_REG_CHX_OFFSET_MID(ch) + 0 x01)
#define AD4851_REG_CHX_GAIN(ch) (AD4851_REG_CHX_OFFSET(ch) + 0 x03)
#define AD4851_REG_CHX_GAIN_LSB(ch) AD4851_REG_CHX_GAIN(ch)
#define AD4851_REG_CHX_GAIN_MSB(ch) (AD4851_REG_CHX_GAIN(ch) + 0 x01)
#define AD4851_REG_CHX_PHASE(ch) (AD4851_REG_CHX_GAIN(ch) + 0 x02)
#define AD4851_REG_CHX_PHASE_LSB(ch) AD4851_REG_CHX_PHASE(ch)
#define AD4851_REG_CHX_PHASE_MSB(ch) (AD4851_REG_CHX_PHASE_LSB(ch) + 0 x01)
#define AD4851_REG_TESTPAT_0(c) (0 x38 + (c) * 0 x12)
#define AD4851_REG_TESTPAT_1(c) (0 x39 + (c) * 0 x12)
#define AD4851_REG_TESTPAT_2(c) (0 x3A + (c) * 0 x12)
#define AD4851_REG_TESTPAT_3(c) (0 x3B + (c) * 0 x12)
#define AD4851_SW_RESET (BIT(7 ) | BIT(0 ))
#define AD4851_SDO_ENABLE BIT(4 )
#define AD4851_SINGLE_INSTRUCTION BIT(7 )
#define AD4851_REFBUF BIT(2 )
#define AD4851_REFSEL BIT(1 )
#define AD4851_ECHO_CLOCK_MODE BIT(0 )
#define AD4851_PACKET_FORMAT_0 0
#define AD4851_PACKET_FORMAT_1 1
#define AD4851_PACKET_FORMAT_MASK GENMASK(1 , 0 )
#define AD4851_OS_EN_MSK BIT(7 )
#define AD4851_OS_RATIO_MSK GENMASK(3 , 0 )
#define AD4851_TEST_PAT BIT(2 )
#define AD4858_PACKET_SIZE_20 0
#define AD4858_PACKET_SIZE_24 1
#define AD4858_PACKET_SIZE_32 2
#define AD4857_PACKET_SIZE_16 0
#define AD4857_PACKET_SIZE_24 1
#define AD4851_TESTPAT_0_DEFAULT 0 x2A
#define AD4851_TESTPAT_1_DEFAULT 0 x3C
#define AD4851_TESTPAT_2_DEFAULT 0 xCE
#define AD4851_TESTPAT_3_DEFAULT(c) (0 x0A + (0 x10 * (c)))
#define AD4851_SOFTSPAN_0V_2V5 0
#define AD4851_SOFTSPAN_N2V5_2V5 1
#define AD4851_SOFTSPAN_0V_5V 2
#define AD4851_SOFTSPAN_N5V_5V 3
#define AD4851_SOFTSPAN_0V_6V25 4
#define AD4851_SOFTSPAN_N6V25_6V25 5
#define AD4851_SOFTSPAN_0V_10V 6
#define AD4851_SOFTSPAN_N10V_10V 7
#define AD4851_SOFTSPAN_0V_12V5 8
#define AD4851_SOFTSPAN_N12V5_12V5 9
#define AD4851_SOFTSPAN_0V_20V 10
#define AD4851_SOFTSPAN_N20V_20V 11
#define AD4851_SOFTSPAN_0V_25V 12
#define AD4851_SOFTSPAN_N25V_25V 13
#define AD4851_SOFTSPAN_0V_40V 14
#define AD4851_SOFTSPAN_N40V_40V 15
#define AD4851_MAX_LANES 8
#define AD4851_MAX_IODELAY 32
#define AD4851_T_CNVH_NS 40
#define AD4851_T_CNVH_NS_MARGIN 10
#define AD4841_MAX_SCALE_AVAIL 8
#define AD4851_MAX_CH_NR 8
#define AD4851_CH_START 0
struct ad4851_scale {
unsigned int scale_val;
u8 reg_val;
};
static const struct ad4851_scale ad4851_scale_table_unipolar[] = {
{ 2500 , 0 x0 },
{ 5000 , 0 x2 },
{ 6250 , 0 x4 },
{ 10000 , 0 x6 },
{ 12500 , 0 x8 },
{ 20000 , 0 xA },
{ 25000 , 0 xC },
{ 40000 , 0 xE },
};
static const struct ad4851_scale ad4851_scale_table_bipolar[] = {
{ 5000 , 0 x1 },
{ 10000 , 0 x3 },
{ 12500 , 0 x5 },
{ 20000 , 0 x7 },
{ 25000 , 0 x9 },
{ 40000 , 0 xB },
{ 50000 , 0 xD },
{ 80000 , 0 xF },
};
static const unsigned int ad4851_scale_avail_unipolar[] = {
2500 ,
5000 ,
6250 ,
10000 ,
12500 ,
20000 ,
25000 ,
40000 ,
};
static const unsigned int ad4851_scale_avail_bipolar[] = {
5000 ,
10000 ,
12500 ,
20000 ,
25000 ,
40000 ,
50000 ,
80000 ,
};
struct ad4851_chip_info {
const char *name;
unsigned int product_id;
int num_scales;
unsigned long max_sample_rate_hz;
unsigned int resolution;
unsigned int max_channels;
int (*parse_channels)(struct iio_dev *indio_dev);
};
enum {
AD4851_SCAN_TYPE_NORMAL,
AD4851_SCAN_TYPE_RESOLUTION_BOOST,
};
struct ad4851_state {
struct spi_device *spi;
struct pwm_device *cnv;
struct iio_backend *back;
/*
* Synchronize access to members the of driver state, and ensure
* atomicity of consecutive regmap operations.
*/
struct mutex lock;
struct regmap *regmap;
const struct ad4851_chip_info *info;
struct gpio_desc *pd_gpio;
bool resolution_boost_enabled;
unsigned long cnv_trigger_rate_hz;
unsigned int osr;
bool vrefbuf_en;
bool vrefio_en;
bool bipolar_ch[AD4851_MAX_CH_NR];
unsigned int scales_unipolar[AD4841_MAX_SCALE_AVAIL][2 ];
unsigned int scales_bipolar[AD4841_MAX_SCALE_AVAIL][2 ];
};
static int ad4851_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct ad4851_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
static int ad4851_set_sampling_freq(struct ad4851_state *st, unsigned int freq)
{
struct pwm_state cnv_state = {
.duty_cycle = AD4851_T_CNVH_NS + AD4851_T_CNVH_NS_MARGIN,
.enabled = true ,
};
int ret;
freq = clamp(freq, 1 , st->info->max_sample_rate_hz);
cnv_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq);
ret = pwm_apply_might_sleep(st->cnv, &cnv_state);
if (ret)
return ret;
st->cnv_trigger_rate_hz = freq;
return 0 ;
}
static const int ad4851_oversampling_ratios[] = {
1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 ,
256 , 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 ,
65536 ,
};
static int ad4851_osr_to_regval(unsigned int ratio)
{
int i;
for (i = 1 ; i < ARRAY_SIZE(ad4851_oversampling_ratios); i++)
if (ratio == ad4851_oversampling_ratios[i])
return i - 1 ;
return -EINVAL;
}
static int __ad4851_get_scale(struct iio_dev *indio_dev, int scale_tbl,
unsigned int *val, unsigned int *val2)
{
const struct iio_scan_type *scan_type;
unsigned int tmp;
scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0 ]);
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
tmp = ((u64)scale_tbl * MICRO) >> scan_type->realbits;
*val = tmp / MICRO;
*val2 = tmp % MICRO;
return 0 ;
}
static int ad4851_scale_fill(struct iio_dev *indio_dev)
{
struct ad4851_state *st = iio_priv(indio_dev);
unsigned int i, val1, val2;
int ret;
for (i = 0 ; i < ARRAY_SIZE(ad4851_scale_avail_unipolar); i++) {
ret = __ad4851_get_scale(indio_dev,
ad4851_scale_avail_unipolar[i],
&val1, &val2);
if (ret)
return ret;
st->scales_unipolar[i][0 ] = val1;
st->scales_unipolar[i][1 ] = val2;
}
for (i = 0 ; i < ARRAY_SIZE(ad4851_scale_avail_bipolar); i++) {
ret = __ad4851_get_scale(indio_dev,
ad4851_scale_avail_bipolar[i],
&val1, &val2);
if (ret)
return ret;
st->scales_bipolar[i][0 ] = val1;
st->scales_bipolar[i][1 ] = val2;
}
return 0 ;
}
static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev,
unsigned int osr)
{
struct ad4851_state *st = iio_priv(indio_dev);
int val, ret;
guard(mutex)(&st->lock);
if (osr == 1 ) {
ret = regmap_clear_bits(st->regmap, AD4851_REG_OVERSAMPLE,
AD4851_OS_EN_MSK);
if (ret)
return ret;
} else {
val = ad4851_osr_to_regval(osr);
if (val < 0 )
return -EINVAL;
ret = regmap_update_bits(st->regmap, AD4851_REG_OVERSAMPLE,
AD4851_OS_EN_MSK |
AD4851_OS_RATIO_MSK,
FIELD_PREP(AD4851_OS_EN_MSK, 1 ) |
FIELD_PREP(AD4851_OS_RATIO_MSK, val));
if (ret)
return ret;
}
/* Channel is ignored by the backend being used here */
ret = iio_backend_oversampling_ratio_set(st->back, 0 , osr);
if (ret)
return ret;
switch (st->info->resolution) {
case 20 :
switch (osr) {
case 0 :
return -EINVAL;
case 1 :
val = 20 ;
break ;
default :
val = 24 ;
break ;
}
break ;
case 16 :
val = 16 ;
break ;
default :
return -EINVAL;
}
ret = iio_backend_data_size_set(st->back, val);
if (ret)
return ret;
if (osr == 1 || st->info->resolution == 16 ) {
ret = regmap_clear_bits(st->regmap, AD4851_REG_PACKET,
AD4851_PACKET_FORMAT_MASK);
if (ret)
return ret;
st->resolution_boost_enabled = false ;
} else {
ret = regmap_update_bits(st->regmap, AD4851_REG_PACKET,
AD4851_PACKET_FORMAT_MASK,
FIELD_PREP(AD4851_PACKET_FORMAT_MASK, 1 ));
if (ret)
return ret;
st->resolution_boost_enabled = true ;
}
if (st->osr != osr) {
ret = ad4851_scale_fill(indio_dev);
if (ret)
return ret;
st->osr = osr;
}
return 0 ;
}
static int ad4851_get_oversampling_ratio(struct ad4851_state *st, unsigned int *val)
{
unsigned int osr;
int ret;
guard(mutex)(&st->lock);
ret = regmap_read(st->regmap, AD4851_REG_OVERSAMPLE, &osr);
if (ret)
return ret;
if (!FIELD_GET(AD4851_OS_EN_MSK, osr))
*val = 1 ;
else
*val = ad4851_oversampling_ratios[FIELD_GET(AD4851_OS_RATIO_MSK, osr) + 1 ];
st->osr = *val;
return IIO_VAL_INT;
}
static void ad4851_pwm_disable(void *data)
{
pwm_disable(data);
}
static int ad4851_setup(struct ad4851_state *st)
{
unsigned int product_id;
int ret;
if (st->pd_gpio) {
/* To initiate a global reset, bring the PD pin high twice */
gpiod_set_value(st->pd_gpio, 1 );
fsleep(1 );
gpiod_set_value(st->pd_gpio, 0 );
fsleep(1 );
gpiod_set_value(st->pd_gpio, 1 );
fsleep(1 );
gpiod_set_value(st->pd_gpio, 0 );
fsleep(1000 );
} else {
ret = regmap_set_bits(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
AD4851_SW_RESET);
if (ret)
return ret;
}
if (st->vrefbuf_en) {
ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
AD4851_REFBUF);
if (ret)
return ret;
}
if (st->vrefio_en) {
ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
AD4851_REFSEL);
if (ret)
return ret;
}
ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_B,
AD4851_SINGLE_INSTRUCTION);
if (ret)
return ret;
if (!(st->spi->mode & SPI_3WIRE)) {
ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
AD4851_SDO_ENABLE);
if (ret)
return ret;
}
ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id);
if (ret)
return ret;
if (product_id != st->info->product_id)
dev_info(&st->spi->dev, "Unknown product ID: 0x%02X\n" ,
product_id);
ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
AD4851_ECHO_CLOCK_MODE);
if (ret)
return ret;
return regmap_write(st->regmap, AD4851_REG_PACKET, 0 );
}
/*
* Find the longest consecutive sequence of false values from field
* and return starting index.
*/
static int ad4851_find_opt(const unsigned long *field, unsigned int start,
unsigned int nbits, unsigned int *val)
{
unsigned int bit = start, end, start_cnt, cnt = 0 ;
for_each_clear_bitrange_from(bit, end, field, start + nbits) {
if (end - bit > cnt) {
cnt = end - bit;
start_cnt = bit - start;
}
}
if (!cnt)
return -ENOENT;
*val = start_cnt;
return cnt;
}
static int ad4851_calibrate(struct iio_dev *indio_dev)
{
struct ad4851_state *st = iio_priv(indio_dev);
unsigned int opt_delay, num_lanes, delay, i, s;
enum iio_backend_interface_type interface_type;
DECLARE_BITMAP(pn_status, AD4851_MAX_LANES * AD4851_MAX_IODELAY);
bool status;
int c, ret;
ret = iio_backend_interface_type_get(st->back, &interface_type);
if (ret)
return ret;
switch (interface_type) {
case IIO_BACKEND_INTERFACE_SERIAL_CMOS:
num_lanes = indio_dev->num_channels;
break ;
case IIO_BACKEND_INTERFACE_SERIAL_LVDS:
num_lanes = 1 ;
break ;
default :
return -EINVAL;
}
if (st->info->resolution == 16 ) {
ret = iio_backend_data_size_set(st->back, 24 );
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_PACKET,
AD4851_TEST_PAT | AD4857_PACKET_SIZE_24);
if (ret)
return ret;
} else {
ret = iio_backend_data_size_set(st->back, 32 );
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_PACKET,
AD4851_TEST_PAT | AD4858_PACKET_SIZE_32);
if (ret)
return ret;
}
for (i = 0 ; i < indio_dev->num_channels; i++) {
ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_0(i),
AD4851_TESTPAT_0_DEFAULT);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_1(i),
AD4851_TESTPAT_1_DEFAULT);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_2(i),
AD4851_TESTPAT_2_DEFAULT);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_3(i),
AD4851_TESTPAT_3_DEFAULT(i));
if (ret)
return ret;
ret = iio_backend_chan_enable(st->back,
indio_dev->channels[i].channel);
if (ret)
return ret;
}
for (i = 0 ; i < num_lanes; i++) {
for (delay = 0 ; delay < AD4851_MAX_IODELAY; delay++) {
ret = iio_backend_iodelay_set(st->back, i, delay);
if (ret)
return ret;
ret = iio_backend_chan_status(st->back, i, &status);
if (ret)
return ret;
__assign_bit(i * AD4851_MAX_IODELAY + delay, pn_status,
status);
}
}
for (i = 0 ; i < num_lanes; i++) {
c = ad4851_find_opt(pn_status, i * AD4851_MAX_IODELAY,
AD4851_MAX_IODELAY, &s);
if (c < 0 )
return c;
opt_delay = s + c / 2 ;
ret = iio_backend_iodelay_set(st->back, i, opt_delay);
if (ret)
return ret;
}
for (i = 0 ; i < indio_dev->num_channels; i++) {
ret = iio_backend_chan_disable(st->back, i);
if (ret)
return ret;
}
ret = iio_backend_data_size_set(st->back, 20 );
if (ret)
return ret;
return regmap_write(st->regmap, AD4851_REG_PACKET, 0 );
}
static int ad4851_get_calibscale(struct ad4851_state *st, int ch, int *val, int *val2)
{
unsigned int reg_val;
int gain;
int ret;
guard(mutex)(&st->lock);
ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), ®_val);
if (ret)
return ret;
gain = reg_val << 8 ;
ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), ®_val);
if (ret)
return ret;
gain |= reg_val;
*val = gain;
*val2 = 15 ;
return IIO_VAL_FRACTIONAL_LOG2;
}
static int ad4851_set_calibscale(struct ad4851_state *st, int ch, int val,
int val2)
{
u64 gain;
u8 buf[2 ];
int ret;
if (val < 0 || val2 < 0 )
return -EINVAL;
gain = val * MICRO + val2;
gain = DIV_U64_ROUND_CLOSEST(gain * 32768 , MICRO);
put_unaligned_be16(gain, buf);
guard(mutex)(&st->lock);
ret = regmap_write(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), buf[0 ]);
if (ret)
return ret;
return regmap_write(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), buf[1 ]);
}
static int ad4851_get_calibbias(struct ad4851_state *st, int ch, int *val)
{
unsigned int lsb, mid, msb;
int ret;
guard(mutex)(&st->lock);
/*
* After testing, the bulk_write operations doesn't work as expected
* here since the cs needs to be raised after each byte transaction.
*/
ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), &msb);
if (ret)
return ret;
ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), &mid);
if (ret)
return ret;
ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), &lsb);
if (ret)
return ret;
if (st->info->resolution == 16 ) {
*val = msb << 8 ;
*val |= mid;
*val = sign_extend32(*val, 15 );
} else {
*val = msb << 12 ;
*val |= mid << 4 ;
*val |= lsb >> 4 ;
*val = sign_extend32(*val, 19 );
}
return IIO_VAL_INT;
}
static int ad4851_set_calibbias(struct ad4851_state *st, int ch, int val)
{
u8 buf[3 ];
int ret;
if (val < 0 )
return -EINVAL;
if (st->info->resolution == 16 )
put_unaligned_be16(val, buf);
else
put_unaligned_be24(val << 4 , buf);
guard(mutex)(&st->lock);
/*
* After testing, the bulk_write operations doesn't work as expected
* here since the cs needs to be raised after each byte transaction.
*/
ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), buf[2 ]);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), buf[1 ]);
if (ret)
return ret;
return regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), buf[0 ]);
}
static int ad4851_set_scale(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int val, int val2)
{
struct ad4851_state *st = iio_priv(indio_dev);
unsigned int scale_val[2 ];
unsigned int i;
const struct ad4851_scale *scale_table;
size_t table_size;
int ret;
if (st->bipolar_ch[chan->channel]) {
scale_table = ad4851_scale_table_bipolar;
table_size = ARRAY_SIZE(ad4851_scale_table_bipolar);
} else {
scale_table = ad4851_scale_table_unipolar;
table_size = ARRAY_SIZE(ad4851_scale_table_unipolar);
}
for (i = 0 ; i < table_size; i++) {
ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val,
&scale_val[0 ], &scale_val[1 ]);
if (ret)
return ret;
if (scale_val[0 ] != val || scale_val[1 ] != val2)
continue ;
return regmap_write(st->regmap,
AD4851_REG_CHX_SOFTSPAN(chan->channel),
scale_table[i].reg_val);
}
return -EINVAL;
}
static int ad4851_get_scale(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2)
{
struct ad4851_state *st = iio_priv(indio_dev);
const struct ad4851_scale *scale_table;
size_t table_size;
u32 softspan_val;
int i, ret;
if (st->bipolar_ch[chan->channel]) {
scale_table = ad4851_scale_table_bipolar;
table_size = ARRAY_SIZE(ad4851_scale_table_bipolar);
} else {
scale_table = ad4851_scale_table_unipolar;
table_size = ARRAY_SIZE(ad4851_scale_table_unipolar);
}
ret = regmap_read(st->regmap, AD4851_REG_CHX_SOFTSPAN(chan->channel),
&softspan_val);
if (ret)
return ret;
for (i = 0 ; i < table_size; i++) {
if (softspan_val == scale_table[i].reg_val)
break ;
}
if (i == table_size)
return -EIO;
ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, val,
val2);
if (ret)
return ret;
return IIO_VAL_INT_PLUS_MICRO;
}
static int ad4851_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long info)
{
struct ad4851_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->cnv_trigger_rate_hz;
*val2 = st->osr;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_CALIBSCALE:
return ad4851_get_calibscale(st, chan->channel, val, val2);
case IIO_CHAN_INFO_SCALE:
return ad4851_get_scale(indio_dev, chan, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return ad4851_get_calibbias(st, chan->channel, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4851_get_oversampling_ratio(st, val);
default :
return -EINVAL;
}
}
static int ad4851_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct ad4851_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < 0 || val2 < 0 )
return -EINVAL;
return ad4851_set_sampling_freq(st, val * st->osr + val2 * st->osr / MICRO);
case IIO_CHAN_INFO_SCALE:
return ad4851_set_scale(indio_dev, chan, val, val2);
case IIO_CHAN_INFO_CALIBSCALE:
return ad4851_set_calibscale(st, chan->channel, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return ad4851_set_calibbias(st, chan->channel, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4851_set_oversampling_ratio(indio_dev, val);
default :
return -EINVAL;
}
}
static int ad4851_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad4851_state *st = iio_priv(indio_dev);
unsigned int c;
int ret;
for (c = 0 ; c < indio_dev->num_channels; c++) {
if (test_bit(c, scan_mask))
ret = iio_backend_chan_enable(st->back, c);
else
ret = iio_backend_chan_disable(st->back, c);
if (ret)
return ret;
}
return 0 ;
}
static int ad4851_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct ad4851_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (st->bipolar_ch[chan->channel]) {
*vals = (const int *)st->scales_bipolar;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(ad4851_scale_avail_bipolar) * 2 ;
} else {
*vals = (const int *)st->scales_unipolar;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(ad4851_scale_avail_unipolar) * 2 ;
}
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ad4851_oversampling_ratios;
*length = ARRAY_SIZE(ad4851_oversampling_ratios);
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default :
return -EINVAL;
}
}
static const struct iio_scan_type ad4851_scan_type_20_u[] = {
[AD4851_SCAN_TYPE_NORMAL] = {
.sign = 'u' ,
.realbits = 20 ,
.storagebits = 32 ,
},
[AD4851_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 'u' ,
.realbits = 24 ,
.storagebits = 32 ,
},
};
static const struct iio_scan_type ad4851_scan_type_20_b[] = {
[AD4851_SCAN_TYPE_NORMAL] = {
.sign = 's' ,
.realbits = 20 ,
.storagebits = 32 ,
},
[AD4851_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 's' ,
.realbits = 24 ,
.storagebits = 32 ,
},
};
static int ad4851_get_current_scan_type(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad4851_state *st = iio_priv(indio_dev);
return st->resolution_boost_enabled ? AD4851_SCAN_TYPE_RESOLUTION_BOOST
: AD4851_SCAN_TYPE_NORMAL;
}
#define AD4851_IIO_CHANNEL \
.type = IIO_VOLTAGE, \
.info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.indexed = 1
/*
* In case of AD4858_IIO_CHANNEL the scan_type is handled dynamically during the
* parse_channels function.
*/
#define AD4858_IIO_CHANNEL \
{ \
AD4851_IIO_CHANNEL \
}
#define AD4857_IIO_CHANNEL \
{ \
AD4851_IIO_CHANNEL, \
.scan_type = { \
.sign = 'u' , \
.realbits = 16 , \
.storagebits = 16 , \
}, \
}
static int ad4851_parse_channels_common(struct iio_dev *indio_dev,
struct iio_chan_spec **chans,
const struct iio_chan_spec ad4851_chan)
{
struct ad4851_state *st = iio_priv(indio_dev);
struct device *dev = &st->spi->dev;
struct iio_chan_spec *channels, *chan_start;
unsigned int num_channels, reg;
unsigned int index = 0 ;
int ret;
num_channels = device_get_child_node_count(dev);
if (num_channels > AD4851_MAX_CH_NR)
return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n" ,
num_channels);
channels = devm_kcalloc(dev, num_channels, sizeof (*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
chan_start = channels;
device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg" , ®);
if (ret)
return dev_err_probe(dev, ret,
"Missing channel number\n" );
if (reg >= AD4851_MAX_CH_NR)
return dev_err_probe(dev, -EINVAL,
"Invalid channel number\n" );
*channels = ad4851_chan;
channels->scan_index = index++;
channels->channel = reg;
if (fwnode_property_present(child, "diff-channels" )) {
channels->channel2 = reg + st->info->max_channels;
channels->differential = 1 ;
}
st->bipolar_ch[reg] = fwnode_property_read_bool(child, "bipolar" );
if (st->bipolar_ch[reg]) {
channels->scan_type.sign = 's' ;
} else {
ret = regmap_write(st->regmap, AD4851_REG_CHX_SOFTSPAN(reg),
AD4851_SOFTSPAN_0V_40V);
if (ret)
return ret;
}
channels++;
}
*chans = chan_start;
return num_channels;
}
static int ad4857_parse_channels(struct iio_dev *indio_dev)
{
struct iio_chan_spec *ad4851_channels;
const struct iio_chan_spec ad4851_chan = AD4857_IIO_CHANNEL;
int ret;
ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels,
ad4851_chan);
if (ret < 0 )
return ret;
indio_dev->channels = ad4851_channels;
indio_dev->num_channels = ret;
return 0 ;
}
static int ad4858_parse_channels(struct iio_dev *indio_dev)
{
struct ad4851_state *st = iio_priv(indio_dev);
struct device *dev = &st->spi->dev;
struct iio_chan_spec *ad4851_channels;
const struct iio_chan_spec ad4851_chan = AD4858_IIO_CHANNEL;
int ret, i = 0 ;
ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels,
ad4851_chan);
if (ret < 0 )
return ret;
device_for_each_child_node_scoped(dev, child) {
ad4851_channels[i].has_ext_scan_type = 1 ;
if (fwnode_property_read_bool(child, "bipolar" )) {
ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_b;
ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_b);
} else {
ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_u;
ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_u);
}
i++;
}
indio_dev->channels = ad4851_channels;
indio_dev->num_channels = ret;
return 0 ;
}
/*
* parse_channels() function handles the rest of the channel related attributes
* that are usually are stored in the chip info structure.
*/
static const struct ad4851_chip_info ad4851_info = {
.name = "ad4851" ,
.product_id = 0 x67,
.max_sample_rate_hz = 250 * KILO,
.resolution = 16 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4857_parse_channels,
};
static const struct ad4851_chip_info ad4852_info = {
.name = "ad4852" ,
.product_id = 0 x66,
.max_sample_rate_hz = 250 * KILO,
.resolution = 20 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4858_parse_channels,
};
static const struct ad4851_chip_info ad4853_info = {
.name = "ad4853" ,
.product_id = 0 x65,
.max_sample_rate_hz = 1 * MEGA,
.resolution = 16 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4857_parse_channels,
};
static const struct ad4851_chip_info ad4854_info = {
.name = "ad4854" ,
.product_id = 0 x64,
.max_sample_rate_hz = 1 * MEGA,
.resolution = 20 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4858_parse_channels,
};
static const struct ad4851_chip_info ad4855_info = {
.name = "ad4855" ,
.product_id = 0 x63,
.max_sample_rate_hz = 250 * KILO,
.resolution = 16 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4857_parse_channels,
};
static const struct ad4851_chip_info ad4856_info = {
.name = "ad4856" ,
.product_id = 0 x62,
.max_sample_rate_hz = 250 * KILO,
.resolution = 20 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4858_parse_channels,
};
static const struct ad4851_chip_info ad4857_info = {
.name = "ad4857" ,
.product_id = 0 x61,
.max_sample_rate_hz = 1 * MEGA,
.resolution = 16 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4857_parse_channels,
};
static const struct ad4851_chip_info ad4858_info = {
.name = "ad4858" ,
.product_id = 0 x60,
.max_sample_rate_hz = 1 * MEGA,
.resolution = 20 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4858_parse_channels,
};
static const struct ad4851_chip_info ad4858i_info = {
.name = "ad4858i" ,
.product_id = 0 x6F,
.max_sample_rate_hz = 1 * MEGA,
.resolution = 20 ,
.max_channels = AD4851_MAX_CH_NR,
.parse_channels = ad4858_parse_channels,
};
static const struct iio_info ad4851_iio_info = {
.debugfs_reg_access = ad4851_reg_access,
.read_raw = ad4851_read_raw,
.write_raw = ad4851_write_raw,
.update_scan_mode = ad4851_update_scan_mode,
.get_current_scan_type = ad4851_get_current_scan_type,
.read_avail = ad4851_read_avail,
};
static const struct regmap_config regmap_config = {
.reg_bits = 16 ,
.val_bits = 8 ,
.read_flag_mask = BIT(7 ),
};
static const char * const ad4851_power_supplies[] = {
"vcc" , "vdd" , "vee" , "vio" ,
};
static int ad4851_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct device *dev = &spi->dev;
struct ad4851_state *st;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof (*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad4851_power_supplies),
ad4851_power_supplies);
if (ret)
return dev_err_probe(dev, ret,
"failed to get and enable supplies\n" );
ret = devm_regulator_get_enable_optional(dev, "vddh" );
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vddh voltage\n" );
ret = devm_regulator_get_enable_optional(dev, "vddl" );
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vddl voltage\n" );
ret = devm_regulator_get_enable_optional(dev, "vrefbuf" );
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vrefbuf voltage\n" );
st->vrefbuf_en = ret != -ENODEV;
ret = devm_regulator_get_enable_optional(dev, "vrefio" );
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vrefio voltage\n" );
st->vrefio_en = ret != -ENODEV;
st->pd_gpio = devm_gpiod_get_optional(dev, "pd" , GPIOD_OUT_LOW);
if (IS_ERR(st->pd_gpio))
return dev_err_probe(dev, PTR_ERR(st->pd_gpio),
"Error on requesting pd GPIO\n" );
st->cnv = devm_pwm_get(dev, NULL);
if (IS_ERR(st->cnv))
return dev_err_probe(dev, PTR_ERR(st->cnv),
"Error on requesting pwm\n" );
st->info = spi_get_device_match_data(spi);
if (!st->info)
return -ENODEV;
st->regmap = devm_regmap_init_spi(spi, ®map_config);
if (IS_ERR(st->regmap))
return PTR_ERR(st->regmap);
ret = ad4851_set_sampling_freq(st, HZ_PER_MHZ);
if (ret)
return ret;
ret = devm_add_action_or_reset(&st->spi->dev, ad4851_pwm_disable,
st->cnv);
if (ret)
return ret;
ret = ad4851_setup(st);
if (ret)
return ret;
indio_dev->name = st->info->name;
indio_dev->info = &ad4851_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = st->info->parse_channels(indio_dev);
if (ret)
return ret;
ret = ad4851_scale_fill(indio_dev);
if (ret)
return ret;
st->back = devm_iio_backend_get(dev, NULL);
if (IS_ERR(st->back))
return PTR_ERR(st->back);
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(dev, st->back);
if (ret)
return ret;
ret = ad4851_calibrate(indio_dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id ad4851_of_match[] = {
{ .compatible = "adi,ad4851" , .data = &ad4851_info, },
{ .compatible = "adi,ad4852" , .data = &ad4852_info, },
{ .compatible = "adi,ad4853" , .data = &ad4853_info, },
{ .compatible = "adi,ad4854" , .data = &ad4854_info, },
{ .compatible = "adi,ad4855" , .data = &ad4855_info, },
{ .compatible = "adi,ad4856" , .data = &ad4856_info, },
{ .compatible = "adi,ad4857" , .data = &ad4857_info, },
{ .compatible = "adi,ad4858" , .data = &ad4858_info, },
{ .compatible = "adi,ad4858i" , .data = &ad4858i_info, },
{ }
};
static const struct spi_device_id ad4851_spi_id[] = {
{ "ad4851" , (kernel_ulong_t)&ad4851_info },
{ "ad4852" , (kernel_ulong_t)&ad4852_info },
{ "ad4853" , (kernel_ulong_t)&ad4853_info },
{ "ad4854" , (kernel_ulong_t)&ad4854_info },
{ "ad4855" , (kernel_ulong_t)&ad4855_info },
{ "ad4856" , (kernel_ulong_t)&ad4856_info },
{ "ad4857" , (kernel_ulong_t)&ad4857_info },
{ "ad4858" , (kernel_ulong_t)&ad4858_info },
{ "ad4858i" , (kernel_ulong_t)&ad4858i_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4851_spi_id);
static struct spi_driver ad4851_driver = {
.probe = ad4851_probe,
.driver = {
.name = "ad4851" ,
.of_match_table = ad4851_of_match,
},
.id_table = ad4851_spi_id,
};
module_spi_driver(ad4851_driver);
MODULE_AUTHOR("Sergiu Cuciurean <sergiu.cuciurean@analog.com>" );
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>" );
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>" );
MODULE_DESCRIPTION("Analog Devices AD4851 DAS driver" );
MODULE_LICENSE("GPL" );
MODULE_IMPORT_NS("IIO_BACKEND" );
Messung V0.5 in Prozent C=93 H=97 G=94
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland