2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00
linux/drivers/iio/adc/ad4851.c
Antoniu Miclaus 499a8cee81 iio: adc: ad4851: fix ad4858 chan pointer handling
The pointer returned from ad4851_parse_channels_common() is incremented
internally as each channel is populated. In ad4858_parse_channels(),
the same pointer was further incremented while setting ext_scan_type
fields for each channel. This resulted in indio_dev->channels being set
to a pointer past the end of the allocated array, potentially causing
memory corruption or undefined behavior.

Fix this by iterating over the channels using an explicit index instead
of incrementing the pointer. This preserves the original base pointer
and ensures all channel metadata is set correctly.

Fixes: 6250803fe2 ("iio: adc: ad4851: add ad485x driver")
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Link: https://patch.msgid.link/20250509101657.6742-1-antoniu.miclaus@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
2025-05-11 14:12:49 +01:00

1316 lines
32 KiB
C

// 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 0x00
#define AD4851_REG_INTERFACE_CONFIG_B 0x01
#define AD4851_REG_PRODUCT_ID_L 0x04
#define AD4851_REG_PRODUCT_ID_H 0x05
#define AD4851_REG_DEVICE_CTRL 0x25
#define AD4851_REG_PACKET 0x26
#define AD4851_REG_OVERSAMPLE 0x27
#define AD4851_REG_CH_CONFIG_BASE 0x2A
#define AD4851_REG_CHX_SOFTSPAN(ch) ((0x12 * (ch)) + AD4851_REG_CH_CONFIG_BASE)
#define AD4851_REG_CHX_OFFSET(ch) (AD4851_REG_CHX_SOFTSPAN(ch) + 0x01)
#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) + 0x01)
#define AD4851_REG_CHX_OFFSET_MSB(ch) (AD4851_REG_CHX_OFFSET_MID(ch) + 0x01)
#define AD4851_REG_CHX_GAIN(ch) (AD4851_REG_CHX_OFFSET(ch) + 0x03)
#define AD4851_REG_CHX_GAIN_LSB(ch) AD4851_REG_CHX_GAIN(ch)
#define AD4851_REG_CHX_GAIN_MSB(ch) (AD4851_REG_CHX_GAIN(ch) + 0x01)
#define AD4851_REG_CHX_PHASE(ch) (AD4851_REG_CHX_GAIN(ch) + 0x02)
#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) + 0x01)
#define AD4851_REG_TESTPAT_0(c) (0x38 + (c) * 0x12)
#define AD4851_REG_TESTPAT_1(c) (0x39 + (c) * 0x12)
#define AD4851_REG_TESTPAT_2(c) (0x3A + (c) * 0x12)
#define AD4851_REG_TESTPAT_3(c) (0x3B + (c) * 0x12)
#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 0x2A
#define AD4851_TESTPAT_1_DEFAULT 0x3C
#define AD4851_TESTPAT_2_DEFAULT 0xCE
#define AD4851_TESTPAT_3_DEFAULT(c) (0x0A + (0x10 * (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, 0x0 },
{ 5000, 0x2 },
{ 6250, 0x4 },
{ 10000, 0x6 },
{ 12500, 0x8 },
{ 20000, 0xA },
{ 25000, 0xC },
{ 40000, 0xE },
};
static const struct ad4851_scale ad4851_scale_table_bipolar[] = {
{ 5000, 0x1 },
{ 10000, 0x3 },
{ 12500, 0x5 },
{ 20000, 0x7 },
{ 25000, 0x9 },
{ 40000, 0xB },
{ 50000, 0xD },
{ 80000, 0xF },
};
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,
const struct iio_chan_spec *chan,
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;
}
ret = iio_backend_oversampling_ratio_set(st->back, 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;
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), &reg_val);
if (ret)
return ret;
gain = reg_val << 8;
ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), &reg_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, chan, 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", &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 = 0x67,
.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 = 0x66,
.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 = 0x65,
.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 = 0x64,
.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 = 0x63,
.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 = 0x62,
.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 = 0x61,
.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 = 0x60,
.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 = 0x6F,
.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, &regmap_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");