iio: adc: Add support for ad4062

The AD4060/AD4062 are versatile, 16-bit/12-bit, successive approximation
register (SAR) analog-to-digital converter (ADC) with low-power and
threshold monitoring modes.

Signed-off-by: Jorge Marques <jorge.marques@analog.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Jorge Marques 2025-12-17 13:13:26 +01:00 committed by Jonathan Cameron
parent 1b1ddab024
commit d5284402d2
4 changed files with 832 additions and 0 deletions

View File

@ -1439,6 +1439,7 @@ S: Supported
W: https://ez.analog.com/linux-software-drivers W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml F: Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
F: Documentation/iio/ad4062.rst F: Documentation/iio/ad4062.rst
F: drivers/iio/adc/ad4062.c
ANALOG DEVICES INC AD4080 DRIVER ANALOG DEVICES INC AD4080 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com> M: Antoniu Miclaus <antoniu.miclaus@analog.com>

View File

@ -70,6 +70,17 @@ config AD4030
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called ad4030. called ad4030.
config AD4062
tristate "Analog Devices AD4062 Driver"
depends on I3C
select REGMAP_I3C
help
Say yes here to build support for Analog Devices AD4062 I3C analog
to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4062.
config AD4080 config AD4080
tristate "Analog Devices AD4080 high speed ADC" tristate "Analog Devices AD4080 high speed ADC"
depends on SPI depends on SPI

View File

@ -11,6 +11,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o obj-$(CONFIG_AD4030) += ad4030.o
obj-$(CONFIG_AD4062) += ad4062.o
obj-$(CONFIG_AD4080) += ad4080.o obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.o obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4170_4) += ad4170-4.o obj-$(CONFIG_AD4170_4) += ad4170-4.o

819
drivers/iio/adc/ad4062.c Normal file
View File

