mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 09:13:38 +00:00
7763e40f35
The AD4695 has a calibration feature that allows the user to compensate for variations in the analog front end. This implements this feature in the driver using the standard `calibgain` and `calibbias` attributes. Signed-off-by: David Lechner <dlechner@baylibre.com> Link: https://patch.msgid.link/20240820-ad4695-gain-offset-v1-2-c8f6e3b47551@baylibre.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1186 lines
34 KiB
C
1186 lines
34 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* SPI ADC driver for Analog Devices Inc. AD4695 and similar chips
|
|
*
|
|
* https://www.analog.com/en/products/ad4695.html
|
|
* https://www.analog.com/en/products/ad4696.html
|
|
* https://www.analog.com/en/products/ad4697.html
|
|
* https://www.analog.com/en/products/ad4698.html
|
|
*
|
|
* Copyright 2024 Analog Devices Inc.
|
|
* Copyright 2024 BayLibre, SAS
|
|
*/
|
|
|
|
#include <linux/align.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
#include <linux/minmax.h>
|
|
#include <linux/property.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/units.h>
|
|
|
|
#include <dt-bindings/iio/adi,ad4695.h>
|
|
|
|
/* AD4695 registers */
|
|
#define AD4695_REG_SPI_CONFIG_A 0x0000
|
|
#define AD4695_REG_SPI_CONFIG_A_SW_RST (BIT(7) | BIT(0))
|
|
#define AD4695_REG_SPI_CONFIG_A_ADDR_DIR BIT(5)
|
|
#define AD4695_REG_SPI_CONFIG_B 0x0001
|
|
#define AD4695_REG_SPI_CONFIG_B_INST_MODE BIT(7)
|
|
#define AD4695_REG_DEVICE_TYPE 0x0003
|
|
#define AD4695_REG_SCRATCH_PAD 0x000A
|
|
#define AD4695_REG_VENDOR_L 0x000C
|
|
#define AD4695_REG_VENDOR_H 0x000D
|
|
#define AD4695_REG_LOOP_MODE 0x000E
|
|
#define AD4695_REG_SPI_CONFIG_C 0x0010
|
|
#define AD4695_REG_SPI_CONFIG_C_MB_STRICT BIT(7)
|
|
#define AD4695_REG_SPI_STATUS 0x0011
|
|
#define AD4695_REG_STATUS 0x0014
|
|
#define AD4695_REG_ALERT_STATUS1 0x0015
|
|
#define AD4695_REG_ALERT_STATUS2 0x0016
|
|
#define AD4695_REG_CLAMP_STATUS 0x001A
|
|
#define AD4695_REG_SETUP 0x0020
|
|
#define AD4695_REG_SETUP_LDO_EN BIT(4)
|
|
#define AD4695_REG_SETUP_SPI_MODE BIT(2)
|
|
#define AD4695_REG_SETUP_SPI_CYC_CTRL BIT(1)
|
|
#define AD4695_REG_REF_CTRL 0x0021
|
|
#define AD4695_REG_REF_CTRL_OV_MODE BIT(7)
|
|
#define AD4695_REG_REF_CTRL_VREF_SET GENMASK(4, 2)
|
|
#define AD4695_REG_REF_CTRL_REFHIZ_EN BIT(1)
|
|
#define AD4695_REG_REF_CTRL_REFBUF_EN BIT(0)
|
|
#define AD4695_REG_SEQ_CTRL 0x0022
|
|
#define AD4695_REG_SEQ_CTRL_STD_SEQ_EN BIT(7)
|
|
#define AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS GENMASK(6, 0)
|
|
#define AD4695_REG_AC_CTRL 0x0023
|
|
#define AD4695_REG_STD_SEQ_CONFIG 0x0024
|
|
#define AD4695_REG_GPIO_CTRL 0x0026
|
|
#define AD4695_REG_GP_MODE 0x0027
|
|
#define AD4695_REG_TEMP_CTRL 0x0029
|
|
#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0)
|
|
#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n))
|
|
#define AD4695_REG_CONFIG_IN_MODE BIT(6)
|
|
#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4)
|
|
#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3)
|
|
#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n)))
|
|
#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n)))
|
|
#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n)))
|
|
#define AD4695_REG_OFFSET_IN(n) (0x00A0 | (2 * (n)))
|
|
#define AD4695_REG_GAIN_IN(n) (0x00C0 | (2 * (n)))
|
|
#define AD4695_REG_AS_SLOT(n) (0x0100 | (n))
|
|
#define AD4695_REG_AS_SLOT_INX GENMASK(3, 0)
|
|
|
|
/* Conversion mode commands */
|
|
#define AD4695_CMD_EXIT_CNV_MODE 0x0A
|
|
#define AD4695_CMD_TEMP_CHAN 0x0F
|
|
#define AD4695_CMD_VOLTAGE_CHAN(n) (0x10 | (n))
|
|
|
|
/* timing specs */
|
|
#define AD4695_T_CONVERT_NS 415
|
|
#define AD4695_T_WAKEUP_HW_MS 3
|
|
#define AD4695_T_WAKEUP_SW_MS 3
|
|
#define AD4695_T_REFBUF_MS 100
|
|
#define AD4695_T_REGCONFIG_NS 20
|
|
#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA)
|
|
|
|
/* Max number of voltage input channels. */
|
|
#define AD4695_MAX_CHANNELS 16
|
|
/* Max size of 1 raw sample in bytes. */
|
|
#define AD4695_MAX_CHANNEL_SIZE 2
|
|
|
|
enum ad4695_in_pair {
|
|
AD4695_IN_PAIR_REFGND,
|
|
AD4695_IN_PAIR_COM,
|
|
AD4695_IN_PAIR_EVEN_ODD,
|
|
};
|
|
|
|
struct ad4695_chip_info {
|
|
const char *name;
|
|
int max_sample_rate;
|
|
u32 t_acq_ns;
|
|
u8 num_voltage_inputs;
|
|
};
|
|
|
|
struct ad4695_channel_config {
|
|
unsigned int channel;
|
|
bool highz_en;
|
|
bool bipolar;
|
|
enum ad4695_in_pair pin_pairing;
|
|
unsigned int common_mode_mv;
|
|
};
|
|
|
|
struct ad4695_state {
|
|
struct spi_device *spi;
|
|
struct regmap *regmap;
|
|
struct regmap *regmap16;
|
|
struct gpio_desc *reset_gpio;
|
|
/* voltages channels plus temperature and timestamp */
|
|
struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2];
|
|
struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS];
|
|
const struct ad4695_chip_info *chip_info;
|
|
/* Reference voltage. */
|
|
unsigned int vref_mv;
|
|
/* Common mode input pin voltage. */
|
|
unsigned int com_mv;
|
|
/* 1 per voltage and temperature chan plus 1 xfer to trigger 1st CNV */
|
|
struct spi_transfer buf_read_xfer[AD4695_MAX_CHANNELS + 2];
|
|
struct spi_message buf_read_msg;
|
|
/* Raw conversion data received. */
|
|
u8 buf[ALIGN((AD4695_MAX_CHANNELS + 2) * AD4695_MAX_CHANNEL_SIZE,
|
|
sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
|
|
u16 raw_data;
|
|
/* Commands to send for single conversion. */
|
|
u16 cnv_cmd;
|
|
u8 cnv_cmd2;
|
|
};
|
|
|
|
static const struct regmap_range ad4695_regmap_rd_ranges[] = {
|
|
regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
|
|
regmap_reg_range(AD4695_REG_DEVICE_TYPE, AD4695_REG_DEVICE_TYPE),
|
|
regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
|
|
regmap_reg_range(AD4695_REG_VENDOR_L, AD4695_REG_LOOP_MODE),
|
|
regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
|
|
regmap_reg_range(AD4695_REG_STATUS, AD4695_REG_ALERT_STATUS2),
|
|
regmap_reg_range(AD4695_REG_CLAMP_STATUS, AD4695_REG_CLAMP_STATUS),
|
|
regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL),
|
|
regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL),
|
|
regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)),
|
|
regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)),
|
|
};
|
|
|
|
static const struct regmap_access_table ad4695_regmap_rd_table = {
|
|
.yes_ranges = ad4695_regmap_rd_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap_rd_ranges),
|
|
};
|
|
|
|
static const struct regmap_range ad4695_regmap_wr_ranges[] = {
|
|
regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
|
|
regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
|
|
regmap_reg_range(AD4695_REG_LOOP_MODE, AD4695_REG_LOOP_MODE),
|
|
regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
|
|
regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL),
|
|
regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL),
|
|
regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)),
|
|
regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)),
|
|
};
|
|
|
|
static const struct regmap_access_table ad4695_regmap_wr_table = {
|
|
.yes_ranges = ad4695_regmap_wr_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap_wr_ranges),
|
|
};
|
|
|
|
static const struct regmap_config ad4695_regmap_config = {
|
|
.name = "ad4695-8",
|
|
.reg_bits = 16,
|
|
.val_bits = 8,
|
|
.max_register = AD4695_REG_AS_SLOT(127),
|
|
.rd_table = &ad4695_regmap_rd_table,
|
|
.wr_table = &ad4695_regmap_wr_table,
|
|
.can_multi_write = true,
|
|
};
|
|
|
|
static const struct regmap_range ad4695_regmap16_rd_ranges[] = {
|
|
regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG),
|
|
regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)),
|
|
};
|
|
|
|
static const struct regmap_access_table ad4695_regmap16_rd_table = {
|
|
.yes_ranges = ad4695_regmap16_rd_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_rd_ranges),
|
|
};
|
|
|
|
static const struct regmap_range ad4695_regmap16_wr_ranges[] = {
|
|
regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG),
|
|
regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)),
|
|
};
|
|
|
|
static const struct regmap_access_table ad4695_regmap16_wr_table = {
|
|
.yes_ranges = ad4695_regmap16_wr_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_wr_ranges),
|
|
};
|
|
|
|
static const struct regmap_config ad4695_regmap16_config = {
|
|
.name = "ad4695-16",
|
|
.reg_bits = 16,
|
|
.reg_stride = 2,
|
|
.val_bits = 16,
|
|
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
|
.max_register = AD4695_REG_GAIN_IN(15),
|
|
.rd_table = &ad4695_regmap16_rd_table,
|
|
.wr_table = &ad4695_regmap16_wr_table,
|
|
.can_multi_write = true,
|
|
};
|
|
|
|
static const struct iio_chan_spec ad4695_channel_template = {
|
|
.type = IIO_VOLTAGE,
|
|
.indexed = 1,
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
|
BIT(IIO_CHAN_INFO_SCALE) |
|
|
BIT(IIO_CHAN_INFO_OFFSET) |
|
|
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
|
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
|
.info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
|
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
|
.scan_type = {
|
|
.sign = 'u',
|
|
.realbits = 16,
|
|
.storagebits = 16,
|
|
},
|
|
};
|
|
|
|
static const struct iio_chan_spec ad4695_temp_channel_template = {
|
|
.address = AD4695_CMD_TEMP_CHAN,
|
|
.type = IIO_TEMP,
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
|
BIT(IIO_CHAN_INFO_SCALE) |
|
|
BIT(IIO_CHAN_INFO_OFFSET),
|
|
.scan_type = {
|
|
.sign = 's',
|
|
.realbits = 16,
|
|
.storagebits = 16,
|
|
},
|
|
};
|
|
|
|
static const struct iio_chan_spec ad4695_soft_timestamp_channel_template =
|
|
IIO_CHAN_SOFT_TIMESTAMP(0);
|
|
|
|
static const char * const ad4695_power_supplies[] = {
|
|
"avdd", "vio"
|
|
};
|
|
|
|
static const struct ad4695_chip_info ad4695_chip_info = {
|
|
.name = "ad4695",
|
|
.max_sample_rate = 500 * KILO,
|
|
.t_acq_ns = 1715,
|
|
.num_voltage_inputs = 16,
|
|
};
|
|
|
|
static const struct ad4695_chip_info ad4696_chip_info = {
|
|
.name = "ad4696",
|
|
.max_sample_rate = 1 * MEGA,
|
|
.t_acq_ns = 715,
|
|
.num_voltage_inputs = 16,
|
|
};
|
|
|
|
static const struct ad4695_chip_info ad4697_chip_info = {
|
|
.name = "ad4697",
|
|
.max_sample_rate = 500 * KILO,
|
|
.t_acq_ns = 1715,
|
|
.num_voltage_inputs = 8,
|
|
};
|
|
|
|
static const struct ad4695_chip_info ad4698_chip_info = {
|
|
.name = "ad4698",
|
|
.max_sample_rate = 1 * MEGA,
|
|
.t_acq_ns = 715,
|
|
.num_voltage_inputs = 8,
|
|
};
|
|
|
|
/**
|
|
* ad4695_set_single_cycle_mode - Set the device in single cycle mode
|
|
* @st: The AD4695 state
|
|
* @channel: The first channel to read
|
|
*
|
|
* As per the datasheet, to enable single cycle mode, we need to set
|
|
* STD_SEQ_EN=0, NUM_SLOTS_AS=0 and CYC_CTRL=1 (Table 15). Setting SPI_MODE=1
|
|
* triggers the first conversion using the channel in AS_SLOT0.
|
|
*
|
|
* Context: can sleep, must be called with iio_device_claim_direct held
|
|
* Return: 0 on success, a negative error code on failure
|
|
*/
|
|
static int ad4695_set_single_cycle_mode(struct ad4695_state *st,
|
|
unsigned int channel)
|
|
{
|
|
int ret;
|
|
|
|
ret = regmap_clear_bits(st->regmap, AD4695_REG_SEQ_CTRL,
|
|
AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
|
|
AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(0),
|
|
FIELD_PREP(AD4695_REG_AS_SLOT_INX, channel));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return regmap_set_bits(st->regmap, AD4695_REG_SETUP,
|
|
AD4695_REG_SETUP_SPI_MODE |
|
|
AD4695_REG_SETUP_SPI_CYC_CTRL);
|
|
}
|
|
|
|
/**
|
|
* ad4695_enter_advanced_sequencer_mode - Put the ADC in advanced sequencer mode
|
|
* @st: The driver state
|
|
* @n: The number of slots to use - must be >= 2, <= 128
|
|
*
|
|
* As per the datasheet, to enable advanced sequencer, we need to set
|
|
* STD_SEQ_EN=0, NUM_SLOTS_AS=n-1 and CYC_CTRL=0 (Table 15). Setting SPI_MODE=1
|
|
* triggers the first conversion using the channel in AS_SLOT0.
|
|
*
|
|
* Return: 0 on success, a negative error code on failure
|
|
*/
|
|
static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n)
|
|
{
|
|
int ret;
|
|
|
|
ret = regmap_update_bits(st->regmap, AD4695_REG_SEQ_CTRL,
|
|
AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
|
|
AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS,
|
|
FIELD_PREP(AD4695_REG_SEQ_CTRL_STD_SEQ_EN, 0) |
|
|
FIELD_PREP(AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, n - 1));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return regmap_update_bits(st->regmap, AD4695_REG_SETUP,
|
|
AD4695_REG_SETUP_SPI_MODE | AD4695_REG_SETUP_SPI_CYC_CTRL,
|
|
FIELD_PREP(AD4695_REG_SETUP_SPI_MODE, 1) |
|
|
FIELD_PREP(AD4695_REG_SETUP_SPI_CYC_CTRL, 0));
|
|
}
|
|
|
|
/**
|
|
* ad4695_exit_conversion_mode - Exit conversion mode
|
|
* @st: The AD4695 state
|
|
*
|
|
* Sends SPI command to exit conversion mode.
|
|
*
|
|
* Return: 0 on success, a negative error code on failure
|
|
*/
|
|
static int ad4695_exit_conversion_mode(struct ad4695_state *st)
|
|
{
|
|
struct spi_transfer xfer = {
|
|
.tx_buf = &st->cnv_cmd2,
|
|
.len = 1,
|
|
.delay.value = AD4695_T_REGCONFIG_NS,
|
|
.delay.unit = SPI_DELAY_UNIT_NSECS,
|
|
};
|
|
|
|
/*
|
|
* Technically, could do a 5-bit transfer, but shifting to start of
|
|
* 8 bits instead for better SPI controller support.
|
|
*/
|
|
st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3;
|
|
|
|
return spi_sync_transfer(st->spi, &xfer, 1);
|
|
}
|
|
|
|
static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv)
|
|
{
|
|
u8 val;
|
|
|
|
if (vref_mv >= 2400 && vref_mv <= 2750)
|
|
val = 0;
|
|
else if (vref_mv > 2750 && vref_mv <= 3250)
|
|
val = 1;
|
|
else if (vref_mv > 3250 && vref_mv <= 3750)
|
|
val = 2;
|
|
else if (vref_mv > 3750 && vref_mv <= 4500)
|
|
val = 3;
|
|
else if (vref_mv > 4500 && vref_mv <= 5100)
|
|
val = 4;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return regmap_update_bits(st->regmap, AD4695_REG_REF_CTRL,
|
|
AD4695_REG_REF_CTRL_VREF_SET,
|
|
FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val));
|
|
}
|
|
|
|
static int ad4695_write_chn_cfg(struct ad4695_state *st,
|
|
struct ad4695_channel_config *cfg)
|
|
{
|
|
u32 mask, val;
|
|
|
|
mask = AD4695_REG_CONFIG_IN_MODE;
|
|
val = FIELD_PREP(AD4695_REG_CONFIG_IN_MODE, cfg->bipolar ? 1 : 0);
|
|
|
|
mask |= AD4695_REG_CONFIG_IN_PAIR;
|
|
val |= FIELD_PREP(AD4695_REG_CONFIG_IN_PAIR, cfg->pin_pairing);
|
|
|
|
mask |= AD4695_REG_CONFIG_IN_AINHIGHZ_EN;
|
|
val |= FIELD_PREP(AD4695_REG_CONFIG_IN_AINHIGHZ_EN,
|
|
cfg->highz_en ? 1 : 0);
|
|
|
|
return regmap_update_bits(st->regmap,
|
|
AD4695_REG_CONFIG_IN(cfg->channel),
|
|
mask, val);
|
|
}
|
|
|
|
static int ad4695_buffer_preenable(struct iio_dev *indio_dev)
|
|
{
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
struct spi_transfer *xfer;
|
|
u8 temp_chan_bit = st->chip_info->num_voltage_inputs;
|
|
u32 bit, num_xfer, num_slots;
|
|
u32 temp_en = 0;
|
|
int ret;
|
|
|
|
/*
|
|
* We are using the advanced sequencer since it is the only way to read
|
|
* multiple channels that allows individual configuration of each
|
|
* voltage input channel. Slot 0 in the advanced sequencer is used to
|
|
* account for the gap between trigger polls - we don't read data from
|
|
* this slot. Each enabled voltage channel is assigned a slot starting
|
|
* with slot 1.
|
|
*/
|
|
num_slots = 1;
|
|
|
|
memset(st->buf_read_xfer, 0, sizeof(st->buf_read_xfer));
|
|
|
|
/* First xfer is only to trigger conversion of slot 1, so no rx. */
|
|
xfer = &st->buf_read_xfer[0];
|
|
xfer->cs_change = 1;
|
|
xfer->delay.value = st->chip_info->t_acq_ns;
|
|
xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
|
|
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
num_xfer = 1;
|
|
|
|
iio_for_each_active_channel(indio_dev, bit) {
|
|
xfer = &st->buf_read_xfer[num_xfer];
|
|
xfer->bits_per_word = 16;
|
|
xfer->rx_buf = &st->buf[(num_xfer - 1) * 2];
|
|
xfer->len = 2;
|
|
xfer->cs_change = 1;
|
|
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
|
|
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
|
|
if (bit == temp_chan_bit) {
|
|
temp_en = 1;
|
|
} else {
|
|
ret = regmap_write(st->regmap,
|
|
AD4695_REG_AS_SLOT(num_slots),
|
|
FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit));
|
|
if (ret)
|
|
return ret;
|
|
|
|
num_slots++;
|
|
}
|
|
|
|
num_xfer++;
|
|
}
|
|
|
|
/*
|
|
* The advanced sequencer requires that at least 2 slots are enabled.
|
|
* Since slot 0 is always used for other purposes, we need only 1
|
|
* enabled voltage channel to meet this requirement. If the temperature
|
|
* channel is the only enabled channel, we need to add one more slot
|
|
* in the sequence but not read from it.
|
|
*/
|
|
if (num_slots < 2) {
|
|
/* move last xfer so we can insert one more xfer before it */
|
|
st->buf_read_xfer[num_xfer] = *xfer;
|
|
num_xfer++;
|
|
|
|
/* modify 2nd to last xfer for extra slot */
|
|
memset(xfer, 0, sizeof(*xfer));
|
|
xfer->cs_change = 1;
|
|
xfer->delay.value = st->chip_info->t_acq_ns;
|
|
xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
|
|
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
xfer++;
|
|
|
|
/* and add the extra slot in the sequencer */
|
|
ret = regmap_write(st->regmap,
|
|
AD4695_REG_AS_SLOT(num_slots),
|
|
FIELD_PREP(AD4695_REG_AS_SLOT_INX, 0));
|
|
if (ret)
|
|
return ret;
|
|
|
|
num_slots++;
|
|
}
|
|
|
|
/*
|
|
* Don't keep CS asserted after last xfer. Also triggers conversion of
|
|
* slot 0.
|
|
*/
|
|
xfer->cs_change = 0;
|
|
|
|
/*
|
|
* Temperature channel isn't included in the sequence, but rather
|
|
* controlled by setting a bit in the TEMP_CTRL register.
|
|
*/
|
|
|
|
ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL,
|
|
AD4695_REG_TEMP_CTRL_TEMP_EN,
|
|
FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, temp_en));
|
|
if (ret)
|
|
return ret;
|
|
|
|
spi_message_init_with_transfers(&st->buf_read_msg, st->buf_read_xfer,
|
|
num_xfer);
|
|
|
|
ret = spi_optimize_message(st->spi, &st->buf_read_msg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* This triggers conversion of slot 0. */
|
|
ret = ad4695_enter_advanced_sequencer_mode(st, num_slots);
|
|
if (ret)
|
|
spi_unoptimize_message(&st->buf_read_msg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ad4695_buffer_postdisable(struct iio_dev *indio_dev)
|
|
{
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
ret = ad4695_exit_conversion_mode(st);
|
|
if (ret)
|
|
return ret;
|
|
|
|
spi_unoptimize_message(&st->buf_read_msg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct iio_buffer_setup_ops ad4695_buffer_setup_ops = {
|
|
.preenable = ad4695_buffer_preenable,
|
|
.postdisable = ad4695_buffer_postdisable,
|
|
};
|
|
|
|
static irqreturn_t ad4695_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
ret = spi_sync(st->spi, &st->buf_read_msg);
|
|
if (ret)
|
|
goto out;
|
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, st->buf, pf->timestamp);
|
|
|
|
out:
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/**
|
|
* ad4695_read_one_sample - Read a single sample using single-cycle mode
|
|
* @st: The AD4695 state
|
|
* @address: The address of the channel to read
|
|
*
|
|
* Upon successful return, the sample will be stored in `st->raw_data`.
|
|
*
|
|
* Context: can sleep, must be called with iio_device_claim_direct held
|
|
* Return: 0 on success, a negative error code on failure
|
|
*/
|
|
static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
|
|
{
|
|
struct spi_transfer xfer[2] = { };
|
|
int ret, i = 0;
|
|
|
|
ret = ad4695_set_single_cycle_mode(st, address);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Setting the first channel to the temperature channel isn't supported
|
|
* in single-cycle mode, so we have to do an extra xfer to read the
|
|
* temperature.
|
|
*/
|
|
if (address == AD4695_CMD_TEMP_CHAN) {
|
|
/* We aren't reading, so we can make this a short xfer. */
|
|
st->cnv_cmd2 = AD4695_CMD_TEMP_CHAN << 3;
|
|
xfer[0].tx_buf = &st->cnv_cmd2;
|
|
xfer[0].len = 1;
|
|
xfer[0].cs_change = 1;
|
|
xfer[0].cs_change_delay.value = AD4695_T_CONVERT_NS;
|
|
xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
|
|
|
|
i = 1;
|
|
}
|
|
|
|
/* Then read the result and exit conversion mode. */
|
|
st->cnv_cmd = AD4695_CMD_EXIT_CNV_MODE << 11;
|
|
xfer[i].bits_per_word = 16;
|
|
xfer[i].tx_buf = &st->cnv_cmd;
|
|
xfer[i].rx_buf = &st->raw_data;
|
|
xfer[i].len = 2;
|
|
|
|
return spi_sync_transfer(st->spi, xfer, i + 1);
|
|
}
|
|
|
|
static int ad4695_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index];
|
|
u8 realbits = chan->scan_type.realbits;
|
|
unsigned int reg_val;
|
|
int ret, tmp;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
ret = ad4695_read_one_sample(st, chan->address);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (chan->scan_type.sign == 's')
|
|
*val = sign_extend32(st->raw_data, realbits - 1);
|
|
else
|
|
*val = st->raw_data;
|
|
|
|
return IIO_VAL_INT;
|
|
}
|
|
unreachable();
|
|
case IIO_CHAN_INFO_SCALE:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
*val = st->vref_mv;
|
|
*val2 = chan->scan_type.realbits;
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
case IIO_TEMP:
|
|
/* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */
|
|
*val = st->vref_mv * -556;
|
|
*val2 = 16;
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_OFFSET:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
if (cfg->pin_pairing == AD4695_IN_PAIR_COM)
|
|
*val = st->com_mv * (1 << realbits) / st->vref_mv;
|
|
else if (cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD)
|
|
*val = cfg->common_mode_mv * (1 << realbits) / st->vref_mv;
|
|
else
|
|
*val = 0;
|
|
|
|
return IIO_VAL_INT;
|
|
case IIO_TEMP:
|
|
/* T_offset (°C) = -725 mV / (-1.8 mV/°C) */
|
|
/* T_offset (raw) = T_offset (°C) * (-1.8 mV/°C) * 2^16 / V_REF (mV) */
|
|
*val = -47513600;
|
|
*val2 = st->vref_mv;
|
|
return IIO_VAL_FRACTIONAL;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_CALIBSCALE:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
ret = regmap_read(st->regmap16,
|
|
AD4695_REG_GAIN_IN(chan->scan_index),
|
|
®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*val = reg_val;
|
|
*val2 = 15;
|
|
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
}
|
|
unreachable();
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
ret = regmap_read(st->regmap16,
|
|
AD4695_REG_OFFSET_IN(chan->scan_index),
|
|
®_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
tmp = sign_extend32(reg_val, 15);
|
|
|
|
*val = tmp / 4;
|
|
*val2 = abs(tmp) % 4 * MICRO / 4;
|
|
|
|
if (tmp < 0 && *val2) {
|
|
*val *= -1;
|
|
*val2 *= -1;
|
|
}
|
|
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
unreachable();
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int ad4695_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int val, int val2, long mask)
|
|
{
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
unsigned int reg_val;
|
|
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_CALIBSCALE:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
if (val < 0 || val2 < 0)
|
|
reg_val = 0;
|
|
else if (val > 1)
|
|
reg_val = U16_MAX;
|
|
else
|
|
reg_val = (val * (1 << 16) +
|
|
mul_u64_u32_div(val2, 1 << 16,
|
|
MICRO)) / 2;
|
|
|
|
return regmap_write(st->regmap16,
|
|
AD4695_REG_GAIN_IN(chan->scan_index),
|
|
reg_val);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
if (val2 >= 0 && val > S16_MAX / 4)
|
|
reg_val = S16_MAX;
|
|
else if ((val2 < 0 ? -val : val) < S16_MIN / 4)
|
|
reg_val = S16_MIN;
|
|
else if (val2 < 0)
|
|
reg_val = clamp_t(int,
|
|
-(val * 4 + -val2 * 4 / MICRO),
|
|
S16_MIN, S16_MAX);
|
|
else if (val < 0)
|
|
reg_val = clamp_t(int,
|
|
val * 4 - val2 * 4 / MICRO,
|
|
S16_MIN, S16_MAX);
|
|
else
|
|
reg_val = clamp_t(int,
|
|
val * 4 + val2 * 4 / MICRO,
|
|
S16_MIN, S16_MAX);
|
|
|
|
return regmap_write(st->regmap16,
|
|
AD4695_REG_OFFSET_IN(chan->scan_index),
|
|
reg_val);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
unreachable();
|
|
}
|
|
|
|
static int ad4695_read_avail(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
const int **vals, int *type, int *length,
|
|
long mask)
|
|
{
|
|
static const int ad4695_calibscale_available[6] = {
|
|
/* Range of 0 (inclusive) to 2 (exclusive) */
|
|
0, 15, 1, 15, U16_MAX, 15
|
|
};
|
|
static const int ad4695_calibbias_available[6] = {
|
|
/*
|
|
* Datasheet says FSR/8 which translates to signed/4. The step
|
|
* depends on oversampling ratio which is always 1 for now.
|
|
*/
|
|
S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4
|
|
};
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_CALIBSCALE:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
*vals = ad4695_calibscale_available;
|
|
*type = IIO_VAL_FRACTIONAL_LOG2;
|
|
return IIO_AVAIL_RANGE;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
*vals = ad4695_calibbias_available;
|
|
*type = IIO_VAL_INT_PLUS_MICRO;
|
|
return IIO_AVAIL_RANGE;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev,
|
|
unsigned int reg,
|
|
unsigned int writeval,
|
|
unsigned int *readval)
|
|
{
|
|
struct ad4695_state *st = iio_priv(indio_dev);
|
|
|
|
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
|
if (readval) {
|
|
if (regmap_check_range_table(st->regmap, reg,
|
|
&ad4695_regmap_rd_table))
|
|
return regmap_read(st->regmap, reg, readval);
|
|
if (regmap_check_range_table(st->regmap16, reg,
|
|
&ad4695_regmap16_rd_table))
|
|
return regmap_read(st->regmap16, reg, readval);
|
|
} else {
|
|
if (regmap_check_range_table(st->regmap, reg,
|
|
&ad4695_regmap_wr_table))
|
|
return regmap_write(st->regmap, reg, writeval);
|
|
if (regmap_check_range_table(st->regmap16, reg,
|
|
&ad4695_regmap16_wr_table))
|
|
return regmap_write(st->regmap16, reg, writeval);
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct iio_info ad4695_info = {
|
|
.read_raw = &ad4695_read_raw,
|
|
.write_raw = &ad4695_write_raw,
|
|
.read_avail = &ad4695_read_avail,
|
|
.debugfs_reg_access = &ad4695_debugfs_reg_access,
|
|
};
|
|
|
|
static int ad4695_parse_channel_cfg(struct ad4695_state *st)
|
|
{
|
|
struct device *dev = &st->spi->dev;
|
|
struct ad4695_channel_config *chan_cfg;
|
|
struct iio_chan_spec *iio_chan;
|
|
int ret, i;
|
|
|
|
/* populate defaults */
|
|
for (i = 0; i < st->chip_info->num_voltage_inputs; i++) {
|
|
chan_cfg = &st->channels_cfg[i];
|
|
iio_chan = &st->iio_chan[i];
|
|
|
|
chan_cfg->highz_en = true;
|
|
chan_cfg->channel = i;
|
|
|
|
*iio_chan = ad4695_channel_template;
|
|
iio_chan->channel = i;
|
|
iio_chan->scan_index = i;
|
|
iio_chan->address = AD4695_CMD_VOLTAGE_CHAN(i);
|
|
}
|
|
|
|
/* modify based on firmware description */
|
|
device_for_each_child_node_scoped(dev, child) {
|
|
u32 reg, val;
|
|
|
|
ret = fwnode_property_read_u32(child, "reg", ®);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"failed to read reg property (%s)\n",
|
|
fwnode_get_name(child));
|
|
|
|
if (reg >= st->chip_info->num_voltage_inputs)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"reg out of range (%s)\n",
|
|
fwnode_get_name(child));
|
|
|
|
iio_chan = &st->iio_chan[reg];
|
|
chan_cfg = &st->channels_cfg[reg];
|
|
|
|
chan_cfg->highz_en =
|
|
!fwnode_property_read_bool(child, "adi,no-high-z");
|
|
chan_cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
|
|
|
|
ret = fwnode_property_read_u32(child, "common-mode-channel",
|
|
&val);
|
|
if (ret && ret != -EINVAL)
|
|
return dev_err_probe(dev, ret,
|
|
"failed to read common-mode-channel (%s)\n",
|
|
fwnode_get_name(child));
|
|
|
|
if (ret == -EINVAL || val == AD4695_COMMON_MODE_REFGND)
|
|
chan_cfg->pin_pairing = AD4695_IN_PAIR_REFGND;
|
|
else if (val == AD4695_COMMON_MODE_COM)
|
|
chan_cfg->pin_pairing = AD4695_IN_PAIR_COM;
|
|
else
|
|
chan_cfg->pin_pairing = AD4695_IN_PAIR_EVEN_ODD;
|
|
|
|
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
|
|
val % 2 == 0)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"common-mode-channel must be odd number (%s)\n",
|
|
fwnode_get_name(child));
|
|
|
|
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
|
|
val != reg + 1)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"common-mode-channel must be next consecutive channel (%s)\n",
|
|
fwnode_get_name(child));
|
|
|
|
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) {
|
|
char name[5];
|
|
|
|
snprintf(name, sizeof(name), "in%d", reg + 1);
|
|
|
|
ret = devm_regulator_get_enable_read_voltage(dev, name);
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret,
|
|
"failed to get %s voltage (%s)\n",
|
|
name, fwnode_get_name(child));
|
|
|
|
chan_cfg->common_mode_mv = ret / 1000;
|
|
}
|
|
|
|
if (chan_cfg->bipolar &&
|
|
chan_cfg->pin_pairing == AD4695_IN_PAIR_REFGND)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"bipolar mode is not available for inputs paired with REFGND (%s).\n",
|
|
fwnode_get_name(child));
|
|
|
|
if (chan_cfg->bipolar)
|
|
iio_chan->scan_type.sign = 's';
|
|
|
|
ret = ad4695_write_chn_cfg(st, chan_cfg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* Temperature channel must be next scan index after voltage channels. */
|
|
st->iio_chan[i] = ad4695_temp_channel_template;
|
|
st->iio_chan[i].scan_index = i;
|
|
i++;
|
|
|
|
st->iio_chan[i] = ad4695_soft_timestamp_channel_template;
|
|
st->iio_chan[i].scan_index = i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad4695_probe(struct spi_device *spi)
|
|
{
|
|
struct device *dev = &spi->dev;
|
|
struct ad4695_state *st;
|
|
struct iio_dev *indio_dev;
|
|
struct gpio_desc *cnv_gpio;
|
|
bool use_internal_ldo_supply;
|
|
bool use_internal_ref_buffer;
|
|
int ret;
|
|
|
|
cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
|
|
if (IS_ERR(cnv_gpio))
|
|
return dev_err_probe(dev, PTR_ERR(cnv_gpio),
|
|
"Failed to get CNV GPIO\n");
|
|
|
|
/* Driver currently requires CNV pin to be connected to SPI CS */
|
|
if (cnv_gpio)
|
|
return dev_err_probe(dev, -ENODEV,
|
|
"CNV GPIO is not supported\n");
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
st = iio_priv(indio_dev);
|
|
st->spi = spi;
|
|
|
|
st->chip_info = spi_get_device_match_data(spi);
|
|
if (!st->chip_info)
|
|
return -EINVAL;
|
|
|
|
/* Registers cannot be read at the max allowable speed */
|
|
spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ;
|
|
|
|
st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config);
|
|
if (IS_ERR(st->regmap))
|
|
return dev_err_probe(dev, PTR_ERR(st->regmap),
|
|
"Failed to initialize regmap\n");
|
|
|
|
st->regmap16 = devm_regmap_init_spi(spi, &ad4695_regmap16_config);
|
|
if (IS_ERR(st->regmap16))
|
|
return dev_err_probe(dev, PTR_ERR(st->regmap16),
|
|
"Failed to initialize regmap16\n");
|
|
|
|
ret = devm_regulator_bulk_get_enable(dev,
|
|
ARRAY_SIZE(ad4695_power_supplies),
|
|
ad4695_power_supplies);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"Failed to enable power supplies\n");
|
|
|
|
/* If LDO_IN supply is present, then we are using internal LDO. */
|
|
ret = devm_regulator_get_enable_optional(dev, "ldo-in");
|
|
if (ret < 0 && ret != -ENODEV)
|
|
return dev_err_probe(dev, ret,
|
|
"Failed to enable LDO_IN supply\n");
|
|
|
|
use_internal_ldo_supply = ret == 0;
|
|
|
|
if (!use_internal_ldo_supply) {
|
|
/* Otherwise we need an external VDD supply. */
|
|
ret = devm_regulator_get_enable(dev, "vdd");
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret,
|
|
"Failed to enable VDD supply\n");
|
|
}
|
|
|
|
/* If REFIN supply is given, then we are using internal buffer */
|
|
ret = devm_regulator_get_enable_read_voltage(dev, "refin");
|
|
if (ret < 0 && ret != -ENODEV)
|
|
return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n");
|
|
|
|
if (ret != -ENODEV) {
|
|
st->vref_mv = ret / 1000;
|
|
use_internal_ref_buffer = true;
|
|
} else {
|
|
/* Otherwise, we need an external reference. */
|
|
ret = devm_regulator_get_enable_read_voltage(dev, "ref");
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret,
|
|
"Failed to get REF voltage\n");
|
|
|
|
st->vref_mv = ret / 1000;
|
|
use_internal_ref_buffer = false;
|
|
}
|
|
|
|
ret = devm_regulator_get_enable_read_voltage(dev, "com");
|
|
if (ret < 0 && ret != -ENODEV)
|
|
return dev_err_probe(dev, ret, "Failed to get COM voltage\n");
|
|
|
|
st->com_mv = ret == -ENODEV ? 0 : ret / 1000;
|
|
|
|
/*
|
|
* Reset the device using hardware reset if available or fall back to
|
|
* software reset.
|
|
*/
|
|
|
|
st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
|
if (IS_ERR(st->reset_gpio))
|
|
return PTR_ERR(st->reset_gpio);
|
|
|
|
if (st->reset_gpio) {
|
|
gpiod_set_value(st->reset_gpio, 0);
|
|
msleep(AD4695_T_WAKEUP_HW_MS);
|
|
} else {
|
|
ret = regmap_write(st->regmap, AD4695_REG_SPI_CONFIG_A,
|
|
AD4695_REG_SPI_CONFIG_A_SW_RST);
|
|
if (ret)
|
|
return ret;
|
|
|
|
msleep(AD4695_T_WAKEUP_SW_MS);
|
|
}
|
|
|
|
/* Needed for regmap16 to be able to work correctly. */
|
|
ret = regmap_set_bits(st->regmap, AD4695_REG_SPI_CONFIG_A,
|
|
AD4695_REG_SPI_CONFIG_A_ADDR_DIR);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Disable internal LDO if it isn't needed. */
|
|
ret = regmap_update_bits(st->regmap, AD4695_REG_SETUP,
|
|
AD4695_REG_SETUP_LDO_EN,
|
|
FIELD_PREP(AD4695_REG_SETUP_LDO_EN,
|
|
use_internal_ldo_supply ? 1 : 0));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* configure reference supply */
|
|
|
|
if (device_property_present(dev, "adi,no-ref-current-limit")) {
|
|
ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
|
|
AD4695_REG_REF_CTRL_OV_MODE);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (device_property_present(dev, "adi,no-ref-high-z")) {
|
|
if (use_internal_ref_buffer)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"Cannot disable high-Z mode for internal reference buffer\n");
|
|
|
|
ret = regmap_clear_bits(st->regmap, AD4695_REG_REF_CTRL,
|
|
AD4695_REG_REF_CTRL_REFHIZ_EN);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
ret = ad4695_set_ref_voltage(st, st->vref_mv);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (use_internal_ref_buffer) {
|
|
ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
|
|
AD4695_REG_REF_CTRL_REFBUF_EN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Give the capacitor some time to charge up. */
|
|
msleep(AD4695_T_REFBUF_MS);
|
|
}
|
|
|
|
ret = ad4695_parse_channel_cfg(st);
|
|
if (ret)
|
|
return ret;
|
|
|
|
indio_dev->name = st->chip_info->name;
|
|
indio_dev->info = &ad4695_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->channels = st->iio_chan;
|
|
indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
|
|
|
|
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
|
iio_pollfunc_store_time,
|
|
ad4695_trigger_handler,
|
|
&ad4695_buffer_setup_ops);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return devm_iio_device_register(dev, indio_dev);
|
|
}
|
|
|
|
static const struct spi_device_id ad4695_spi_id_table[] = {
|
|
{ .name = "ad4695", .driver_data = (kernel_ulong_t)&ad4695_chip_info },
|
|
{ .name = "ad4696", .driver_data = (kernel_ulong_t)&ad4696_chip_info },
|
|
{ .name = "ad4697", .driver_data = (kernel_ulong_t)&ad4697_chip_info },
|
|
{ .name = "ad4698", .driver_data = (kernel_ulong_t)&ad4698_chip_info },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, ad4695_spi_id_table);
|
|
|
|
static const struct of_device_id ad4695_of_match_table[] = {
|
|
{ .compatible = "adi,ad4695", .data = &ad4695_chip_info, },
|
|
{ .compatible = "adi,ad4696", .data = &ad4696_chip_info, },
|
|
{ .compatible = "adi,ad4697", .data = &ad4697_chip_info, },
|
|
{ .compatible = "adi,ad4698", .data = &ad4698_chip_info, },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ad4695_of_match_table);
|
|
|
|
static struct spi_driver ad4695_driver = {
|
|
.driver = {
|
|
.name = "ad4695",
|
|
.of_match_table = ad4695_of_match_table,
|
|
},
|
|
.probe = ad4695_probe,
|
|
.id_table = ad4695_spi_id_table,
|
|
};
|
|
module_spi_driver(ad4695_driver);
|
|
|
|
MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
|
|
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
|
|
MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver");
|
|
MODULE_LICENSE("GPL");
|