mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 09:31:50 +00:00
7bf50a968a
Not all of the parts supported by this driver use single supply. Hence we add chip_info fields to say what additional supplies exist and in the case of vref, ensure that is used for the reference voltage rather than vcc. One corner case is the ad7091r which has an internal reference that can be over-driven by an external reference connected on the vref pin. To handle that force_ext_vref is introduced and set if an optional vref regulator is present. Tested using really simple QEMU model and some fixed regulators. The devm_add_action_or_reset() callback is changed to take the regulator as it's parameter so we can use one callback for all the different regulators without having to store pointers to them in the iio_priv() structure. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Cc: Michael Hennerich <michael.hennerich@analog.com> Reported-by: kernel test robot <lkp@intel.com> Reviewed-by: Lars-Peter Clausen <lars@metafoo.de> Link: https://lore.kernel.org/r/20210425163154.73209-2-jic23@kernel.org
468 lines
11 KiB
C
468 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
|
|
* TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver
|
|
*
|
|
* Copyright 2010 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/bitops.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
|
|
struct ad7476_state;
|
|
|
|
struct ad7476_chip_info {
|
|
unsigned int int_vref_uv;
|
|
struct iio_chan_spec channel[2];
|
|
/* channels used when convst gpio is defined */
|
|
struct iio_chan_spec convst_channel[2];
|
|
void (*reset)(struct ad7476_state *);
|
|
bool has_vref;
|
|
bool has_vdrive;
|
|
};
|
|
|
|
struct ad7476_state {
|
|
struct spi_device *spi;
|
|
const struct ad7476_chip_info *chip_info;
|
|
struct regulator *ref_reg;
|
|
struct gpio_desc *convst_gpio;
|
|
struct spi_transfer xfer;
|
|
struct spi_message msg;
|
|
/*
|
|
* DMA (thus cache coherency maintenance) requires the
|
|
* transfer buffers to live in their own cache lines.
|
|
* Make the buffer large enough for one 16 bit sample and one 64 bit
|
|
* aligned 64 bit timestamp.
|
|
*/
|
|
unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
|
|
____cacheline_aligned;
|
|
};
|
|
|
|
enum ad7476_supported_device_ids {
|
|
ID_AD7091,
|
|
ID_AD7091R,
|
|
ID_AD7273,
|
|
ID_AD7274,
|
|
ID_AD7276,
|
|
ID_AD7277,
|
|
ID_AD7278,
|
|
ID_AD7466,
|
|
ID_AD7467,
|
|
ID_AD7468,
|
|
ID_AD7475,
|
|
ID_AD7495,
|
|
ID_AD7940,
|
|
ID_ADC081S,
|
|
ID_ADC101S,
|
|
ID_ADC121S,
|
|
ID_ADS7866,
|
|
ID_ADS7867,
|
|
ID_ADS7868,
|
|
ID_LTC2314_14,
|
|
};
|
|
|
|
static void ad7091_convst(struct ad7476_state *st)
|
|
{
|
|
if (!st->convst_gpio)
|
|
return;
|
|
|
|
gpiod_set_value(st->convst_gpio, 0);
|
|
udelay(1); /* CONVST pulse width: 10 ns min */
|
|
gpiod_set_value(st->convst_gpio, 1);
|
|
udelay(1); /* Conversion time: 650 ns max */
|
|
}
|
|
|
|
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct ad7476_state *st = iio_priv(indio_dev);
|
|
int b_sent;
|
|
|
|
ad7091_convst(st);
|
|
|
|
b_sent = spi_sync(st->spi, &st->msg);
|
|
if (b_sent < 0)
|
|
goto done;
|
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
|
iio_get_time_ns(indio_dev));
|
|
done:
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void ad7091_reset(struct ad7476_state *st)
|
|
{
|
|
/* Any transfers with 8 scl cycles will reset the device */
|
|
spi_read(st->spi, st->data, 1);
|
|
}
|
|
|
|
static int ad7476_scan_direct(struct ad7476_state *st)
|
|
{
|
|
int ret;
|
|
|
|
ad7091_convst(st);
|
|
|
|
ret = spi_sync(st->spi, &st->msg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return be16_to_cpup((__be16 *)st->data);
|
|
}
|
|
|
|
static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val,
|
|
int *val2,
|
|
long m)
|
|
{
|
|
int ret;
|
|
struct ad7476_state *st = iio_priv(indio_dev);
|
|
int scale_uv;
|
|
|
|
switch (m) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
ret = ad7476_scan_direct(st);
|
|
iio_device_release_direct_mode(indio_dev);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
*val = (ret >> st->chip_info->channel[0].scan_type.shift) &
|
|
GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
if (st->ref_reg) {
|
|
scale_uv = regulator_get_voltage(st->ref_reg);
|
|
if (scale_uv < 0)
|
|
return scale_uv;
|
|
} else {
|
|
scale_uv = st->chip_info->int_vref_uv;
|
|
}
|
|
*val = scale_uv / 1000;
|
|
*val2 = chan->scan_type.realbits;
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
|
|
{ \
|
|
.type = IIO_VOLTAGE, \
|
|
.indexed = 1, \
|
|
.info_mask_separate = _info_mask_sep, \
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
|
.scan_type = { \
|
|
.sign = 'u', \
|
|
.realbits = (bits), \
|
|
.storagebits = 16, \
|
|
.shift = (_shift), \
|
|
.endianness = IIO_BE, \
|
|
}, \
|
|
}
|
|
|
|
#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
|
|
BIT(IIO_CHAN_INFO_RAW))
|
|
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
|
BIT(IIO_CHAN_INFO_RAW))
|
|
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
|
BIT(IIO_CHAN_INFO_RAW))
|
|
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
|
#define AD7091R_CONVST_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), \
|
|
BIT(IIO_CHAN_INFO_RAW))
|
|
#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
|
|
BIT(IIO_CHAN_INFO_RAW))
|
|
|
|
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
|
[ID_AD7091] = {
|
|
.channel[0] = AD7091R_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.convst_channel[0] = AD7091R_CONVST_CHAN(12),
|
|
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.reset = ad7091_reset,
|
|
},
|
|
[ID_AD7091R] = {
|
|
.channel[0] = AD7091R_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.convst_channel[0] = AD7091R_CONVST_CHAN(12),
|
|
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.int_vref_uv = 2500000,
|
|
.has_vref = true,
|
|
.reset = ad7091_reset,
|
|
},
|
|
[ID_AD7273] = {
|
|
.channel[0] = AD7940_CHAN(10),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.has_vref = true,
|
|
},
|
|
[ID_AD7274] = {
|
|
.channel[0] = AD7940_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.has_vref = true,
|
|
},
|
|
[ID_AD7276] = {
|
|
.channel[0] = AD7940_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7277] = {
|
|
.channel[0] = AD7940_CHAN(10),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7278] = {
|
|
.channel[0] = AD7940_CHAN(8),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7466] = {
|
|
.channel[0] = AD7476_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7467] = {
|
|
.channel[0] = AD7476_CHAN(10),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7468] = {
|
|
.channel[0] = AD7476_CHAN(8),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_AD7475] = {
|
|
.channel[0] = AD7476_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.has_vref = true,
|
|
.has_vdrive = true,
|
|
},
|
|
[ID_AD7495] = {
|
|
.channel[0] = AD7476_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.int_vref_uv = 2500000,
|
|
.has_vdrive = true,
|
|
},
|
|
[ID_AD7940] = {
|
|
.channel[0] = AD7940_CHAN(14),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADC081S] = {
|
|
.channel[0] = ADC081S_CHAN(8),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADC101S] = {
|
|
.channel[0] = ADC081S_CHAN(10),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADC121S] = {
|
|
.channel[0] = ADC081S_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADS7866] = {
|
|
.channel[0] = ADS786X_CHAN(12),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADS7867] = {
|
|
.channel[0] = ADS786X_CHAN(10),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_ADS7868] = {
|
|
.channel[0] = ADS786X_CHAN(8),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
},
|
|
[ID_LTC2314_14] = {
|
|
.channel[0] = AD7940_CHAN(14),
|
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
|
.has_vref = true,
|
|
},
|
|
};
|
|
|
|
static const struct iio_info ad7476_info = {
|
|
.read_raw = &ad7476_read_raw,
|
|
};
|
|
|
|
static void ad7476_reg_disable(void *data)
|
|
{
|
|
struct regulator *reg = data;
|
|
|
|
regulator_disable(reg);
|
|
}
|
|
|
|
static int ad7476_probe(struct spi_device *spi)
|
|
{
|
|
struct ad7476_state *st;
|
|
struct iio_dev *indio_dev;
|
|
struct regulator *reg;
|
|
int ret;
|
|
|
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
st = iio_priv(indio_dev);
|
|
st->chip_info =
|
|
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
|
|
|
reg = devm_regulator_get(&spi->dev, "vcc");
|
|
if (IS_ERR(reg))
|
|
return PTR_ERR(reg);
|
|
|
|
ret = regulator_enable(reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Either vcc or vref (below) as appropriate */
|
|
if (!st->chip_info->int_vref_uv)
|
|
st->ref_reg = reg;
|
|
|
|
if (st->chip_info->has_vref) {
|
|
|
|
/* If a device has an internal reference vref is optional */
|
|
if (st->chip_info->int_vref_uv) {
|
|
reg = devm_regulator_get_optional(&spi->dev, "vref");
|
|
if (IS_ERR(reg) && (PTR_ERR(reg) != -ENODEV))
|
|
return PTR_ERR(reg);
|
|
} else {
|
|
reg = devm_regulator_get(&spi->dev, "vref");
|
|
if (IS_ERR(reg))
|
|
return PTR_ERR(reg);
|
|
}
|
|
|
|
if (!IS_ERR(reg)) {
|
|
ret = regulator_enable(reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = devm_add_action_or_reset(&spi->dev,
|
|
ad7476_reg_disable,
|
|
reg);
|
|
if (ret)
|
|
return ret;
|
|
st->ref_reg = reg;
|
|
} else {
|
|
/*
|
|
* Can only get here if device supports both internal
|
|
* and external reference, but the regulator connected
|
|
* to the external reference is not connected.
|
|
* Set the reference regulator pointer to NULL to
|
|
* indicate this.
|
|
*/
|
|
st->ref_reg = NULL;
|
|
}
|
|
}
|
|
|
|
if (st->chip_info->has_vdrive) {
|
|
reg = devm_regulator_get(&spi->dev, "vdrive");
|
|
if (IS_ERR(reg))
|
|
return PTR_ERR(reg);
|
|
|
|
ret = regulator_enable(reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable,
|
|
reg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
st->convst_gpio = devm_gpiod_get_optional(&spi->dev,
|
|
"adi,conversion-start",
|
|
GPIOD_OUT_LOW);
|
|
if (IS_ERR(st->convst_gpio))
|
|
return PTR_ERR(st->convst_gpio);
|
|
|
|
spi_set_drvdata(spi, indio_dev);
|
|
|
|
st->spi = spi;
|
|
|
|
indio_dev->name = spi_get_device_id(spi)->name;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->channels = st->chip_info->channel;
|
|
indio_dev->num_channels = 2;
|
|
indio_dev->info = &ad7476_info;
|
|
|
|
if (st->convst_gpio)
|
|
indio_dev->channels = st->chip_info->convst_channel;
|
|
/* Setup default message */
|
|
|
|
st->xfer.rx_buf = &st->data;
|
|
st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
|
|
|
|
spi_message_init(&st->msg);
|
|
spi_message_add_tail(&st->xfer, &st->msg);
|
|
|
|
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
|
|
&ad7476_trigger_handler, NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (st->chip_info->reset)
|
|
st->chip_info->reset(st);
|
|
|
|
return devm_iio_device_register(&spi->dev, indio_dev);
|
|
}
|
|
|
|
static const struct spi_device_id ad7476_id[] = {
|
|
{"ad7091", ID_AD7091},
|
|
{"ad7091r", ID_AD7091R},
|
|
{"ad7273", ID_AD7273},
|
|
{"ad7274", ID_AD7274},
|
|
{"ad7276", ID_AD7276},
|
|
{"ad7277", ID_AD7277},
|
|
{"ad7278", ID_AD7278},
|
|
{"ad7466", ID_AD7466},
|
|
{"ad7467", ID_AD7467},
|
|
{"ad7468", ID_AD7468},
|
|
{"ad7475", ID_AD7475},
|
|
{"ad7476", ID_AD7466},
|
|
{"ad7476a", ID_AD7466},
|
|
{"ad7477", ID_AD7467},
|
|
{"ad7477a", ID_AD7467},
|
|
{"ad7478", ID_AD7468},
|
|
{"ad7478a", ID_AD7468},
|
|
{"ad7495", ID_AD7495},
|
|
{"ad7910", ID_AD7467},
|
|
{"ad7920", ID_AD7466},
|
|
{"ad7940", ID_AD7940},
|
|
{"adc081s", ID_ADC081S},
|
|
{"adc101s", ID_ADC101S},
|
|
{"adc121s", ID_ADC121S},
|
|
{"ads7866", ID_ADS7866},
|
|
{"ads7867", ID_ADS7867},
|
|
{"ads7868", ID_ADS7868},
|
|
{"ltc2314-14", ID_LTC2314_14},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
|
|
|
static struct spi_driver ad7476_driver = {
|
|
.driver = {
|
|
.name = "ad7476",
|
|
},
|
|
.probe = ad7476_probe,
|
|
.id_table = ad7476_id,
|
|
};
|
|
module_spi_driver(ad7476_driver);
|
|
|
|
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
|
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
|
MODULE_LICENSE("GPL v2");
|