@ -0,0 +1,819 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Analog Devices AD4062 I3C ADC driver
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i3c/device.h>
#include <linux/i3c/master.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/unaligned.h>
#include <linux/util_macros.h>
#define AD4062_REG_INTERFACE_CONFIG_A 0x00
#define AD4062_REG_DEVICE_CONFIG 0x02
#define AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK GENMASK(1, 0)
#define AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE 3
#define AD4062_REG_PROD_ID_1 0x05
#define AD4062_REG_DEVICE_GRADE 0x06
#define AD4062_REG_SCRATCH_PAD 0x0A
#define AD4062_REG_VENDOR_H 0x0D
#define AD4062_REG_STREAM_MODE 0x0E
#define AD4062_REG_INTERFACE_STATUS 0x11
#define AD4062_REG_MODE_SET 0x20
#define AD4062_REG_MODE_SET_ENTER_ADC BIT(0)
#define AD4062_REG_ADC_MODES 0x21
#define AD4062_REG_ADC_MODES_MODE_MSK GENMASK(1, 0)
#define AD4062_REG_ADC_CONFIG 0x22
#define AD4062_REG_ADC_CONFIG_REF_EN_MSK BIT(5)
#define AD4062_REG_ADC_CONFIG_SCALE_EN_MSK BIT(4)
#define AD4062_REG_AVG_CONFIG 0x23
#define AD4062_REG_GP_CONF 0x24
#define AD4062_REG_GP_CONF_MODE_MSK_1 GENMASK(6, 4)
#define AD4062_REG_INTR_CONF 0x25
#define AD4062_REG_INTR_CONF_EN_MSK_1 GENMASK(5, 4)
#define AD4062_REG_TIMER_CONFIG 0x27
#define AD4062_REG_TIMER_CONFIG_FS_MASK GENMASK(7, 4)
#define AD4062_REG_MON_VAL 0x2F
#define AD4062_REG_ADC_IBI_EN 0x31
#define AD4062_REG_ADC_IBI_EN_CONV_TRIGGER BIT(2)
#define AD4062_REG_FUSE_CRC 0x40
#define AD4062_REG_DEVICE_STATUS 0x41
#define AD4062_REG_DEVICE_STATUS_DEVICE_RESET BIT(6)
#define AD4062_REG_IBI_STATUS 0x48
#define AD4062_REG_CONV_READ_LSB 0x50
#define AD4062_REG_CONV_TRIGGER_32BITS 0x59
#define AD4062_REG_CONV_AUTO 0x61
#define AD4062_MAX_REG AD4062_REG_CONV_AUTO
#define AD4062_MON_VAL_MIDDLE_POINT 0x8000
#define AD4062_I3C_VENDOR 0x0177
#define AD4062_SOFT_RESET 0x81
#define AD4060_PROD_ID 0x7A
#define AD4062_PROD_ID 0x7C
#define AD4062_GP_DRDY 0x2
#define AD4062_INTR_EN_NEITHER 0x0
#define AD4062_TCONV_NS 270
enum ad4062_operation_mode {
AD4062_SAMPLE_MODE = 0x0,
AD4062_BURST_AVERAGING_MODE = 0x1,
AD4062_MONITOR_MODE = 0x3,
};
struct ad4062_chip_info {
const struct iio_chan_spec channels[1];
const char *name;
u16 prod_id;
u16 avg_max;
};
enum {
AD4062_SCAN_TYPE_SAMPLE,
AD4062_SCAN_TYPE_BURST_AVG,
};
static const unsigned int ad4062_conversion_freqs[] = {
2000000, 1000000, 300000, 100000, /* 0 - 3 */
33300, 10000, 3000, 500, /* 4 - 7 */
333, 250, 200, 166, /* 8 - 11 */
140, 124, 111, /* 12 - 15 */
};
struct ad4062_state {
const struct ad4062_chip_info *chip;
const struct ad4062_bus_ops *ops;
enum ad4062_operation_mode mode;
struct completion completion;
struct iio_trigger *trigger;
struct iio_dev *indio_dev;
struct i3c_device *i3cdev;
struct regmap *regmap;
int vref_uV;
unsigned int samp_freqs[ARRAY_SIZE(ad4062_conversion_freqs)];
u16 sampling_frequency;
u8 oversamp_ratio;
u8 conv_addr;
union {
__be32 be32;
__be16 be16;
} buf __aligned(IIO_DMA_MINALIGN);
};
static const struct regmap_range ad4062_regmap_rd_ranges[] = {
regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_GRADE),
regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_INTERFACE_STATUS),
regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_IBI_STATUS),
regmap_reg_range(AD4062_REG_CONV_READ_LSB, AD4062_REG_CONV_AUTO),
};
static const struct regmap_access_table ad4062_regmap_rd_table = {
.yes_ranges = ad4062_regmap_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(ad4062_regmap_rd_ranges),
};
static const struct regmap_range ad4062_regmap_wr_ranges[] = {
regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_CONFIG),
regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_SCRATCH_PAD),
regmap_reg_range(AD4062_REG_STREAM_MODE, AD4062_REG_INTERFACE_STATUS),
regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_DEVICE_STATUS),
};
static const struct regmap_access_table ad4062_regmap_wr_table = {
.yes_ranges = ad4062_regmap_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(ad4062_regmap_wr_ranges),
};
#define AD4062_CHAN { \
.type = IIO_VOLTAGE, \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.indexed = 1, \
.channel = 0, \
}
static const struct ad4062_chip_info ad4060_chip_info = {
.name = "ad4060",
.channels = { AD4062_CHAN },
.prod_id = AD4060_PROD_ID,
.avg_max = 256,
};
static const struct ad4062_chip_info ad4062_chip_info = {
.name = "ad4062",
.channels = { AD4062_CHAN },
.prod_id = AD4062_PROD_ID,
.avg_max = 4096,
};
static int ad4062_set_oversampling_ratio(struct ad4062_state *st, int val, int val2)
{
const u32 _max = st->chip->avg_max;
const u32 _min = 1;
int ret;
if (!in_range(val, _min, _max) || val2 != 0)
return -EINVAL;
/* 1 disables oversampling */
val = ilog2(val);
if (val == 0) {
st->mode = AD4062_SAMPLE_MODE;
} else {
st->mode = AD4062_BURST_AVERAGING_MODE;
ret = regmap_write(st->regmap, AD4062_REG_AVG_CONFIG, val - 1);
if (ret)
return ret;
}
st->oversamp_ratio = val;
return 0;
}
static int ad4062_get_oversampling_ratio(struct ad4062_state *st, int *val)
{
int ret, buf;
if (st->mode == AD4062_SAMPLE_MODE) {
*val = 1;
return 0;
}
ret = regmap_read(st->regmap, AD4062_REG_AVG_CONFIG, &buf);
if (ret)
return ret;
*val = BIT(buf + 1);
return 0;
}
static int ad4062_calc_sampling_frequency(unsigned int fosc, unsigned int oversamp_ratio)
{
/* From datasheet p.31: (n_avg - 1)/fosc + tconv */
u32 n_avg = BIT(oversamp_ratio) - 1;
u32 period_ns = NSEC_PER_SEC / fosc;
/* Result is less than 1 Hz */
if (n_avg >= fosc)
return 1;
return NSEC_PER_SEC / (n_avg * period_ns + AD4062_TCONV_NS);
}
static int ad4062_populate_sampling_frequency(struct ad4062_state *st)
{
for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++)
st->samp_freqs[i] =
ad4062_calc_sampling_frequency(ad4062_conversion_freqs[i],
st->oversamp_ratio);
return 0;
}
static int ad4062_get_sampling_frequency(struct ad4062_state *st, int *val)
{
int freq = ad4062_conversion_freqs[st->sampling_frequency];
*val = ad4062_calc_sampling_frequency(freq, st->oversamp_ratio);
return IIO_VAL_INT;
}
static int ad4062_set_sampling_frequency(struct ad4062_state *st, int val, int val2)
{
int ret;
if (val2 != 0)
return -EINVAL;
ret = ad4062_populate_sampling_frequency(st);
if (ret)
return ret;
st->sampling_frequency =
find_closest_descending(val, st->samp_freqs,
ARRAY_SIZE(ad4062_conversion_freqs));
return 0;
}
static int ad4062_check_ids(struct ad4062_state *st)
{
struct device *dev = &st->i3cdev->dev;
int ret;
u16 val;
ret = regmap_bulk_read(st->regmap, AD4062_REG_PROD_ID_1,
&st->buf.be16, sizeof(st->buf.be16));
if (ret)
return ret;
val = be16_to_cpu(st->buf.be16);
if (val != st->chip->prod_id)
dev_warn(dev, "Production ID x%x does not match known values", val);
ret = regmap_bulk_read(st->regmap, AD4062_REG_VENDOR_H,
&st->buf.be16, sizeof(st->buf.be16));
if (ret)
return ret;
val = be16_to_cpu(st->buf.be16);
if (val != AD4062_I3C_VENDOR) {
dev_err(dev, "Vendor ID x%x does not match expected value\n", val);
return -ENODEV;
}
return 0;
}
static int ad4062_conversion_frequency_set(struct ad4062_state *st, u8 val)
{
return regmap_write(st->regmap, AD4062_REG_TIMER_CONFIG,
FIELD_PREP(AD4062_REG_TIMER_CONFIG_FS_MASK, val));
}
static int ad4062_set_operation_mode(struct ad4062_state *st,
enum ad4062_operation_mode mode)
{
int ret;
ret = ad4062_conversion_frequency_set(st, st->sampling_frequency);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_MODES,
AD4062_REG_ADC_MODES_MODE_MSK, mode);
if (ret)
return ret;
return regmap_write(st->regmap, AD4062_REG_MODE_SET,
AD4062_REG_MODE_SET_ENTER_ADC);
}
static int ad4062_soft_reset(struct ad4062_state *st)
{
u8 val = AD4062_SOFT_RESET;
int ret;
ret = regmap_write(st->regmap, AD4062_REG_INTERFACE_CONFIG_A, val);
if (ret)
return ret;
/* Wait AD4062 treset time, datasheet p8 */
ndelay(60);
return 0;
}
static int ad4062_setup(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
const bool *ref_sel)
{
struct ad4062_state *st = iio_priv(indio_dev);
int ret;
ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
AD4062_REG_GP_CONF_MODE_MSK_1,
FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1,
AD4062_GP_DRDY));
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
AD4062_REG_ADC_CONFIG_REF_EN_MSK,
FIELD_PREP(AD4062_REG_ADC_CONFIG_REF_EN_MSK,
*ref_sel));
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4062_REG_DEVICE_STATUS,
AD4062_REG_DEVICE_STATUS_DEVICE_RESET);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF,
AD4062_REG_INTR_CONF_EN_MSK_1,
FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_1,
AD4062_INTR_EN_NEITHER));
if (ret)
return ret;
st->buf.be16 = cpu_to_be16(AD4062_MON_VAL_MIDDLE_POINT);
return regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
&st->buf.be16, sizeof(st->buf.be16));
}
static irqreturn_t ad4062_irq_handler_drdy(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct ad4062_state *st = iio_priv(indio_dev);
complete(&st->completion);
return IRQ_HANDLED;
}
static void ad4062_ibi_handler(struct i3c_device *i3cdev,
const struct i3c_ibi_payload *payload)
{
struct ad4062_state *st = i3cdev_get_drvdata(i3cdev);
complete(&st->completion);
}
static void ad4062_disable_ibi(void *data)
{
struct i3c_device *i3cdev = data;
i3c_device_disable_ibi(i3cdev);
}
static void ad4062_free_ibi(void *data)
{
struct i3c_device *i3cdev = data;
i3c_device_free_ibi(i3cdev);
}
static int ad4062_request_ibi(struct i3c_device *i3cdev)
{
const struct i3c_ibi_setup ibireq = {
.max_payload_len = 1,
.num_slots = 1,
.handler = ad4062_ibi_handler,
};
int ret;
ret = i3c_device_request_ibi(i3cdev, &ibireq);
if (ret)
return ret;
ret = devm_add_action_or_reset(&i3cdev->dev, ad4062_free_ibi, i3cdev);
if (ret)
return ret;
ret = i3c_device_enable_ibi(i3cdev);
if (ret)
return ret;
return devm_add_action_or_reset(&i3cdev->dev, ad4062_disable_ibi, i3cdev);
}
static int ad4062_request_irq(struct iio_dev *indio_dev)
{
struct ad4062_state *st = iio_priv(indio_dev);
struct device *dev = &st->i3cdev->dev;
int ret;
ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp1");
if (ret == -EPROBE_DEFER)
return ret;
if (ret < 0)
return regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
AD4062_REG_ADC_IBI_EN_CONV_TRIGGER,
AD4062_REG_ADC_IBI_EN_CONV_TRIGGER);
return devm_request_threaded_irq(dev, ret,
ad4062_irq_handler_drdy,
NULL, IRQF_ONESHOT, indio_dev->name,
indio_dev);
}
static const int ad4062_oversampling_avail[] = {
1, 2, 4, 8, 16, 32, 64, 128, /* 0 - 7 */
256, 512, 1024, 2048, 4096, /* 8 - 12 */
};
static int ad4062_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, const int **vals,
int *type, int *len, long mask)
{
struct ad4062_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ad4062_oversampling_avail;
*len = ARRAY_SIZE(ad4062_oversampling_avail);
*len -= st->chip->avg_max == 256 ? 4 : 0;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ad4062_populate_sampling_frequency(st);
if (ret)
return ret;
*vals = st->samp_freqs;
*len = st->oversamp_ratio ? ARRAY_SIZE(ad4062_conversion_freqs) : 1;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int ad4062_get_chan_calibscale(struct ad4062_state *st, int *val, int *val2)
{
int ret;
ret = regmap_bulk_read(st->regmap, AD4062_REG_MON_VAL,
&st->buf.be16, sizeof(st->buf.be16));
if (ret)
return ret;
/* From datasheet: code out = code in × mon_val/0x8000 */
*val = be16_to_cpu(st->buf.be16) * 2;
*val2 = 16;
return IIO_VAL_FRACTIONAL_LOG2;
}
static int ad4062_set_chan_calibscale(struct ad4062_state *st, int gain_int,
int gain_frac)
{
/* Divide numerator and denumerator by known great common divider */
const u32 mon_val = AD4062_MON_VAL_MIDDLE_POINT / 64;
const u32 micro = MICRO / 64;
const u32 gain_fp = gain_int * MICRO + gain_frac;
const u32 reg_val = DIV_ROUND_CLOSEST(gain_fp * mon_val, micro);
int ret;
/* Checks if the gain is in range and the value fits the field */
if (gain_int < 0 || gain_int > 1 || reg_val > BIT(16) - 1)
return -EINVAL;
st->buf.be16 = cpu_to_be16(reg_val);
ret = regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
&st->buf.be16, sizeof(st->buf.be16));
if (ret)
return ret;
/* Enable scale if gain is not equal to one */
return regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
FIELD_PREP(AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
!(gain_int == 1 && gain_frac == 0)));
}
static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
{
struct i3c_device *i3cdev = st->i3cdev;
struct i3c_priv_xfer xfer_trigger = {
.data.out = &st->conv_addr,
.len = sizeof(st->conv_addr),
.rnw = false,
};
struct i3c_priv_xfer xfer_sample = {
.data.in = &st->buf.be32,
.len = sizeof(st->buf.be32),
.rnw = true,
};
int ret;
PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
ret = ad4062_set_operation_mode(st, st->mode);
if (ret)
return ret;
reinit_completion(&st->completion);
/* Change address pointer to trigger conversion */
st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS;
ret = i3c_device_do_priv_xfers(i3cdev, &xfer_trigger, 1);
if (ret)
return ret;
/*
* Single sample read should be used only for oversampling and
* sampling frequency pairs that take less than 1 sec.
*/
ret = wait_for_completion_timeout(&st->completion,
msecs_to_jiffies(1000));
if (!ret)
return -ETIMEDOUT;
ret = i3c_device_do_priv_xfers(i3cdev, &xfer_sample, 1);
if (ret)
return ret;
*val = be32_to_cpu(st->buf.be32);
return 0;
}
static int ad4062_read_raw_dispatch(struct ad4062_state *st,
int *val, int *val2, long info)
{
switch (info) {
case IIO_CHAN_INFO_RAW:
return ad4062_read_chan_raw(st, val);
case IIO_CHAN_INFO_CALIBSCALE:
return ad4062_get_chan_calibscale(st, val, val2);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4062_get_oversampling_ratio(st, val);
default:
return -EINVAL;
}
}
static int ad4062_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct ad4062_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ad4062_get_sampling_frequency(st, val);
}
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad4062_read_raw_dispatch(st, val, val2, info);
iio_device_release_direct(indio_dev);
return ret ?: IIO_VAL_INT;
}
static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2,
long info)
{
switch (info) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4062_set_oversampling_ratio(st, val, val2);
case IIO_CHAN_INFO_CALIBSCALE:
return ad4062_set_chan_calibscale(st, val, val2);
default:
return -EINVAL;
}
};
static int ad4062_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long info)
{
struct ad4062_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ad4062_set_sampling_frequency(st, val, val2);
}
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad4062_write_raw_dispatch(st, val, val2, info);
iio_device_release_direct(indio_dev);
return ret;
}
static int ad4062_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct ad4062_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
else
return regmap_write(st->regmap, reg, writeval);
}
static const struct iio_info ad4062_info = {
.read_raw = ad4062_read_raw,
.write_raw = ad4062_write_raw,
.read_avail = ad4062_read_avail,
.debugfs_reg_access = ad4062_debugfs_reg_access,
};
static const struct regmap_config ad4062_regmap_config = {
.name = "ad4062",
.reg_bits = 8,
.val_bits = 8,
.max_register = AD4062_MAX_REG,
.rd_table = &ad4062_regmap_rd_table,
.wr_table = &ad4062_regmap_wr_table,
.can_sleep = true,
};
static int ad4062_regulators_get(struct ad4062_state *st, bool *ref_sel)
{
struct device *dev = &st->i3cdev->dev;
int ret;
ret = devm_regulator_get_enable(dev, "vio");
if (ret)
return dev_err_probe(dev, ret, "Failed to enable vio voltage\n");
st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref");
*ref_sel = st->vref_uV == -ENODEV;
if (st->vref_uV < 0 && !*ref_sel)
return dev_err_probe(dev, st->vref_uV,
"Failed to enable and read ref voltage\n");
if (*ref_sel) {
st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vdd");
if (st->vref_uV < 0)
return dev_err_probe(dev, st->vref_uV,
"Failed to enable and read vdd voltage\n");
} else {
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable vdd regulator\n");
}
return 0;
}
static const struct i3c_device_id ad4062_id_table[] = {
I3C_DEVICE(AD4062_I3C_VENDOR, AD4060_PROD_ID, &ad4060_chip_info),
I3C_DEVICE(AD4062_I3C_VENDOR, AD4062_PROD_ID, &ad4062_chip_info),
{ }
};
MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
static int ad4062_probe(struct i3c_device *i3cdev)
{
const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
const struct ad4062_chip_info *chip = id->data;
struct device *dev = &i3cdev->dev;
struct iio_dev *indio_dev;
struct ad4062_state *st;
bool ref_sel;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->i3cdev = i3cdev;
i3cdev_set_drvdata(i3cdev, st);
init_completion(&st->completion);
ret = ad4062_regulators_get(st, &ref_sel);
if (ret)
return ret;
st->regmap = devm_regmap_init_i3c(i3cdev, &ad4062_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to initialize regmap\n");
st->mode = AD4062_SAMPLE_MODE;
st->chip = chip;
st->sampling_frequency = 0;
st->oversamp_ratio = 0;
st->indio_dev = indio_dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = 1;
indio_dev->info = &ad4062_info;
indio_dev->name = chip->name;
indio_dev->channels = chip->channels;
ret = ad4062_soft_reset(st);
if (ret)
return dev_err_probe(dev, ret, "AD4062 failed to soft reset\n");
ret = ad4062_check_ids(st);
if (ret)
return ret;
ret = ad4062_setup(indio_dev, indio_dev->channels, &ref_sel);
if (ret)
return ret;
ret = ad4062_request_irq(indio_dev);
if (ret)
return ret;
pm_runtime_set_active(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n");
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
ret = ad4062_request_ibi(i3cdev);
if (ret)
return dev_err_probe(dev, ret, "Failed to request i3c ibi\n");
return devm_iio_device_register(dev, indio_dev);
}
static int ad4062_runtime_suspend(struct device *dev)
{
struct ad4062_state *st = dev_get_drvdata(dev);
return regmap_write(st->regmap, AD4062_REG_DEVICE_CONFIG,
FIELD_PREP(AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK,
AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE));
}
static int ad4062_runtime_resume(struct device *dev)
{
struct ad4062_state *st = dev_get_drvdata(dev);
int ret;
ret = regmap_clear_bits(st->regmap, AD4062_REG_DEVICE_CONFIG,
AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK);
if (ret)
return ret;
/* Wait device functional blocks to power up */
fsleep(3 * USEC_PER_MSEC);
return 0;
}
static DEFINE_RUNTIME_DEV_PM_OPS(ad4062_pm_ops,
ad4062_runtime_suspend, ad4062_runtime_resume, NULL);
static struct i3c_driver ad4062_driver = {
.driver = {
.name = "ad4062",
.pm = pm_ptr(&ad4062_pm_ops),
},
.probe = ad4062_probe,
.id_table = ad4062_id_table,
};
module_i3c_driver(ad4062_driver);
MODULE_AUTHOR("Jorge Marques <jorge.marques@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD4062");
MODULE_LICENSE("GPL");