Thread (13 messages) 13 messages, 4 authors, 2025-09-07

Re: [PATCH v3 4/4] iio: adc: ad7768-1: add support for ADAQ776x-1 ADC Family

From: David Lechner <dlechner@baylibre.com>
Date: 2025-09-05 17:08:56
Also in: linux-iio, lkml

On 9/5/25 4:49 AM, Jonathan Santos wrote:
Add support for ADAQ7767/68/69-1 series, which includes PGIA and
Anti-aliasing filter (AAF) gains. Unlike the AD7768-1, they do not
provide a VCM regulator interface.
...
+static void ad7768_fill_scale_tbl(struct iio_dev *dev)
+{
+	struct ad7768_state *st = iio_priv(dev);
+	const struct iio_scan_type *scan_type;
+	int val, val2, tmp0, tmp1, i;
+	struct u64_fract fract;
+	unsigned long n, d;
+	u64 tmp2;
+
+	scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
+	if (scan_type->sign == 's')
+		val2 = scan_type->realbits - 1;
+	else
+		val2 = scan_type->realbits;
+
+	for (i = 0; i < st->chip->num_pga_modes; i++) {
+		/* Convert gain to a fraction format */
+		fract.numerator = st->chip->pga_gains[i];
+		fract.denominator = MILLI;
+		if (st->chip->has_variable_aaf) {
+			fract.numerator *= ad7768_aaf_gains_bp[st->aaf_gain];
+			fract.denominator *= 10000;
+		}
+
+		rational_best_approximation(fract.numerator, fract.denominator,
+					    INT_MAX, INT_MAX, &n, &d);
+
+		val = mult_frac(st->vref_uv, d, n);
+		/* Would multiply by NANO here, but value is already in milli */
+		tmp2 = shift_right((u64)val * MICRO, val2);
shift_right() is only needed for signed values. Can just use >> since this is
dealing with unsigned.
+		tmp0 = div_u64_rem(tmp2, NANO, &tmp1);
+		st->scale_tbl[i][0] = tmp0; /* Integer part */
+		st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */
+	}
+}
+
...
+static int ad7768_set_pga_gain(struct ad7768_state *st,
+			       int gain_mode)
+{
+	int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset);
+	int ret;
+
+	guard(mutex)(&st->pga_lock);
+
+	ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL, AD7768_GPIO_PGIA_EN);
Hiding multiple fields in a macro makes it harder to see what this is doing.
+	if (ret)
+		return ret;
+
+	/* Write the respective gain values to GPIOs 0, 1, 2 */
+	ret = regmap_write(st->regmap, AD7768_REG_GPIO_WRITE,
+			   AD7768_GPIO_WRITE(pgia_pins_value));
Hiding the FIELD_PREP() in a macro makes this a bit harder to understand.
quoted hunk ↗ jump to hunk
+	if (ret)
+		return ret;
+
+	st->pga_gain_mode = gain_mode;
+
+	return 0;
+}
+
 static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
 {
 	struct iio_dev *indio_dev = gpiochip_get_data(chip);
@@ -778,6 +925,10 @@ static const struct iio_chan_spec ad7768_channels[] = {
 	AD7768_CHAN(0, 0),
 };
 
+static const struct iio_chan_spec adaq776x_channels[] = {
+	AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)),
+};
+
 static int ad7768_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan,
 			   int *val, int *val2, long info)
@@ -805,7 +956,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
 		return IIO_VAL_INT;
 
 	case IIO_CHAN_INFO_SCALE:
-		*val = (st->vref_uv * 2) / 1000;
+		if (st->chip->has_pga) {
+			guard(mutex)(&st->pga_lock);
+
+			*val = st->scale_tbl[st->pga_gain_mode][0];
+			*val2 = st->scale_tbl[st->pga_gain_mode][1];
+			return IIO_VAL_INT_PLUS_NANO;
+		}
+
+		temp = (st->vref_uv * 2) / 1000;
+		if (st->chip->has_variable_aaf)
+			temp = (temp * 10000) / ad7768_aaf_gains_bp[st->aaf_gain];
Should we add a BASIS_POINTS macro to units.h since we are calling this a
standard unit?
+
+		*val = temp;
 		*val2 = scan_type->realbits;
 
 		return IIO_VAL_FRACTIONAL_LOG2;
...
+static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st)
+{
+	bool aaf_gain_provided;
+	u32 val;
+	int ret;
+
+	st->aaf_gain = AD7768_AAF_IN1;
No sense in setting this if we return early on -EINVAL.
+	ret = device_property_read_u32(dev, "adi,aaf-gain-bp", &val);
+	aaf_gain_provided = (ret == 0);
Only -EINVAL means the property is not present. Other errors can be returned.
Or add a comment explaining why we don't care about other errors.
+	if (!aaf_gain_provided && st->chip->has_variable_aaf) {
+		dev_warn(dev, "AAF gain not specified, using default\n");
Using the default seems like a normal thing to do, so should not be a warning.
+		return 0;
+	}
+
+	if (aaf_gain_provided && !st->chip->has_variable_aaf) {
+		dev_warn(dev, "AAF gain not supported for %s\n", st->chip->name);
Returning error here would be senseible. Clearly there is something wrong with
the devicetree. But it could be something else, like the wrong compaible string.
+		return 0;
+	}
+
+	if (aaf_gain_provided) {
+		switch (val) {
+		case 10000:
+			st->aaf_gain = AD7768_AAF_IN1;
+			break;
+		case 3640:
+			st->aaf_gain = AD7768_AAF_IN2;
+			break;
+		case 1430:
+			st->aaf_gain = AD7768_AAF_IN3;
+			break;
+		default:
+			return dev_err_probe(dev, -EINVAL,
+					     "Invalid firmware provided AAF gain\n");
+		}
+	}
+
+	return 0;
+}
+
...
quoted hunk ↗ jump to hunk
 static int ad7768_probe(struct spi_device *spi)
@@ -1399,7 +1669,13 @@ static int ad7768_probe(struct spi_device *spi)
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
 	/* Register VCM output regulator */
-	ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
+	if (st->chip->has_vcm_regulator) {
+		ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
ad7768_register_regulators() now seems mis-named since it only handles VMC
supply.
+		if (ret)
+			return ret;
+	}
+
+	ret = ad7768_parse_aaf_gain(&spi->dev, st);
 	if (ret)
 		return ret;
 
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help