summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/ad7606.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/ad7606.c')
-rw-r--r--drivers/iio/adc/ad7606.c1504
1 files changed, 1241 insertions, 263 deletions
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index 0a60ecc69d38..d9271894f091 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -5,6 +5,7 @@
* Copyright 2011 Analog Devices Inc.
*/
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -12,14 +13,19 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
+#include <linux/units.h>
#include <linux/util_macros.h>
-#include <linux/iio/iio.h>
+#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
@@ -27,28 +33,247 @@
#include "ad7606.h"
+#define AD7606_CALIB_GAIN_MIN 0
+#define AD7606_CALIB_GAIN_STEP 1024
+#define AD7606_CALIB_GAIN_MAX (63 * AD7606_CALIB_GAIN_STEP)
+
/*
* Scales are computed as 5000/32768 and 10000/32768 respectively,
- * so that when applied to the raw values they provide mV values
+ * so that when applied to the raw values they provide mV values.
+ * The scale arrays are kept as IIO_VAL_INT_PLUS_MICRO, so index
+ * X is the integer part and X + 1 is the fractional part.
*/
-static const unsigned int ad7606_scale_avail[2] = {
- 152588, 305176
+static const unsigned int ad7606_16bit_hw_scale_avail[2][2] = {
+ { 0, 152588 }, { 0, 305176 }
+};
+
+static const unsigned int ad7606_18bit_hw_scale_avail[2][2] = {
+ { 0, 38147 }, { 0, 76294 }
+};
+
+static const unsigned int ad7606c_16bit_single_ended_unipolar_scale_avail[3][2] = {
+ { 0, 76294 }, { 0, 152588 }, { 0, 190735 }
+};
+
+static const unsigned int ad7606c_16bit_single_ended_bipolar_scale_avail[5][2] = {
+ { 0, 76294 }, { 0, 152588 }, { 0, 190735 }, { 0, 305176 }, { 0, 381470 }
+};
+
+static const unsigned int ad7606c_16bit_differential_bipolar_scale_avail[4][2] = {
+ { 0, 152588 }, { 0, 305176 }, { 0, 381470 }, { 0, 610352 }
+};
+
+static const unsigned int ad7606c_18bit_single_ended_unipolar_scale_avail[3][2] = {
+ { 0, 19073 }, { 0, 38147 }, { 0, 47684 }
+};
+
+static const unsigned int ad7606c_18bit_single_ended_bipolar_scale_avail[5][2] = {
+ { 0, 19073 }, { 0, 38147 }, { 0, 47684 }, { 0, 76294 }, { 0, 95367 }
};
+static const unsigned int ad7606c_18bit_differential_bipolar_scale_avail[4][2] = {
+ { 0, 38147 }, { 0, 76294 }, { 0, 95367 }, { 0, 152588 }
+};
-static const unsigned int ad7616_sw_scale_avail[3] = {
- 76293, 152588, 305176
+static const unsigned int ad7606_16bit_sw_scale_avail[3][2] = {
+ { 0, 76293 }, { 0, 152588 }, { 0, 305176 }
+};
+
+static const unsigned int ad7607_hw_scale_avail[2][2] = {
+ { 0, 610352 }, { 1, 220703 }
+};
+
+static const unsigned int ad7609_hw_scale_avail[2][2] = {
+ { 0, 152588 }, { 0, 305176 }
};
static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
+static const unsigned int ad7606b_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256,
+};
+
static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
-static int ad7606_reset(struct ad7606_state *st)
+static const int ad7606_calib_offset_avail[3] = {
+ -128, 1, 127,
+};
+
+static const int ad7606c_18bit_calib_offset_avail[3] = {
+ -512, 4, 508,
+};
+
+static const int ad7606b_calib_phase_avail[][2] = {
+ { 0, 0 }, { 0, 1250 }, { 0, 318750 },
+};
+
+static const int ad7606c_calib_phase_avail[][2] = {
+ { 0, 0 }, { 0, 1000 }, { 0, 255000 },
+};
+
+static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7607_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan);
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev);
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev);
+
+const struct ad7606_chip_info ad7605_4_info = {
+ .max_samplerate = 300 * KILO,
+ .name = "ad7605-4",
+ .bits = 16,
+ .num_adc_channels = 4,
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+};
+EXPORT_SYMBOL_NS_GPL(ad7605_4_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606_8_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7606-8",
+ .bits = 16,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606_8_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606_6_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7606-6",
+ .bits = 16,
+ .num_adc_channels = 6,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .offload_storagebits = 32,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606_4_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7606-4",
+ .bits = 16,
+ .num_adc_channels = 4,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .offload_storagebits = 32,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606b_info = {
+ .max_samplerate = 800 * KILO,
+ .name = "ad7606b",
+ .bits = 16,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
+ .offload_storagebits = 32,
+ .calib_gain_avail = true,
+ .calib_offset_avail = ad7606_calib_offset_avail,
+ .calib_phase_avail = ad7606b_calib_phase_avail,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606c_16_info = {
+ .max_samplerate = 1 * MEGA,
+ .name = "ad7606c16",
+ .bits = 16,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606c_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
+ .offload_storagebits = 32,
+ .calib_gain_avail = true,
+ .calib_offset_avail = ad7606_calib_offset_avail,
+ .calib_phase_avail = ad7606c_calib_phase_avail,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7607_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7607",
+ .bits = 14,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7607_chan_scale_setup,
+ .offload_storagebits = 32,
+};
+EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7608_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7608",
+ .bits = 18,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7608_chan_scale_setup,
+ .offload_storagebits = 32,
+};
+EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7609_info = {
+ .max_samplerate = 200 * KILO,
+ .name = "ad7609",
+ .bits = 18,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7609_chan_scale_setup,
+ .offload_storagebits = 32,
+};
+EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7606c_18_info = {
+ .max_samplerate = 1 * MEGA,
+ .name = "ad7606c18",
+ .bits = 18,
+ .num_adc_channels = 8,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ .scale_setup_cb = ad7606c_18bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
+ .offload_storagebits = 32,
+ .calib_gain_avail = true,
+ .calib_offset_avail = ad7606c_18bit_calib_offset_avail,
+ .calib_phase_avail = ad7606c_calib_phase_avail,
+};
+EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
+
+const struct ad7606_chip_info ad7616_info = {
+ .max_samplerate = 1 * MEGA,
+ .init_delay_ms = 15,
+ .name = "ad7616",
+ .bits = 16,
+ .num_adc_channels = 16,
+ .oversampling_avail = ad7616_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
+ .os_req_reset = true,
+ .scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7616_sw_mode_setup,
+ .offload_storagebits = 16,
+};
+EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606");
+
+int ad7606_reset(struct ad7606_state *st)
{
if (st->gpio_reset) {
gpiod_set_value(st->gpio_reset, 1);
@@ -59,6 +284,245 @@ static int ad7606_reset(struct ad7606_state *st)
return -ENODEV;
}
+EXPORT_SYMBOL_NS_GPL(ad7606_reset, "IIO_AD7606");
+
+static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+
+ if (!st->sw_mode_en) {
+ /* tied to logic low, analog input range is +/- 5V */
+ ci->range = 0;
+ ci->scale_avail = ad7606_16bit_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
+ return 0;
+ }
+
+ /* Scale of 0.076293 is only available in sw mode */
+ /* After reset, in software mode, ±10 V is set by default */
+ ci->range = 2;
+ ci->scale_avail = ad7606_16bit_sw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail);
+
+ return 0;
+}
+
+static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch,
+ bool *bipolar, bool *differential)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci;
+ unsigned int num_channels = st->chip_info->num_adc_channels;
+ struct device *dev = st->dev;
+ int ret;
+
+ *bipolar = false;
+ *differential = false;
+
+ device_for_each_child_node_scoped(dev, child) {
+ u32 pins[2];
+ int reg;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return ret;
+
+ /* channel number (here) is from 1 to num_channels */
+ if (reg < 1 || reg > num_channels)
+ return -EINVAL;
+
+ /* Loop until we are in the right channel. */
+ if (reg != (ch + 1))
+ continue;
+
+ *bipolar = fwnode_property_read_bool(child, "bipolar");
+
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ pins, ARRAY_SIZE(pins));
+ /* Channel is differential, if pins are the same as 'reg' */
+ if (ret == 0 && (pins[0] != reg || pins[1] != reg)) {
+ dev_err(dev,
+ "Differential pins must be the same as 'reg'");
+ return -EINVAL;
+ }
+
+ *differential = (ret == 0);
+
+ if (*differential && !*bipolar) {
+ dev_err(dev,
+ "'bipolar' must be added for diff channel %d\n",
+ reg);
+ return -EINVAL;
+ }
+
+ ci = &st->chan_info[reg - 1];
+
+ ci->r_gain = 0;
+ ret = fwnode_property_read_u32(child, "adi,rfilter-ohms",
+ &ci->r_gain);
+ if (ret == 0 && ci->r_gain > AD7606_CALIB_GAIN_MAX)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+ bool bipolar, differential;
+ int ret;
+
+ if (!st->sw_mode_en) {
+ ci->range = 0;
+ ci->scale_avail = ad7606_18bit_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
+ return 0;
+ }
+
+ ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar,
+ &differential);
+ if (ret)
+ return ret;
+
+ if (differential) {
+ ci->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail);
+ /* Bipolar differential ranges start at 8 (b1000) */
+ ci->reg_offset = 8;
+ ci->range = 1;
+ chan->differential = 1;
+ chan->channel2 = chan->channel;
+
+ return 0;
+ }
+
+ chan->differential = 0;
+
+ if (bipolar) {
+ ci->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail);
+ /* Bipolar single-ended ranges start at 0 (b0000) */
+ ci->reg_offset = 0;
+ ci->range = 3;
+ chan->scan_type.sign = 's';
+
+ return 0;
+ }
+
+ ci->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail);
+ /* Unipolar single-ended ranges start at 5 (b0101) */
+ ci->reg_offset = 5;
+ ci->range = 1;
+ chan->scan_type.sign = 'u';
+
+ return 0;
+}
+
+static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+ bool bipolar, differential;
+ int ret;
+
+ if (!st->sw_mode_en) {
+ ci->range = 0;
+ ci->scale_avail = ad7606_16bit_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
+ return 0;
+ }
+
+ ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar,
+ &differential);
+ if (ret)
+ return ret;
+
+ if (differential) {
+ ci->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail);
+ /* Bipolar differential ranges start at 8 (b1000) */
+ ci->reg_offset = 8;
+ ci->range = 1;
+ chan->differential = 1;
+ chan->channel2 = chan->channel;
+ chan->scan_type.sign = 's';
+
+ return 0;
+ }
+
+ chan->differential = 0;
+
+ if (bipolar) {
+ ci->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail);
+ /* Bipolar single-ended ranges start at 0 (b0000) */
+ ci->reg_offset = 0;
+ ci->range = 3;
+ chan->scan_type.sign = 's';
+
+ return 0;
+ }
+
+ ci->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail;
+ ci->num_scales =
+ ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail);
+ /* Unipolar single-ended ranges start at 5 (b0101) */
+ ci->reg_offset = 5;
+ ci->range = 1;
+ chan->scan_type.sign = 'u';
+
+ return 0;
+}
+
+static int ad7607_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+
+ ci->range = 0;
+ ci->scale_avail = ad7607_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail);
+ return 0;
+}
+
+static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+
+ ci->range = 0;
+ ci->scale_avail = ad7606_18bit_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
+ return 0;
+}
+
+static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
+
+ ci->range = 0;
+ ci->scale_avail = ad7609_hw_scale_avail;
+ ci->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail);
+ return 0;
+}
static int ad7606_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
@@ -68,52 +532,100 @@ static int ad7606_reg_access(struct iio_dev *indio_dev,
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
+
if (readval) {
ret = st->bops->reg_read(st, reg);
if (ret < 0)
- goto err_unlock;
+ return ret;
*readval = ret;
- ret = 0;
+ return 0;
} else {
- ret = st->bops->reg_write(st, reg, writeval);
+ return st->bops->reg_write(st, reg, writeval);
}
-err_unlock:
- mutex_unlock(&st->lock);
+}
+
+static int ad7606_pwm_set_high(struct ad7606_state *st)
+{
+ struct pwm_state cnvst_pwm_state;
+ int ret;
+
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+ cnvst_pwm_state.enabled = true;
+ cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period;
+
+ ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state);
+
return ret;
}
-static int ad7606_read_samples(struct ad7606_state *st)
+int ad7606_pwm_set_low(struct ad7606_state *st)
{
- unsigned int num = st->chip_info->num_channels - 1;
- u16 *data = st->data;
+ struct pwm_state cnvst_pwm_state;
int ret;
- /*
- * The frstdata signal is set to high while and after reading the sample
- * of the first channel and low for all other channels. This can be used
- * to check that the incoming data is correctly aligned. During normal
- * operation the data should never become unaligned, but some glitch or
- * electrostatic discharge might cause an extra read or clock cycle.
- * Monitoring the frstdata signal allows to recover from such failure
- * situations.
- */
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+ cnvst_pwm_state.enabled = true;
+ cnvst_pwm_state.duty_cycle = 0;
- if (st->gpio_frstdata) {
- ret = st->bops->read_block(st->dev, 1, data);
- if (ret)
- return ret;
+ ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state);
- if (!gpiod_get_value(st->gpio_frstdata)) {
- ad7606_reset(st);
- return -EIO;
- }
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_low, "IIO_AD7606");
- data++;
- num--;
- }
+int ad7606_pwm_set_swing(struct ad7606_state *st)
+{
+ struct pwm_state cnvst_pwm_state;
+
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+ cnvst_pwm_state.enabled = true;
+ cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2;
+
+ return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state);
+}
+EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_swing, "IIO_AD7606");
+
+static bool ad7606_pwm_is_swinging(struct ad7606_state *st)
+{
+ struct pwm_state cnvst_pwm_state;
+
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+
+ return cnvst_pwm_state.duty_cycle != cnvst_pwm_state.period &&
+ cnvst_pwm_state.duty_cycle != 0;
+}
+
+static int ad7606_set_sampling_freq(struct ad7606_state *st, unsigned long freq)
+{
+ struct pwm_state cnvst_pwm_state;
+ bool is_swinging = ad7606_pwm_is_swinging(st);
+ bool is_high;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ /* Retrieve the previous state. */
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+ is_high = cnvst_pwm_state.duty_cycle == cnvst_pwm_state.period;
+
+ cnvst_pwm_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq);
+ cnvst_pwm_state.polarity = PWM_POLARITY_NORMAL;
+ if (is_high)
+ cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period;
+ else if (is_swinging)
+ cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2;
+ else
+ cnvst_pwm_state.duty_cycle = 0;
+
+ return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state);
+}
+
+static int ad7606_read_samples(struct ad7606_state *st)
+{
+ unsigned int num = st->chip_info->num_adc_channels;
- return st->bops->read_block(st->dev, num, data);
+ return st->bops->read_block(st->dev, num, &st->data);
}
static irqreturn_t ad7606_trigger_handler(int irq, void *p)
@@ -123,45 +635,122 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p)
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
ret = ad7606_read_samples(st);
- if (ret == 0)
- iio_push_to_buffers_with_timestamp(indio_dev, st->data,
- iio_get_time_ns(indio_dev));
+ if (ret)
+ goto error_ret;
+ iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data),
+ iio_get_time_ns(indio_dev));
+error_ret:
iio_trigger_notify_done(indio_dev->trig);
/* The rising edge of the CONVST signal starts a new conversion. */
gpiod_set_value(st->gpio_convst, 1);
- mutex_unlock(&st->lock);
-
return IRQ_HANDLED;
}
-static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
+static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch,
+ int *val)
{
struct ad7606_state *st = iio_priv(indio_dev);
+ const struct iio_chan_spec *chan;
+ unsigned int realbits;
int ret;
- gpiod_set_value(st->gpio_convst, 1);
- ret = wait_for_completion_timeout(&st->completion,
- msecs_to_jiffies(1000));
- if (!ret) {
- ret = -ETIMEDOUT;
- goto error_ret;
+ if (st->gpio_convst) {
+ gpiod_set_value(st->gpio_convst, 1);
+ } else {
+ ret = ad7606_pwm_set_high(st);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * If no backend, wait for the interruption on busy pin, otherwise just add
+ * a delay to leave time for the data to be available. For now, the latter
+ * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend.
+ * TODO: Add support for reading a single value when the backend is used.
+ */
+ if (st->trig) {
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto error_ret;
+ }
+ } else {
+ /*
+ * If the BUSY interrupt is not available, wait enough time for
+ * the longest possible conversion (max for the whole family is
+ * around 350us).
+ */
+ fsleep(400);
}
ret = ad7606_read_samples(st);
- if (ret == 0)
- ret = st->data[ch];
+ if (ret)
+ goto error_ret;
+
+ chan = &indio_dev->channels[ch];
+ realbits = chan->scan_type.realbits;
+
+ if (realbits > 16)
+ *val = st->data.buf32[ch];
+ else
+ *val = st->data.buf16[ch];
+
+ *val &= GENMASK(realbits - 1, 0);
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val, realbits - 1);
error_ret:
+ if (!st->gpio_convst) {
+ ret = ad7606_pwm_set_low(st);
+ if (ret < 0)
+ return ret;
+ }
gpiod_set_value(st->gpio_convst, 0);
return ret;
}
+static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val)
+{
+ int ret;
+
+ ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch));
+ if (ret < 0)
+ return ret;
+
+ *val = st->chip_info->calib_offset_avail[0] +
+ ret * st->chip_info->calib_offset_avail[1];
+
+ return 0;
+}
+
+static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val,
+ int *val2)
+{
+ int ret;
+
+ ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch));
+ if (ret < 0)
+ return ret;
+
+ *val = 0;
+
+ /*
+ * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
+ * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
+ */
+ *val2 = ret * st->chip_info->calib_phase_avail[1][1];
+
+ return 0;
+}
+
static int ad7606_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
@@ -170,56 +759,69 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
{
int ret, ch = 0;
struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci;
+ struct pwm_state cnvst_pwm_state;
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
- ret = ad7606_scan_direct(indio_dev, chan->address);
- iio_device_release_direct_mode(indio_dev);
-
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_scan_direct(indio_dev, chan->scan_index, val);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
- *val = (short)ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
- ch = chan->address;
- *val = 0;
- *val2 = st->scale_avail[st->range[ch]];
+ ch = chan->scan_index;
+ ci = &st->chan_info[ch];
+ *val = ci->scale_avail[ci->range][0];
+ *val2 = ci->scale_avail[ci->range][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
+ *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_get_calib_offset(st, chan->scan_index, val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CONVDELAY:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
-static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
- unsigned int n, bool micros)
-{
- size_t len = 0;
- int i;
-
- for (i = 0; i < n; i++) {
- len += scnprintf(buf + len, PAGE_SIZE - len,
- micros ? "0.%06u " : "%u ", vals[i]);
- }
- buf[len - 1] = '\n';
-
- return len;
-}
-
static ssize_t in_voltage_scale_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci = &st->chan_info[0];
+ const unsigned int (*vals)[2] = ci->scale_avail;
+ unsigned int i;
+ size_t len = 0;
+
+ for (i = 0; i < ci->num_scales; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
+ vals[i][0], vals[i][1]);
+ buf[len - 1] = '\n';
- return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
+ return len;
}
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
@@ -238,10 +840,9 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(values, 3);
- values[0] = val;
+ values[0] = val & GENMASK(2, 0);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- st->gpio_os->info, values);
+ gpiod_multi_set_value_cansleep(st->gpio_os, values);
/* AD7616 requires a reset to update value */
if (st->chip_info->os_req_reset)
@@ -250,6 +851,64 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
return 0;
}
+static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val)
+{
+ int start_val, step_val, stop_val;
+ int offset;
+
+ start_val = st->chip_info->calib_offset_avail[0];
+ step_val = st->chip_info->calib_offset_avail[1];
+ stop_val = st->chip_info->calib_offset_avail[2];
+
+ if (val < start_val || val > stop_val)
+ return -EINVAL;
+
+ offset = (val - start_val) / step_val;
+
+ return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset);
+}
+
+static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val,
+ int val2)
+{
+ int wreg, start_ns, step_ns, stop_ns;
+
+ if (val != 0)
+ return -EINVAL;
+
+ start_ns = st->chip_info->calib_phase_avail[0][1];
+ step_ns = st->chip_info->calib_phase_avail[1][1];
+ stop_ns = st->chip_info->calib_phase_avail[2][1];
+
+ /*
+ * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
+ * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
+ */
+ if (val2 < start_ns || val2 > stop_ns)
+ return -EINVAL;
+
+ wreg = val2 / step_ns;
+
+ return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg);
+}
+
+static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
static int ad7606_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
@@ -257,21 +916,31 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int scale_avail_uv[AD760X_MAX_SCALES];
+ struct ad7606_chan_info *ci;
int i, ret, ch = 0;
+ guard(mutex)(&st->lock);
+
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- mutex_lock(&st->lock);
- i = find_closest(val2, st->scale_avail, st->num_scales);
if (st->sw_mode_en)
- ch = chan->address;
- ret = st->write_scale(indio_dev, ch, i);
- if (ret < 0) {
- mutex_unlock(&st->lock);
- return ret;
+ ch = chan->scan_index;
+ ci = &st->chan_info[ch];
+ for (i = 0; i < ci->num_scales; i++) {
+ scale_avail_uv[i] = ci->scale_avail[i][0] * MICRO +
+ ci->scale_avail[i][1];
}
- st->range[ch] = i;
- mutex_unlock(&st->lock);
+ val = (val * MICRO) + val2;
+ i = find_closest(val, scale_avail_uv, ci->num_scales);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = st->write_scale(indio_dev, ch, i + ci->reg_offset);
+ iio_device_release_direct(indio_dev);
+ if (ret < 0)
+ return ret;
+ ci->range = i;
return 0;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -279,16 +948,32 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
- mutex_lock(&st->lock);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = st->write_os(indio_dev, i);
- if (ret < 0) {
- mutex_unlock(&st->lock);
+ iio_device_release_direct(indio_dev);
+ if (ret < 0)
return ret;
- }
st->oversampling = st->oversampling_avail[i];
- mutex_unlock(&st->lock);
return 0;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < 0 && val2 != 0)
+ return -EINVAL;
+ return ad7606_set_sampling_freq(st, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_set_calib_offset(st, chan->scan_index, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_CONVDELAY:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
default:
return -EINVAL;
}
@@ -300,9 +985,15 @@ static ssize_t ad7606_oversampling_ratio_avail(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
+ const unsigned int *vals = st->oversampling_avail;
+ unsigned int i;
+ size_t len = 0;
- return ad7606_show_avail(buf, st->oversampling_avail,
- st->num_os_ratios, false);
+ for (i = 0; i < st->num_os_ratios; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u ", vals[i]);
+ buf[len - 1] = '\n';
+
+ return len;
}
static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
@@ -336,102 +1027,13 @@ static const struct attribute_group ad7606_attribute_group_range = {
.attrs = ad7606_attributes_range,
};
-static const struct iio_chan_spec ad7605_channels[] = {
- IIO_CHAN_SOFT_TIMESTAMP(4),
- AD7605_CHANNEL(0),
- AD7605_CHANNEL(1),
- AD7605_CHANNEL(2),
- AD7605_CHANNEL(3),
-};
-
-static const struct iio_chan_spec ad7606_channels[] = {
- IIO_CHAN_SOFT_TIMESTAMP(8),
- AD7606_CHANNEL(0),
- AD7606_CHANNEL(1),
- AD7606_CHANNEL(2),
- AD7606_CHANNEL(3),
- AD7606_CHANNEL(4),
- AD7606_CHANNEL(5),
- AD7606_CHANNEL(6),
- AD7606_CHANNEL(7),
-};
-
-/*
- * The current assumption that this driver makes for AD7616, is that it's
- * working in Hardware Mode with Serial, Burst and Sequencer modes activated.
- * To activate them, following pins must be pulled high:
- * -SER/PAR
- * -SEQEN
- * And following pins must be pulled low:
- * -WR/BURST
- * -DB4/SER1W
- */
-static const struct iio_chan_spec ad7616_channels[] = {
- IIO_CHAN_SOFT_TIMESTAMP(16),
- AD7606_CHANNEL(0),
- AD7606_CHANNEL(1),
- AD7606_CHANNEL(2),
- AD7606_CHANNEL(3),
- AD7606_CHANNEL(4),
- AD7606_CHANNEL(5),
- AD7606_CHANNEL(6),
- AD7606_CHANNEL(7),
- AD7606_CHANNEL(8),
- AD7606_CHANNEL(9),
- AD7606_CHANNEL(10),
- AD7606_CHANNEL(11),
- AD7606_CHANNEL(12),
- AD7606_CHANNEL(13),
- AD7606_CHANNEL(14),
- AD7606_CHANNEL(15),
-};
-
-static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
- /* More devices added in future */
- [ID_AD7605_4] = {
- .channels = ad7605_channels,
- .num_channels = 5,
- },
- [ID_AD7606_8] = {
- .channels = ad7606_channels,
- .num_channels = 9,
- .oversampling_avail = ad7606_oversampling_avail,
- .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
- },
- [ID_AD7606_6] = {
- .channels = ad7606_channels,
- .num_channels = 7,
- .oversampling_avail = ad7606_oversampling_avail,
- .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
- },
- [ID_AD7606_4] = {
- .channels = ad7606_channels,
- .num_channels = 5,
- .oversampling_avail = ad7606_oversampling_avail,
- .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
- },
- [ID_AD7606B] = {
- .channels = ad7606_channels,
- .num_channels = 9,
- .oversampling_avail = ad7606_oversampling_avail,
- .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
- },
- [ID_AD7616] = {
- .channels = ad7616_channels,
- .num_channels = 17,
- .oversampling_avail = ad7616_oversampling_avail,
- .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
- .os_req_reset = true,
- .init_delay_ms = 15,
- },
-};
-
static int ad7606_request_gpios(struct ad7606_state *st)
{
struct device *dev = st->dev;
- st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
- GPIOD_OUT_LOW);
+ st->gpio_convst = devm_gpiod_get_optional(dev, "adi,conversion-start",
+ GPIOD_OUT_LOW);
+
if (IS_ERR(st->gpio_convst))
return PTR_ERR(st->gpio_convst);
@@ -445,7 +1047,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
return PTR_ERR(st->gpio_range);
st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
- GPIOD_OUT_HIGH);
+ GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_standby))
return PTR_ERR(st->gpio_standby);
@@ -473,14 +1075,24 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
if (iio_buffer_enabled(indio_dev)) {
- gpiod_set_value(st->gpio_convst, 0);
- iio_trigger_poll_chained(st->trig);
+ if (st->gpio_convst) {
+ gpiod_set_value(st->gpio_convst, 0);
+ } else {
+ ret = ad7606_pwm_set_low(st);
+ if (ret < 0) {
+ dev_err(st->dev, "PWM set low failed");
+ goto done;
+ }
+ }
+ iio_trigger_poll_nested(st->trig);
} else {
complete(&st->completion);
}
+done:
return IRQ_HANDLED;
};
@@ -513,14 +1125,89 @@ static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
return 0;
}
+static int ad7606_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct ad7606_chan_info *ci;
+ unsigned int ch = 0;
+
+ switch (info) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = st->oversampling_avail;
+ *length = st->num_os_ratios;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (st->sw_mode_en)
+ ch = chan->scan_index;
+
+ ci = &st->chan_info[ch];
+ *vals = (int *)ci->scale_avail;
+ *length = ci->num_scales * 2;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = st->chip_info->calib_offset_avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_CONVDELAY:
+ *vals = (const int *)st->chip_info->calib_phase_avail;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_RANGE;
+ }
+ return -EINVAL;
+}
+
+static int ad7606_backend_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_pwm_set_swing(st);
+}
+
+static int ad7606_backend_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_pwm_set_low(st);
+}
+
+static int ad7606_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ /*
+ * The update scan mode is only for iio backend compatible drivers.
+ * If the specific update_scan_mode is not defined in the bus ops,
+ * just do nothing and return 0.
+ */
+ if (!st->bops->update_scan_mode)
+ return 0;
+
+ return st->bops->update_scan_mode(indio_dev, scan_mask);
+}
+
static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
.postenable = &ad7606_buffer_postenable,
.predisable = &ad7606_buffer_predisable,
};
+static const struct iio_buffer_setup_ops ad7606_backend_buffer_ops = {
+ .postenable = &ad7606_backend_buffer_postenable,
+ .predisable = &ad7606_backend_buffer_predisable,
+};
+
static const struct iio_info ad7606_info_no_os_or_range = {
.read_raw = &ad7606_read_raw,
.validate_trigger = &ad7606_validate_trigger,
+ .update_scan_mode = &ad7606_update_scan_mode,
};
static const struct iio_info ad7606_info_os_and_range = {
@@ -528,14 +1215,17 @@ static const struct iio_info ad7606_info_os_and_range = {
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os_and_range,
.validate_trigger = &ad7606_validate_trigger,
+ .update_scan_mode = &ad7606_update_scan_mode,
};
-static const struct iio_info ad7606_info_os_range_and_debug = {
+static const struct iio_info ad7606_info_sw_mode = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
+ .read_avail = &ad7606_read_avail,
+ .write_raw_get_fmt = ad7606_write_raw_get_fmt,
.debugfs_reg_access = &ad7606_reg_access,
- .attrs = &ad7606_attribute_group_os_and_range,
.validate_trigger = &ad7606_validate_trigger,
+ .update_scan_mode = &ad7606_update_scan_mode,
};
static const struct iio_info ad7606_info_os = {
@@ -543,6 +1233,7 @@ static const struct iio_info ad7606_info_os = {
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os,
.validate_trigger = &ad7606_validate_trigger,
+ .update_scan_mode = &ad7606_update_scan_mode,
};
static const struct iio_info ad7606_info_range = {
@@ -550,21 +1241,254 @@ static const struct iio_info ad7606_info_range = {
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_range,
.validate_trigger = &ad7606_validate_trigger,
+ .update_scan_mode = &ad7606_update_scan_mode,
};
static const struct iio_trigger_ops ad7606_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
-static void ad7606_regulator_disable(void *data)
+static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr,
+ unsigned long mask, unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
- struct ad7606_state *st = data;
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
+
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
- regulator_disable(st->reg);
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+
+ return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ if (st->bops->sw_mode_config) {
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ /* Activate Burst mode and SEQEN MODE */
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(os, 3);
+
+ bitmap_fill(os, 3);
+ /*
+ * Software mode is enabled when all three oversampling
+ * pins are set to high. If oversampling gpios are defined
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os)
+ gpiod_multi_set_value_cansleep(st->gpio_os, os);
+
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606b_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
+
+ if (!st->bops->sw_mode_config)
+ return 0;
+
+ return st->bops->sw_mode_config(indio_dev);
+}
+
+static int ad7606_set_gain_calib(struct ad7606_state *st)
+{
+ struct ad7606_chan_info *ci;
+ int i, ret;
+
+ for (i = 0; i < st->chip_info->num_adc_channels; i++) {
+ ci = &st->chan_info[i];
+ ret = st->bops->reg_write(st, AD7606_CALIB_GAIN(i),
+ DIV_ROUND_CLOSEST(ci->r_gain,
+ AD7606_CALIB_GAIN_STEP));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7606_probe_channels(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *channels;
+ bool slow_bus;
+ int ret, i;
+
+ slow_bus = !(st->bops->iio_backend_config || st->offload_en);
+ indio_dev->num_channels = st->chip_info->num_adc_channels;
+
+ /* Slow buses also get 1 more channel for soft timestamp */
+ if (slow_bus)
+ indio_dev->num_channels++;
+
+ channels = devm_kcalloc(dev, indio_dev->num_channels, sizeof(*channels),
+ GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_adc_channels; i++) {
+ struct iio_chan_spec *chan = &channels[i];
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = i;
+ chan->scan_index = i;
+ chan->scan_type.sign = 's';
+ chan->scan_type.realbits = st->chip_info->bits;
+ /*
+ * If in SPI offload mode, storagebits are set based
+ * on the spi-engine hw implementation.
+ */
+ chan->scan_type.storagebits = st->offload_en ?
+ st->chip_info->offload_storagebits :
+ (st->chip_info->bits > 16 ? 32 : 16);
+
+ chan->scan_type.endianness = IIO_CPU;
+
+ if (indio_dev->modes & INDIO_DIRECT_MODE)
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW);
+
+ if (st->sw_mode_en) {
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate_available |=
+ BIT(IIO_CHAN_INFO_SCALE);
+
+ if (st->chip_info->calib_offset_avail) {
+ chan->info_mask_separate |=
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CONVDELAY);
+ chan->info_mask_separate_available |=
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CONVDELAY);
+ }
+
+ /*
+ * All chips with software mode support oversampling,
+ * so we skip the oversampling_available check. And the
+ * shared_by_type instead of shared_by_all on slow
+ * buses is for backward compatibility.
+ */
+ if (slow_bus)
+ chan->info_mask_shared_by_type |=
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ else
+ chan->info_mask_shared_by_all |=
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+
+ chan->info_mask_shared_by_all_available |=
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ } else {
+ chan->info_mask_shared_by_type |=
+ BIT(IIO_CHAN_INFO_SCALE);
+
+ if (st->chip_info->oversampling_avail)
+ chan->info_mask_shared_by_all |=
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ }
+
+ if (!slow_bus)
+ chan->info_mask_shared_by_all |=
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
+
+ ret = st->chip_info->scale_setup_cb(indio_dev, chan);
+ if (ret)
+ return ret;
+ }
+
+ if (slow_bus)
+ channels[i] = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(i);
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static void ad7606_pwm_disable(void *data)
+{
+ pwm_disable(data);
}
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
- const char *name, unsigned int id,
+ const struct ad7606_chip_info *chip_info,
const struct ad7606_bus_ops *bops)
{
struct ad7606_state *st;
@@ -578,31 +1502,36 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
st = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
st->dev = dev;
- mutex_init(&st->lock);
st->bops = bops;
st->base_address = base_address;
- /* tied to logic low, analog input range is +/- 5V */
- st->range[0] = 0;
st->oversampling = 1;
- st->scale_avail = ad7606_scale_avail;
- st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
+ st->sw_mode_en = device_property_read_bool(dev, "adi,sw-mode");
- st->reg = devm_regulator_get(dev, "avcc");
- if (IS_ERR(st->reg))
- return PTR_ERR(st->reg);
+ if (st->sw_mode_en && !chip_info->sw_setup_cb)
+ return dev_err_probe(dev, -EINVAL,
+ "Software mode is not supported for this chip\n");
- ret = regulator_enable(st->reg);
- if (ret) {
- dev_err(dev, "Failed to enable specified AVcc supply\n");
- return ret;
- }
+ ret = devm_regulator_get_enable(dev, "avcc");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable specified AVcc supply\n");
- ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
+ ret = devm_regulator_get_enable(dev, "vdrive");
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to enable Vdrive supply\n");
+
+ ret = devm_regulator_get_enable_optional(dev, "refin");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "Failed to enable REFIN supply\n");
- st->chip_info = &ad7606_chip_info_tbl[id];
+ st->chip_info = chip_info;
if (st->chip_info->oversampling_num) {
st->oversampling_avail = st->chip_info->oversampling_avail;
@@ -624,12 +1553,21 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
else
indio_dev->info = &ad7606_info_no_os_or_range;
}
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->name = name;
- indio_dev->channels = st->chip_info->channels;
- indio_dev->num_channels = st->chip_info->num_channels;
- init_completion(&st->completion);
+ /* AXI ADC backend doesn't support single read. */
+ indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE;
+ indio_dev->name = chip_info->name;
+
+ /* Using spi-engine with offload support ? */
+ if (st->bops->offload_config) {
+ ret = st->bops->offload_config(dev, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad7606_probe_channels(indio_dev);
+ if (ret)
+ return ret;
ret = ad7606_reset(st);
if (ret)
@@ -641,59 +1579,99 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return -ERESTARTSYS;
}
- st->write_scale = ad7606_write_scale_hw;
- st->write_os = ad7606_write_os_hw;
+ /* If convst pin is not defined, setup PWM. */
+ if (!st->gpio_convst || st->offload_en) {
+ st->cnvst_pwm = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnvst_pwm))
+ return PTR_ERR(st->cnvst_pwm);
- if (st->bops->sw_mode_config)
- st->sw_mode_en = device_property_present(st->dev,
- "adi,sw-mode");
-
- if (st->sw_mode_en) {
- /* Scale of 0.076293 is only available in sw mode */
- st->scale_avail = ad7616_sw_scale_avail;
- st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+ /* The PWM is initialized at 1MHz to have a fast enough GPIO emulation. */
+ ret = ad7606_set_sampling_freq(st, 1 * MEGA);
+ if (ret)
+ return ret;
- /* After reset, in software mode, ±10 V is set by default */
- memset32(st->range, 2, ARRAY_SIZE(st->range));
- indio_dev->info = &ad7606_info_os_range_and_debug;
+ ret = ad7606_pwm_set_low(st);
+ if (ret)
+ return ret;
- ret = st->bops->sw_mode_config(indio_dev);
- if (ret < 0)
+ /*
+ * PWM is not disabled when sampling stops, but instead its duty cycle is set
+ * to 0% to be sure we have a "low" state. After we unload the driver, let's
+ * disable the PWM.
+ */
+ ret = devm_add_action_or_reset(dev, ad7606_pwm_disable,
+ st->cnvst_pwm);
+ if (ret)
return ret;
}
- st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
- indio_dev->name,
- iio_device_id(indio_dev));
- if (!st->trig)
- return -ENOMEM;
+ if (st->bops->iio_backend_config) {
+ /*
+ * If there is a backend, the PWM should not overpass the maximum sampling
+ * frequency the chip supports.
+ */
+ ret = ad7606_set_sampling_freq(st, chip_info->max_samplerate);
+ if (ret)
+ return ret;
- st->trig->ops = &ad7606_trigger_ops;
- iio_trigger_set_drvdata(st->trig, indio_dev);
- ret = devm_iio_trigger_register(dev, st->trig);
- if (ret)
- return ret;
+ ret = st->bops->iio_backend_config(dev, indio_dev);
+ if (ret)
+ return ret;
- indio_dev->trig = iio_trigger_get(st->trig);
+ indio_dev->setup_ops = &ad7606_backend_buffer_ops;
+ } else if (!st->offload_en) {
+ /* Reserve the PWM use only for backend (force gpio_convst definition) */
+ if (!st->gpio_convst)
+ return dev_err_probe(dev, -EINVAL,
+ "No backend, connect convst to a GPIO");
+
+ init_completion(&st->completion);
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7606_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
- ret = devm_request_threaded_irq(dev, irq,
- NULL,
- &ad7606_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- name, indio_dev);
- if (ret)
- return ret;
+ indio_dev->trig = iio_trigger_get(st->trig);
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- &iio_pollfunc_store_time,
- &ad7606_trigger_handler,
- &ad7606_buffer_ops);
- if (ret)
- return ret;
+ ret = devm_request_threaded_irq(dev, irq, NULL, &ad7606_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ chip_info->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7606_trigger_handler,
+ &ad7606_buffer_ops);
+ if (ret)
+ return ret;
+ }
+
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ /* Offload needs 1 DOUT line, applying this setting in sw_setup_cb. */
+ if (st->sw_mode_en || st->offload_en) {
+ indio_dev->info = &ad7606_info_sw_mode;
+ st->chip_info->sw_setup_cb(indio_dev);
+ }
+
+ if (st->sw_mode_en && st->chip_info->calib_gain_avail) {
+ ret = ad7606_set_gain_calib(st);
+ if (ret)
+ return ret;
+ }
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(ad7606_probe);
+EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606");
#ifdef CONFIG_PM_SLEEP
@@ -704,7 +1682,7 @@ static int ad7606_suspend(struct device *dev)
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, 1);
- gpiod_set_value(st->gpio_standby, 0);
+ gpiod_set_value(st->gpio_standby, 1);
}
return 0;
@@ -716,7 +1694,7 @@ static int ad7606_resume(struct device *dev)
struct ad7606_state *st = iio_priv(indio_dev);
if (st->gpio_standby) {
- gpiod_set_value(st->gpio_range, st->range[0]);
+ gpiod_set_value(st->gpio_range, st->chan_info[0].range);
gpiod_set_value(st->gpio_standby, 1);
ad7606_reset(st);
}
@@ -725,7 +1703,7 @@ static int ad7606_resume(struct device *dev)
}
SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
-EXPORT_SYMBOL_GPL(ad7606_pm_ops);
+EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, "IIO_AD7606");
#endif