mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
iio: dac: ad5686: add gain control support
Most of the supported devices rely on a GAIN pin to control a 2x multiplier applied to the output voltage. Other devices, e.g. the single-channel ones, provides a gain control through a bit field in the control register. Some designs might have the GAIN pin hardwired to VDD/VLOGIC or GND, which would have no "gain-gpios" device property, being able to set "adi,range-double" if it is hardwired to VDD. The vref_mv field is moved down in the struct ad5686_state, so that the overall size increase is reduced. Reviewed-by: David Lechner <dlechner@baylibre.com> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com> Signed-off-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
This commit is contained in:
parent
a3c37fa97b
commit
23f66b58b1
|
|
@ -15,10 +15,13 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/wordpart.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
|
|
@ -41,7 +44,8 @@ static int ad5310_control_sync(struct ad5686_state *st)
|
|||
|
||||
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
|
||||
FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) |
|
||||
FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1));
|
||||
FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) |
|
||||
FIELD_PREP(AD5310_DATA_GAIN_MSK, st->double_scale ? 1 : 0));
|
||||
}
|
||||
|
||||
static int ad5683_control_sync(struct ad5686_state *st)
|
||||
|
|
@ -50,7 +54,8 @@ static int ad5683_control_sync(struct ad5686_state *st)
|
|||
|
||||
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
|
||||
FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) |
|
||||
FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1));
|
||||
FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) |
|
||||
FIELD_PREP(AD5683_DATA_GAIN_MSK, st->double_scale ? 1 : 0));
|
||||
}
|
||||
|
||||
static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan)
|
||||
|
|
@ -193,9 +198,14 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
|
|||
GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
if (st->double_scale) {
|
||||
*val = st->scale_avail[2];
|
||||
*val2 = st->scale_avail[3];
|
||||
} else {
|
||||
*val = st->scale_avail[0];
|
||||
*val2 = st->scale_avail[1];
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -207,6 +217,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
bool double_scale;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
|
|
@ -217,6 +229,82 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
|
|||
|
||||
return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val << chan->scan_type.shift);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val == st->scale_avail[0] && val2 == st->scale_avail[1])
|
||||
double_scale = false;
|
||||
else if (val == st->scale_avail[2] && val2 == st->scale_avail[3])
|
||||
double_scale = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (st->double_scale == double_scale)
|
||||
return 0; /* no change */
|
||||
|
||||
if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio)
|
||||
return -EINVAL; /* GAIN pin is board-strapped */
|
||||
|
||||
st->double_scale = double_scale;
|
||||
switch (st->chip_info->regmap_type) {
|
||||
case AD5310_REGMAP:
|
||||
ret = ad5310_control_sync(st);
|
||||
break;
|
||||
case AD5683_REGMAP:
|
||||
ret = ad5683_control_sync(st);
|
||||
break;
|
||||
case AD5686_REGMAP:
|
||||
ret = gpiod_set_value_cansleep(st->gain_gpio,
|
||||
st->double_scale ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret)
|
||||
st->double_scale = !double_scale; /* revert on failure */
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5686_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*type = IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) {
|
||||
/*
|
||||
* GAIN pin is board-strapped, so only the current
|
||||
* scale is available.
|
||||
*/
|
||||
*vals = st->double_scale ? &st->scale_avail[2] :
|
||||
&st->scale_avail[0];
|
||||
*length = 2;
|
||||
return IIO_AVAIL_LIST;
|
||||
}
|
||||
|
||||
*vals = st->scale_avail;
|
||||
*length = ARRAY_SIZE(st->scale_avail);
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -225,6 +313,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
|
|||
static const struct iio_info ad5686_info = {
|
||||
.read_raw = ad5686_read_raw,
|
||||
.write_raw = ad5686_write_raw,
|
||||
.write_raw_get_fmt = ad5686_write_raw_get_fmt,
|
||||
.read_avail = ad5686_read_avail,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
||||
|
|
@ -246,6 +336,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
|||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.address = addr, \
|
||||
.scan_index = chan, \
|
||||
.scan_type = { \
|
||||
|
|
@ -472,6 +563,15 @@ const struct ad5686_chip_info ad5679r_chip_info = {
|
|||
};
|
||||
EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
|
||||
|
||||
static void ad5686_init_scale_avail(struct ad5686_state *st)
|
||||
{
|
||||
int realbits = st->chip_info->channels[0].scan_type.realbits;
|
||||
s64 tmp = 2ULL * st->vref_mv * NANO >> realbits;
|
||||
|
||||
st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]);
|
||||
st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]);
|
||||
}
|
||||
|
||||
static void do_ad5686_trigger_handler(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
|
@ -584,6 +684,16 @@ int ad5686_probe(struct device *dev,
|
|||
return dev_err_probe(dev, PTR_ERR(st->ldac_gpio),
|
||||
"Failed to get LDAC GPIO\n");
|
||||
|
||||
st->double_scale = device_property_read_bool(dev, "adi,range-double");
|
||||
st->gain_gpio = devm_gpiod_get_optional(dev, "gain",
|
||||
st->double_scale ? GPIOD_OUT_HIGH :
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gain_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(st->gain_gpio),
|
||||
"Failed to get GAIN GPIO\n");
|
||||
|
||||
ad5686_init_scale_avail(st);
|
||||
|
||||
/* Initialize masks to all ones */
|
||||
st->pwr_down_mask = ~0;
|
||||
st->pwr_down_mode = ~0;
|
||||
|
|
|
|||
|
|
@ -41,9 +41,11 @@
|
|||
#define AD5686_CMD_CONTROL_REG 0x4
|
||||
#define AD5686_CMD_READBACK_ENABLE_V2 0x5
|
||||
|
||||
#define AD5310_DATA_GAIN_MSK BIT(7)
|
||||
#define AD5310_DATA_REF_MSK BIT(8)
|
||||
#define AD5310_DATA_PD_MSK GENMASK(10, 9)
|
||||
|
||||
#define AD5683_DATA_GAIN_MSK BIT(11) /* DB15 */
|
||||
#define AD5683_DATA_REF_MSK BIT(12) /* DB16 */
|
||||
#define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */
|
||||
|
||||
|
|
@ -127,9 +129,12 @@ extern const struct ad5686_chip_info ad5679r_chip_info;
|
|||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @ops: bus specific operations
|
||||
* @ldac_gpio: LDAC pin GPIO descriptor
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @gain_gpio: GAIN pin GPIO descriptor
|
||||
* @pwr_down_mask: power down mask
|
||||
* @pwr_down_mode: current power down mode
|
||||
* @scale_avail: pre-calculated available scale values
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @double_scale: flag to indicate the gain multiplier is applied
|
||||
* @use_internal_vref: set to true if the internal reference voltage is used
|
||||
* @lock: lock to protect access to state fields, which includes
|
||||
* the data buffer during regmap ops
|
||||
|
|
@ -141,9 +146,12 @@ struct ad5686_state {
|
|||
const struct ad5686_chip_info *chip_info;
|
||||
const struct ad5686_bus_ops *ops;
|
||||
struct gpio_desc *ldac_gpio;
|
||||
unsigned short vref_mv;
|
||||
struct gpio_desc *gain_gpio;
|
||||
unsigned int pwr_down_mask;
|
||||
unsigned int pwr_down_mode;
|
||||
int scale_avail[4];
|
||||
unsigned short vref_mv;
|
||||
bool double_scale;
|
||||
bool use_internal_vref;
|
||||
struct mutex lock;
|
||||
void *bus_data;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user