]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
iio: adc: ad7768-1: add regulator to control VCM output
authorJonathan Santos <Jonathan.Santos@analog.com>
Wed, 11 Jun 2025 11:50:44 +0000 (08:50 -0300)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Thu, 26 Jun 2025 18:32:54 +0000 (19:32 +0100)
The VCM output voltage can be used as a common-mode voltage within the
amplifier preconditioning circuits external to the AD7768-1.

This change allows the user to configure VCM output using the regulator
framework.

Acked-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
Reviewed-by: David Lechner <dlechner@baylibre.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
Link: https://patch.msgid.link/1f02312fdc4131168b194d59f4b1688dc68ea36e.1749569957.git.Jonathan.Santos@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/Kconfig
drivers/iio/adc/ad7768-1.c

index 799437f918df8bd7d7c4565484644e96a1abe9ae..7ad58ca5173fea7aad5fe0770a75f71e4150921d 100644 (file)
@@ -354,6 +354,7 @@ config AD7766
 config AD7768_1
        tristate "Analog Devices AD7768-1 ADC driver"
        depends on SPI
+       select REGULATOR
        select REGMAP_SPI
        select IIO_BUFFER
        select IIO_TRIGGER
index 8b414a1028644edf1333e8720a4d3e860e5166d9..68f876e5d31c598a42a0a95c3028fcf947ebfa12 100644 (file)
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
+#include <linux/minmax.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
 #include <linux/sysfs.h>
 #include <linux/spi/spi.h>
 
 #define AD7768_CONV_MODE_MSK           GENMASK(2, 0)
 #define AD7768_CONV_MODE(x)            FIELD_PREP(AD7768_CONV_MODE_MSK, x)
 
+/* AD7768_REG_ANALOG2 */
+#define AD7768_REG_ANALOG2_VCM_MSK     GENMASK(2, 0)
+#define AD7768_REG_ANALOG2_VCM(x)      FIELD_PREP(AD7768_REG_ANALOG2_VCM_MSK, (x))
+
+#define AD7768_VCM_OFF                 0x07
+
 enum ad7768_conv_mode {
        AD7768_CONTINUOUS,
        AD7768_ONE_SHOT,
@@ -159,6 +167,8 @@ struct ad7768_state {
        struct regmap *regmap;
        struct regmap *regmap24;
        struct regulator *vref;
+       struct regulator_dev *vcm_rdev;
+       unsigned int vcm_output_sel;
        struct clk *mclk;
        unsigned int mclk_freq;
        unsigned int samp_freq;
@@ -662,6 +672,150 @@ static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev)
                                               &ad7768_buffer_ops);
 }
 
+static int ad7768_vcm_enable(struct regulator_dev *rdev)
+{
+       struct iio_dev *indio_dev = rdev_get_drvdata(rdev);
+       struct ad7768_state *st = iio_priv(indio_dev);
+       int ret, regval;
+
+       if (!iio_device_claim_direct(indio_dev))
+               return -EBUSY;
+
+       /* To enable, set the last selected output */
+       regval = AD7768_REG_ANALOG2_VCM(st->vcm_output_sel + 1);
+       ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2,
+                                AD7768_REG_ANALOG2_VCM_MSK, regval);
+       iio_device_release_direct(indio_dev);
+
+       return ret;
+}
+
+static int ad7768_vcm_disable(struct regulator_dev *rdev)
+{
+       struct iio_dev *indio_dev = rdev_get_drvdata(rdev);
+       struct ad7768_state *st = iio_priv(indio_dev);
+       int ret;
+
+       if (!iio_device_claim_direct(indio_dev))
+               return -EBUSY;
+
+       ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2,
+                                AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF);
+       iio_device_release_direct(indio_dev);
+
+       return ret;
+}
+
+static int ad7768_vcm_is_enabled(struct regulator_dev *rdev)
+{
+       struct iio_dev *indio_dev = rdev_get_drvdata(rdev);
+       struct ad7768_state *st = iio_priv(indio_dev);
+       int ret, val;
+
+       if (!iio_device_claim_direct(indio_dev))
+               return -EBUSY;
+
+       ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val);
+       iio_device_release_direct(indio_dev);
+       if (ret)
+               return ret;
+
+       return FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val) != AD7768_VCM_OFF;
+}
+
+static int ad7768_set_voltage_sel(struct regulator_dev *rdev,
+                                 unsigned int selector)
+{
+       unsigned int regval = AD7768_REG_ANALOG2_VCM(selector + 1);
+       struct iio_dev *indio_dev = rdev_get_drvdata(rdev);
+       struct ad7768_state *st = iio_priv(indio_dev);
+       int ret;
+
+       if (!iio_device_claim_direct(indio_dev))
+               return -EBUSY;
+
+       ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2,
+                                AD7768_REG_ANALOG2_VCM_MSK, regval);
+       iio_device_release_direct(indio_dev);
+       if (ret)
+               return ret;
+
+       st->vcm_output_sel = selector;
+
+       return 0;
+}
+
+static int ad7768_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct iio_dev *indio_dev = rdev_get_drvdata(rdev);
+       struct ad7768_state *st = iio_priv(indio_dev);
+       int ret, val;
+
+       if (!iio_device_claim_direct(indio_dev))
+               return -EBUSY;
+
+       ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val);
+       iio_device_release_direct(indio_dev);
+       if (ret)
+               return ret;
+
+       val = FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val);
+
+       return clamp(val, 1, rdev->desc->n_voltages) - 1;
+}
+
+static const struct regulator_ops vcm_regulator_ops = {
+       .enable = ad7768_vcm_enable,
+       .disable = ad7768_vcm_disable,
+       .is_enabled = ad7768_vcm_is_enabled,
+       .list_voltage = regulator_list_voltage_table,
+       .set_voltage_sel = ad7768_set_voltage_sel,
+       .get_voltage_sel = ad7768_get_voltage_sel,
+};
+
+static const unsigned int vcm_voltage_table[] = {
+       2500000,
+       2050000,
+       1650000,
+       1900000,
+       1100000,
+       900000,
+};
+
+static const struct regulator_desc vcm_desc = {
+       .name = "ad7768-1-vcm",
+       .of_match = "vcm-output",
+       .regulators_node = "regulators",
+       .n_voltages = ARRAY_SIZE(vcm_voltage_table),
+       .volt_table = vcm_voltage_table,
+       .ops = &vcm_regulator_ops,
+       .type = REGULATOR_VOLTAGE,
+       .owner = THIS_MODULE,
+};
+
+static int ad7768_register_regulators(struct device *dev, struct ad7768_state *st,
+                                     struct iio_dev *indio_dev)
+{
+       struct regulator_config config = {
+               .dev = dev,
+               .driver_data = indio_dev,
+       };
+       int ret;
+
+       /* Disable the regulator before registering it */
+       ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2,
+                                AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF);
+       if (ret)
+               return ret;
+
+       st->vcm_rdev = devm_regulator_register(dev, &vcm_desc, &config);
+       if (IS_ERR(st->vcm_rdev))
+               return dev_err_probe(dev, PTR_ERR(st->vcm_rdev),
+                                    "failed to register VCM regulator\n");
+
+       return 0;
+}
+
 static int ad7768_probe(struct spi_device *spi)
 {
        struct ad7768_state *st;
@@ -726,6 +880,11 @@ static int ad7768_probe(struct spi_device *spi)
        indio_dev->info = &ad7768_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
 
+       /* Register VCM output regulator */
+       ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
+       if (ret)
+               return ret;
+
        ret = ad7768_setup(st);
        if (ret < 0) {
                dev_err(&spi->dev, "AD7768 setup failed\n");