summaryrefslogtreecommitdiffstats
path: root/drivers/iio/amplifiers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/iio/amplifiers
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/iio/amplifiers')
-rw-r--r--drivers/iio/amplifiers/Kconfig47
-rw-r--r--drivers/iio/amplifiers/Makefile9
-rw-r--r--drivers/iio/amplifiers/ad8366.c335
-rw-r--r--drivers/iio/amplifiers/ada4250.c403
-rw-r--r--drivers/iio/amplifiers/hmc425a.c244
5 files changed, 1038 insertions, 0 deletions
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
new file mode 100644
index 000000000..f217a2a1e
--- /dev/null
+++ b/drivers/iio/amplifiers/Kconfig
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Gain Amplifiers, etc.
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Amplifiers"
+
+config AD8366
+ tristate "Analog Devices AD8366 and similar Gain Amplifiers"
+ depends on SPI
+ depends on GPIOLIB
+ select BITREVERSE
+ help
+ Say yes here to build support for Analog Devices AD8366 and similar
+ gain amplifiers. This driver supports the following gain amplifiers
+ from Analog Devices:
+ AD8366 Dual-Digital Variable Gain Amplifier (VGA)
+ ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
+ ADL5240 Digitally controlled variable gain amplifier (VGA)
+ HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad8366.
+
+config ADA4250
+ tristate "Analog Devices ADA4250 Instrumentation Amplifier"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADA4250
+ SPI Amplifier's support. The driver provides direct access via
+ sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ada4250.
+
+config HMC425
+ tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices HMC425A and similar
+ gain amplifiers or step attenuators.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hmc425a.
+
+endmenu
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
new file mode 100644
index 000000000..212633112
--- /dev/null
+++ b/drivers/iio/amplifiers/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile iio/amplifiers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AD8366) += ad8366.o
+obj-$(CONFIG_ADA4250) += ada4250.o
+obj-$(CONFIG_HMC425) += hmc425a.o
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
new file mode 100644
index 000000000..f2c2ea79a
--- /dev/null
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD8366 and similar Gain Amplifiers
+ * This driver supports the following gain amplifiers:
+ * AD8366 Dual-Digital Variable Gain Amplifier (VGA)
+ * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
+ * ADL5240 Digitally controlled variable gain amplifier (VGA)
+ * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
+ *
+ * Copyright 2012-2019 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitrev.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+enum ad8366_type {
+ ID_AD8366,
+ ID_ADA4961,
+ ID_ADL5240,
+ ID_HMC1119,
+};
+
+struct ad8366_info {
+ int gain_min;
+ int gain_max;
+};
+
+struct ad8366_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ struct mutex lock; /* protect sensor state */
+ struct gpio_desc *reset_gpio;
+ unsigned char ch[2];
+ enum ad8366_type type;
+ struct ad8366_info *info;
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ unsigned char data[2] __aligned(IIO_DMA_MINALIGN);
+};
+
+static struct ad8366_info ad8366_infos[] = {
+ [ID_AD8366] = {
+ .gain_min = 4500,
+ .gain_max = 20500,
+ },
+ [ID_ADA4961] = {
+ .gain_min = -6000,
+ .gain_max = 15000,
+ },
+ [ID_ADL5240] = {
+ .gain_min = -11500,
+ .gain_max = 20000,
+ },
+ [ID_HMC1119] = {
+ .gain_min = -31750,
+ .gain_max = 0,
+ },
+};
+
+static int ad8366_write(struct iio_dev *indio_dev,
+ unsigned char ch_a, unsigned char ch_b)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (st->type) {
+ case ID_AD8366:
+ ch_a = bitrev8(ch_a & 0x3F);
+ ch_b = bitrev8(ch_b & 0x3F);
+
+ st->data[0] = ch_b >> 4;
+ st->data[1] = (ch_b << 4) | (ch_a >> 2);
+ break;
+ case ID_ADA4961:
+ st->data[0] = ch_a & 0x1F;
+ break;
+ case ID_ADL5240:
+ st->data[0] = (ch_a & 0x3F);
+ break;
+ case ID_HMC1119:
+ st->data[0] = ch_a;
+ break;
+ }
+
+ ret = spi_write(st->spi, st->data, indio_dev->num_channels);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "write failed (%d)", ret);
+
+ return ret;
+}
+
+static int ad8366_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ int ret;
+ int code, gain = 0;
+
+ mutex_lock(&st->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ code = st->ch[chan->channel];
+
+ switch (st->type) {
+ case ID_AD8366:
+ gain = code * 253 + 4500;
+ break;
+ case ID_ADA4961:
+ gain = 15000 - code * 1000;
+ break;
+ case ID_ADL5240:
+ gain = 20000 - 31500 + code * 500;
+ break;
+ case ID_HMC1119:
+ gain = -1 * code * 250;
+ break;
+ }
+
+ /* Values in dB */
+ *val = gain / 1000;
+ *val2 = (gain % 1000) * 1000;
+
+ ret = IIO_VAL_INT_PLUS_MICRO_DB;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+};
+
+static int ad8366_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ struct ad8366_info *inf = st->info;
+ int code = 0, gain;
+ int ret;
+
+ /* Values in dB */
+ if (val < 0)
+ gain = (val * 1000) - (val2 / 1000);
+ else
+ gain = (val * 1000) + (val2 / 1000);
+
+ if (gain > inf->gain_max || gain < inf->gain_min)
+ return -EINVAL;
+
+ switch (st->type) {
+ case ID_AD8366:
+ code = (gain - 4500) / 253;
+ break;
+ case ID_ADA4961:
+ code = (15000 - gain) / 1000;
+ break;
+ case ID_ADL5240:
+ code = ((gain - 500 - 20000) / 500) & 0x3F;
+ break;
+ case ID_HMC1119:
+ code = (abs(gain) / 250) & 0x7F;
+ break;
+ }
+
+ mutex_lock(&st->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ st->ch[chan->channel] = code;
+ ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad8366_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ return IIO_VAL_INT_PLUS_MICRO_DB;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad8366_info = {
+ .read_raw = &ad8366_read_raw,
+ .write_raw = &ad8366_write_raw,
+ .write_raw_get_fmt = &ad8366_write_raw_get_fmt,
+};
+
+#define AD8366_CHAN(_channel) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\
+}
+
+static const struct iio_chan_spec ad8366_channels[] = {
+ AD8366_CHAN(0),
+ AD8366_CHAN(1),
+};
+
+static const struct iio_chan_spec ada4961_channels[] = {
+ AD8366_CHAN(0),
+};
+
+static int ad8366_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad8366_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ mutex_init(&st->lock);
+ st->spi = spi;
+ st->type = spi_get_device_id(spi)->driver_data;
+
+ switch (st->type) {
+ case ID_AD8366:
+ indio_dev->channels = ad8366_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
+ break;
+ case ID_ADA4961:
+ case ID_ADL5240:
+ case ID_HMC1119:
+ st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(st->reset_gpio)) {
+ ret = PTR_ERR(st->reset_gpio);
+ goto error_disable_reg;
+ }
+ indio_dev->channels = ada4961_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ada4961_channels);
+ break;
+ default:
+ dev_err(&spi->dev, "Invalid device ID\n");
+ ret = -EINVAL;
+ goto error_disable_reg;
+ }
+
+ st->info = &ad8366_infos[st->type];
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad8366_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad8366_write(indio_dev, 0 , 0);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static void ad8366_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad8366_state *st = iio_priv(indio_dev);
+ struct regulator *reg = st->reg;
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+}
+
+static const struct spi_device_id ad8366_id[] = {
+ {"ad8366", ID_AD8366},
+ {"ada4961", ID_ADA4961},
+ {"adl5240", ID_ADL5240},
+ {"hmc1119", ID_HMC1119},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad8366_id);
+
+static struct spi_driver ad8366_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = ad8366_probe,
+ .remove = ad8366_remove,
+ .id_table = ad8366_id,
+};
+
+module_spi_driver(ad8366_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD8366 and similar Gain Amplifiers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c
new file mode 100644
index 000000000..4b32d350d
--- /dev/null
+++ b/drivers/iio/amplifiers/ada4250.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADA4250 driver
+ *
+ * Copyright 2022 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <asm/unaligned.h>
+
+/* ADA4250 Register Map */
+#define ADA4250_REG_GAIN_MUX 0x00
+#define ADA4250_REG_REFBUF_EN 0x01
+#define ADA4250_REG_RESET 0x02
+#define ADA4250_REG_SNSR_CAL_VAL 0x04
+#define ADA4250_REG_SNSR_CAL_CNFG 0x05
+#define ADA4250_REG_DIE_REV 0x18
+#define ADA4250_REG_CHIP_ID 0x19
+
+/* ADA4250_REG_GAIN_MUX Map */
+#define ADA4250_GAIN_MUX_MSK GENMASK(2, 0)
+
+/* ADA4250_REG_REFBUF Map */
+#define ADA4250_REFBUF_MSK BIT(0)
+
+/* ADA4250_REG_RESET Map */
+#define ADA4250_RESET_MSK BIT(0)
+
+/* ADA4250_REG_SNSR_CAL_VAL Map */
+#define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0)
+
+/* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */
+#define ADA4250_BIAS_SET_MSK GENMASK(3, 2)
+#define ADA4250_RANGE_SET_MSK GENMASK(1, 0)
+
+/* Miscellaneous definitions */
+#define ADA4250_CHIP_ID 0x4250
+#define ADA4250_RANGE1 0
+#define ADA4250_RANGE4 3
+
+/* ADA4250 current bias set */
+enum ada4250_current_bias {
+ ADA4250_BIAS_DISABLED,
+ ADA4250_BIAS_BANDGAP,
+ ADA4250_BIAS_AVDD,
+};
+
+struct ada4250_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator *reg;
+ /* Protect against concurrent accesses to the device and data content */
+ struct mutex lock;
+ u8 bias;
+ u8 gain;
+ int offset_uv;
+ bool refbuf_en;
+};
+
+/* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */
+static const int calibbias_table[] = {0, 1, 2};
+
+/* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */
+static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+static const struct regmap_config ada4250_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+ .max_register = 0x1A,
+};
+
+static int ada4250_set_offset_uv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int offset_uv)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+
+ int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0;
+ u8 offset_raw, range = ADA4250_RANGE1;
+ u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599};
+
+ if (st->bias == 0 || st->bias == 3)
+ return -EINVAL;
+
+ voltage_v = regulator_get_voltage(st->reg);
+ voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000);
+
+ if (st->bias == ADA4250_BIAS_AVDD)
+ x[0] = voltage_v;
+ else
+ x[0] = 5;
+
+ x[1] = 126 * (x[0] - 1);
+
+ for (i = 0; i < 6; i++)
+ x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]);
+
+ if (st->gain == 0)
+ return -EINVAL;
+
+ /*
+ * Compute Range and Voltage per LSB for the Sensor Offset Calibration
+ * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD):
+ * Range 1 Range 2
+ * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) |
+ * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 |
+ * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 |
+ * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 |
+ * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 |
+ * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 |
+ * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 |
+ * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 |
+ */
+ for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) {
+ max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1);
+ min_vos = -1 * max_vos;
+ if (offset_uv > min_vos && offset_uv < max_vos) {
+ range = i;
+ vlsb = x[st->gain] * ((1 << (i + 1)) - 1);
+ break;
+ }
+ }
+
+ if (vlsb <= 0)
+ return -EINVAL;
+
+ offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb);
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG,
+ ADA4250_RANGE_SET_MSK,
+ FIELD_PREP(ADA4250_RANGE_SET_MSK, range));
+ if (ret)
+ goto exit;
+
+ st->offset_uv = offset_raw * vlsb;
+
+ /*
+ * To set the offset calibration value, use bits [6:0] and bit 7 as the
+ * polarity bit (set to "0" for a negative offset and "1" for a positive
+ * offset).
+ */
+ if (offset_uv < 0) {
+ offset_raw |= BIT(7);
+ st->offset_uv *= (-1);
+ }
+
+ ret = regmap_write(st->regmap, ADA4250_REG_SNSR_CAL_VAL, offset_raw);
+
+exit:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ada4250_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = regmap_read(st->regmap, ADA4250_REG_GAIN_MUX, val);
+ if (ret)
+ return ret;
+
+ *val = BIT(*val);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = st->offset_uv;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ *val2 = 1000000;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = regmap_write(st->regmap, ADA4250_REG_GAIN_MUX,
+ FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val)));
+ if (ret)
+ return ret;
+
+ st->gain = ilog2(val);
+
+ return ret;
+ case IIO_CHAN_INFO_OFFSET:
+ return ada4250_set_offset_uv(indio_dev, chan, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG,
+ ADA4250_BIAS_SET_MSK,
+ FIELD_PREP(ADA4250_BIAS_SET_MSK, val));
+ if (ret)
+ return ret;
+
+ st->bias = val;
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = calibbias_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(calibbias_table);
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *vals = hwgain_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(hwgain_table);
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int write_val,
+ unsigned int *read_val)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+
+ if (read_val)
+ return regmap_read(st->regmap, reg, read_val);
+ else
+ return regmap_write(st->regmap, reg, write_val);
+}
+
+static const struct iio_info ada4250_info = {
+ .read_raw = ada4250_read_raw,
+ .write_raw = ada4250_write_raw,
+ .read_avail = &ada4250_read_avail,
+ .debugfs_reg_access = &ada4250_reg_access,
+};
+
+static const struct iio_chan_spec ada4250_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ }
+};
+
+static void ada4250_reg_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static int ada4250_init(struct ada4250_state *st)
+{
+ int ret;
+ u16 chip_id;
+ u8 data[2] __aligned(8) = {};
+ struct spi_device *spi = st->spi;
+
+ st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable");
+
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
+ "failed to get the AVDD voltage\n");
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADA4250_REG_RESET,
+ FIELD_PREP(ADA4250_RESET_MSK, 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2);
+ if (ret)
+ return ret;
+
+ chip_id = get_unaligned_le16(data);
+
+ if (chip_id != ADA4250_CHIP_ID) {
+ dev_err(&spi->dev, "Invalid chip ID.\n");
+ return -EINVAL;
+ }
+
+ return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN,
+ FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en));
+}
+
+static int ada4250_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ struct ada4250_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ st = iio_priv(indio_dev);
+ st->regmap = regmap;
+ st->spi = spi;
+
+ indio_dev->info = &ada4250_info;
+ indio_dev->name = "ada4250";
+ indio_dev->channels = ada4250_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ada4250_channels);
+
+ mutex_init(&st->lock);
+
+ ret = ada4250_init(st);
+ if (ret) {
+ dev_err(&spi->dev, "ADA4250 init failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ada4250_id[] = {
+ { "ada4250", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ada4250_id);
+
+static const struct of_device_id ada4250_of_match[] = {
+ { .compatible = "adi,ada4250" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ada4250_of_match);
+
+static struct spi_driver ada4250_driver = {
+ .driver = {
+ .name = "ada4250",
+ .of_match_table = ada4250_of_match,
+ },
+ .probe = ada4250_probe,
+ .id_table = ada4250_id,
+};
+module_spi_driver(ada4250_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
+MODULE_DESCRIPTION("Analog Devices ADA4250");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c
new file mode 100644
index 000000000..ce80e0c91
--- /dev/null
+++ b/drivers/iio/amplifiers/hmc425a.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMC425A and similar Gain Amplifiers
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+
+enum hmc425a_type {
+ ID_HMC425A,
+};
+
+struct hmc425a_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int num_gpios;
+ int gain_min;
+ int gain_max;
+ int default_gain;
+};
+
+struct hmc425a_state {
+ struct regulator *reg;
+ struct mutex lock; /* protect sensor state */
+ struct hmc425a_chip_info *chip_info;
+ struct gpio_descs *gpios;
+ enum hmc425a_type type;
+ u32 gain;
+};
+
+static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
+{
+ struct hmc425a_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(values, BITS_PER_TYPE(value));
+
+ values[0] = value;
+
+ gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
+ NULL, values);
+ return 0;
+}
+
+static int hmc425a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long m)
+{
+ struct hmc425a_state *st = iio_priv(indio_dev);
+ int code, gain = 0;
+ int ret;
+
+ mutex_lock(&st->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ code = st->gain;
+
+ switch (st->type) {
+ case ID_HMC425A:
+ gain = ~code * -500;
+ break;
+ }
+
+ *val = gain / 1000;
+ *val2 = (gain % 1000) * 1000;
+
+ ret = IIO_VAL_INT_PLUS_MICRO_DB;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+};
+
+static int hmc425a_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct hmc425a_state *st = iio_priv(indio_dev);
+ struct hmc425a_chip_info *inf = st->chip_info;
+ int code = 0, gain;
+ int ret;
+
+ if (val < 0)
+ gain = (val * 1000) - (val2 / 1000);
+ else
+ gain = (val * 1000) + (val2 / 1000);
+
+ if (gain > inf->gain_max || gain < inf->gain_min)
+ return -EINVAL;
+
+ switch (st->type) {
+ case ID_HMC425A:
+ code = ~((abs(gain) / 500) & 0x3F);
+ break;
+ }
+
+ mutex_lock(&st->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ st->gain = code;
+
+ ret = hmc425a_write(indio_dev, st->gain);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ return IIO_VAL_INT_PLUS_MICRO_DB;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info hmc425a_info = {
+ .read_raw = &hmc425a_read_raw,
+ .write_raw = &hmc425a_write_raw,
+ .write_raw_get_fmt = &hmc425a_write_raw_get_fmt,
+};
+
+#define HMC425A_CHAN(_channel) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+}
+
+static const struct iio_chan_spec hmc425a_channels[] = {
+ HMC425A_CHAN(0),
+};
+
+/* Match table for of_platform binding */
+static const struct of_device_id hmc425a_of_match[] = {
+ { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hmc425a_of_match);
+
+static void hmc425a_reg_disable(void *data)
+{
+ struct hmc425a_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
+ [ID_HMC425A] = {
+ .name = "hmc425a",
+ .channels = hmc425a_channels,
+ .num_channels = ARRAY_SIZE(hmc425a_channels),
+ .num_gpios = 6,
+ .gain_min = -31500,
+ .gain_max = 0,
+ .default_gain = -0x40, /* set default gain -31.5db*/
+ },
+};
+
+static int hmc425a_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct hmc425a_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->type = (uintptr_t)device_get_match_data(&pdev->dev);
+
+ st->chip_info = &hmc425a_chip_info_tbl[st->type];
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->name = st->chip_info->name;
+ st->gain = st->chip_info->default_gain;
+
+ st->gpios = devm_gpiod_get_array(&pdev->dev, "ctrl", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpios))
+ return dev_err_probe(&pdev->dev, PTR_ERR(st->gpios),
+ "failed to get gpios\n");
+
+ if (st->gpios->ndescs != st->chip_info->num_gpios) {
+ dev_err(&pdev->dev, "%d GPIOs needed to operate\n",
+ st->chip_info->num_gpios);
+ return -ENODEV;
+ }
+
+ st->reg = devm_regulator_get(&pdev->dev, "vcc-supply");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(&pdev->dev, hmc425a_reg_disable, st);
+ if (ret)
+ return ret;
+
+ mutex_init(&st->lock);
+
+ indio_dev->info = &hmc425a_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver hmc425a_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = hmc425a_of_match,
+ },
+ .probe = hmc425a_probe,
+};
+module_platform_driver(hmc425a_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices HMC425A and similar GPIO control Gain Amplifiers");
+MODULE_LICENSE("GPL v2");