iio: adc: ad7768-1: add regulator to control VCM output

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>
This commit is contained in:
Jonathan Santos 2025-06-11 08:50:44 -03:00 committed by Jonathan Cameron
parent 1905e6c9ce
commit 96b6e814af
2 changed files with 160 additions and 0 deletions

View 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

View File

@ -13,9 +13,11 @@
#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>
@ -82,6 +84,12 @@
#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");