@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -84,6 +85,9 @@
#define AD4170_AFE_BIPOLAR_MSK BIT(4)
#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0)
+/* AD4170_FILTER_REG */
+#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0)
+
/* AD4170 register constants */
/* AD4170_CHAN_MAP_REG constants */
@@ -109,6 +113,11 @@
#define AD4170_ADC_CTRL_MODE_SINGLE 0x4
#define AD4170_ADC_CTRL_MODE_IDLE 0x7
+/* AD4170_FILTER_REG constants */
+#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0
+#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4
+#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6
+
/* Device properties and auxiliary constants */
#define AD4170_NUM_ANALOG_PINS 9
@@ -117,6 +126,7 @@
#define AD4170_MAX_SETUPS 8
#define AD4170_INVALID_SETUP 9
#define AD4170_SPI_MAX_XFER_LEN 6
+#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ)
#define AD4170_INT_REF_2_5V 2500000
@@ -125,6 +135,12 @@
#define AD4170_NUM_PGA_OPTIONS 10
+/* Digital filter properties */
+#define AD4170_SINC3_MIN_FS 4
+#define AD4170_SINC3_MAX_FS 65532
+#define AD4170_SINC5_MIN_FS 1
+#define AD4170_SINC5_MAX_FS 256
+
#define AD4170_GAIN_REG_DEFAULT 0x555555
/* Analog pin functions */
@@ -181,6 +197,12 @@ enum ad4170_ref_select {
AD4170_REF_AVDD
};
+enum ad4170_filter_type {
+ AD4170_SINC5_AVG,
+ AD4170_SINC5,
+ AD4170_SINC3,
+};
+
enum ad4170_regulator {
AD4170_AVDD_SUP,
AD4170_AVSS_SUP,
@@ -202,6 +224,18 @@ static const char * const ad4170_int_pin_names[] = {
[AD4170_INT_PIN_DIG_AUX1] = "dig_aux1",
};
+static const unsigned int ad4170_sinc3_filt_fs_tbl[] = {
+ 4, 8, 12, 16, 20, 40, 48, 80, /* 0 - 7 */
+ 100, 256, 500, 1000, 5000, 8332, 10000, 25000, /* 8 - 15 */
+ 50000, 65532, /* 16 - 17 */
+};
+
+#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)
+
+static const unsigned int ad4170_sinc5_filt_fs_tbl[] = {
+ 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256,
+};
+
struct ad4170_chip_info {
const char *name;
};
@@ -259,6 +293,12 @@ struct ad4170_chan_info {
bool enabled;
};
+static const char * const ad4170_filt_names[] = {
+ [AD4170_SINC5_AVG] = "sinc5+avg",
+ [AD4170_SINC5] = "sinc5",
+ [AD4170_SINC3] = "sinc3",
+};
+
struct ad4170_state {
struct mutex lock; /* Protect read-modify-write and multi write sequences */
int vrefs_uv[AD4170_MAX_SUP];
@@ -268,6 +308,7 @@ struct ad4170_state {
struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS];
struct spi_device *spi;
struct regmap *regmap;
+ int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2];
int pins_fn[AD4170_NUM_ANALOG_PINS];
u32 int_pin_sel;
struct completion completion;
@@ -279,6 +320,38 @@ struct ad4170_state {
u8 rx_buf[4];
};
+static void ad4170_fill_sps_tbl(struct ad4170_state *st)
+{
+ unsigned int tmp0, tmp1, i;
+
+ /*
+ * The ODR can be calculated the same way for sinc5+avg, sinc5, and
+ * sinc3 filter types with the exception that sinc5 filter has a
+ * narrowed range of allowed FILTER_FS values.
+ */
+ for (i = 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) {
+ tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i],
+ &tmp1);
+ tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]);
+ /* Fill sinc5+avg filter SPS table */
+ st->sps_tbl[AD4170_SINC5_AVG][i][0] = tmp0; /* Integer part */
+ st->sps_tbl[AD4170_SINC5_AVG][i][1] = tmp1; /* Fractional part */
+
+ /* Fill sinc3 filter SPS table */
+ st->sps_tbl[AD4170_SINC3][i][0] = tmp0; /* Integer part */
+ st->sps_tbl[AD4170_SINC3][i][1] = tmp1; /* Fractional part */
+ }
+ /* Sinc5 filter ODR doesn't use all FILTER_FS bits */
+ for (i = 0; i < ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); i++) {
+ tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i],
+ &tmp1);
+ tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_filt_fs_tbl[i]);
+ /* Fill sinc5 filter SPS table */
+ st->sps_tbl[AD4170_SINC5][i][0] = tmp0; /* Integer part */
+ st->sps_tbl[AD4170_SINC5][i][1] = tmp1; /* Fractional part */
+ }
+}
+
static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int reg, unsigned int writeval,
unsigned int *readval)
@@ -636,6 +709,105 @@ static int ad4170_set_channel_enable(struct ad4170_state *st,
return 0;
}
+static int __ad4170_get_filter_type(unsigned int filter)
+{
+ u16 f_conf = FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter);
+
+ switch (f_conf) {
+ case AD4170_FILTER_FILTER_TYPE_SINC5_AVG:
+ return AD4170_SINC5_AVG;
+ case AD4170_FILTER_FILTER_TYPE_SINC5:
+ return AD4170_SINC5;
+ case AD4170_FILTER_FILTER_TYPE_SINC3:
+ return AD4170_SINC3;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4170_set_filter_type(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ unsigned int val)
+{
+ struct ad4170_state *st = iio_priv(indio_dev);
+ struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
+ struct ad4170_setup *setup = &chan_info->setup;
+ unsigned int old_filter_fs, old_filter, filter_type_conf;
+ int ret;
+
+ switch (val) {
+ case AD4170_SINC5_AVG:
+ filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5_AVG;
+ break;
+ case AD4170_SINC5:
+ filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5;
+ break;
+ case AD4170_SINC3:
+ filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ guard(mutex)(&st->lock);
+ /*
+ * The filters provide the same ODR for a given filter_fs value but
+ * there are different minimum and maximum filter_fs limits for each
+ * filter. The filter_fs value will be adjusted if the current filter_fs
+ * is out of the limits of the just requested filter. Since the
+ * filter_fs value affects the ODR (sampling_frequency), changing the
+ * filter may lead to a change in the sampling frequency.
+ */
+ old_filter = setup->filter;
+ old_filter_fs = setup->filter_fs;
+ if (val == AD4170_SINC5_AVG || val == AD4170_SINC3)
+ setup->filter_fs = clamp(val, AD4170_SINC3_MIN_FS,
+ AD4170_SINC3_MAX_FS);
+ else
+ setup->filter_fs = clamp(val, AD4170_SINC5_MIN_FS,
+ AD4170_SINC5_MAX_FS);
+
+ setup->filter &= ~AD4170_FILTER_FILTER_TYPE_MSK;
+ setup->filter |= FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK,
+ filter_type_conf);
+
+ ret = ad4170_write_channel_setup(st, chan->address, false);
+ if (ret) {
+ setup->filter = old_filter;
+ setup->filter_fs = old_filter_fs;
+ }
+
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int ad4170_get_filter_type(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct ad4170_state *st = iio_priv(indio_dev);
+ struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
+ struct ad4170_setup *setup = &chan_info->setup;
+
+ return __ad4170_get_filter_type(setup->filter);
+}
+
+static const struct iio_enum ad4170_filter_type_enum = {
+ .items = ad4170_filt_names,
+ .num_items = ARRAY_SIZE(ad4170_filt_names),
+ .get = ad4170_get_filter_type,
+ .set = ad4170_set_filter_type,
+};
+
+static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
+ &ad4170_filter_type_enum),
+ { }
+};
+
static const struct iio_chan_spec ad4170_channel_template = {
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -644,8 +816,11 @@ static const struct iio_chan_spec ad4170_channel_template = {
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_CALIBBIAS) |
- BIT(IIO_CHAN_INFO_CALIBSCALE),
- .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = ad4170_filter_type_ext_info,
.scan_type = {
.realbits = 24,
.storagebits = 32,
@@ -935,7 +1110,8 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,
struct ad4170_state *st = iio_priv(indio_dev);
struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
struct ad4170_setup *setup = &chan_info->setup;
- unsigned int pga;
+ enum ad4170_filter_type f_type;
+ unsigned int pga, fs_idx;
int ret;
switch (info) {
@@ -955,6 +1131,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,
pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
*val = chan_info->offset_tbl[pga];
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ f_type = __ad4170_get_filter_type(setup->filter);
+ switch (f_type) {
+ case AD4170_SINC5_AVG:
+ case AD4170_SINC3:
+ fs_idx = find_closest(setup->filter_fs,
+ ad4170_sinc3_filt_fs_tbl,
+ ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl));
+ *val = st->sps_tbl[f_type][fs_idx][0];
+ *val2 = st->sps_tbl[f_type][fs_idx][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case AD4170_SINC5:
+ fs_idx = find_closest(setup->filter_fs,
+ ad4170_sinc5_filt_fs_tbl,
+ ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl));
+ *val = st->sps_tbl[f_type][fs_idx][0];
+ *val2 = st->sps_tbl[f_type][fs_idx][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_CALIBBIAS:
*val = setup->offset;
return IIO_VAL_INT;
@@ -1040,6 +1237,7 @@ static int ad4170_read_avail(struct iio_dev *indio_dev,
{
struct ad4170_state *st = iio_priv(indio_dev);
struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
+ enum ad4170_filter_type f_type;
switch (info) {
case IIO_CHAN_INFO_SCALE:
@@ -1047,6 +1245,21 @@ static int ad4170_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(chan_info->scale_tbl) * 2;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ f_type = ad4170_get_filter_type(indio_dev, chan);
+ *vals = (int *)st->sps_tbl[f_type];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ switch (f_type) {
+ case AD4170_SINC5_AVG:
+ case AD4170_SINC3:
+ *length = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2;
+ return IIO_AVAIL_LIST;
+ case AD4170_SINC5:
+ *length = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1087,6 +1300,48 @@ static int ad4170_set_pga(struct ad4170_state *st,
return 0;
}
+static int ad4170_set_channel_freq(struct ad4170_state *st,
+ struct iio_chan_spec const *chan, int val,
+ int val2)
+{
+ struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
+ struct ad4170_setup *setup = &chan_info->setup;
+ enum ad4170_filter_type f_type = __ad4170_get_filter_type(setup->filter);
+ unsigned int old_filter_fs, i;
+ int filt_fs_tbl_size, ret;
+
+ switch (f_type) {
+ case AD4170_SINC5_AVG:
+ case AD4170_SINC3:
+ filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl);
+ break;
+ case AD4170_SINC5:
+ filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl);
+ break;
+ }
+
+ for (i = 0; i < filt_fs_tbl_size; i++) {
+ if (st->sps_tbl[f_type][i][0] == val &&
+ st->sps_tbl[f_type][i][1] == val2)
+ break;
+ }
+ if (i == filt_fs_tbl_size)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ old_filter_fs = setup->filter_fs;
+ if (f_type == AD4170_SINC5)
+ setup->filter_fs = ad4170_sinc5_filt_fs_tbl[i];
+ else
+ setup->filter_fs = ad4170_sinc3_filt_fs_tbl[i];
+
+ ret = ad4170_write_channel_setup(st, chan->address, false);
+ if (ret)
+ setup->filter_fs = old_filter_fs;
+
+ return ret;
+}
+
static int ad4170_set_calib_offset(struct ad4170_state *st,
struct iio_chan_spec const *chan, int val)
{
@@ -1134,6 +1389,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_SCALE:
return ad4170_set_pga(st, chan, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad4170_set_channel_freq(st, chan, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return ad4170_set_calib_offset(st, chan, val);
case IIO_CHAN_INFO_CALIBSCALE:
@@ -1164,6 +1421,8 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
case IIO_CHAN_INFO_CALIBSCALE:
return IIO_VAL_INT;
@@ -1398,6 +1657,8 @@ static int ad4170_initial_config(struct iio_dev *indio_dev)
unsigned int i;
int ret;
+ ad4170_fill_sps_tbl(st);
+
ret = ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE);
if (ret)
return dev_err_probe(dev, ret,
@@ -1427,6 +1688,12 @@ static int ad4170_initial_config(struct iio_dev *indio_dev)
return dev_err_probe(dev, ret,
"Failed to write CHAN_MAP_REG\n");
+ ret = ad4170_set_channel_freq(st, chan,
+ AD4170_DEFAULT_SAMP_RATE, 0);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set channel freq\n");
+
ret = ad4170_fill_scale_tbl(indio_dev, chan);
if (ret)
return dev_err_probe(dev, ret,
Add support for sinc3, sinc5, and averaged sinc5 digital filters along with sample frequency configuration. Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com> --- Change log v2 -> v3 - New patch spun out of the base driver patch. drivers/iio/adc/ad4170.c | 273 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 3 deletions(-)