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
F: Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
F: Documentation/iio/ad4062.rst
F: drivers/iio/adc/ad4062.c
ANALOG DEVICES INC AD4080 DRIVER
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
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
tristate "Analog Devices AD4080 high speed ADC"
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_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o
obj-$(CONFIG_AD4062) += ad4062.o
obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.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");