Skip to contentSkip to conversation

Support ROHM BD79105 ADC

PATCH Superseded Matti Vaittinen 5 participants 20 messages Aug 6, 2025 – Aug 7, 2025

Subsystems (4)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Matti Vaittinen

author maintainer
#0 lore raw

cover Support ROHM BD79105 ADC

Add support for the ROHM BD79105 ADC
(and do some minor simplifications to the ad7476 driver while at it).

The first 2 patches were originally sent as an RFC:
https://lore.kernel.org/all/[email protected]/

Revision history:
Simplification RFC => ROHM BD79105 support series v1:
- Use spi_get_device_match_data()
- Fix uV to mV conversion
- Rewording of commit message
- Added patches 3 to 8.

Matti Vaittinen (8):
iio: adc: ad7476: Simplify chip type detection
iio: adc: ad7476: Simplify scale handling
iio: adc: ad7476: Use mV for internal reference
iio: adc: ad7476: Use correct channel for bit info
iio: adc: ad7476: Conditionally call convstart
dt-bindings: iio: adc: ad7476: Add ROHM bd79105
iio: adc: ad7476: Support ROHM BD79105
MAINTAINERS: A driver for simple 1-channel SPI ADCs

 .../bindings/iio/adc/adi,ad7476.yaml          |  16 +
 MAINTAINERS                                   |   5 +
 drivers/iio/adc/ad7476.c                      | 428 +++++++++---------
 3 files changed, 231 insertions(+), 218 deletions(-)
Signature · 1 line
2.50.1
author maintainer
#1 lore raw

PATCH 1/8 iio: adc: ad7476: Simplify chip type detection

The ad7476 driver uses a table of structures for defining the IC variant specific data. Table is indexed using enum values, which are picked by SPI ID.

Having the table and an enum adds extra complexity and may encourage adding IC specific quircks in the code, instead of centralizing the IC differences in one place, the chip-info.

Simplify this by dropping the table and using individual structures for the IC specific data, and storing the IC specific structure's address directly in the SPI ID data. Finally, switch to the spi_get_device_match_data() and add a check for the return value.

---
Revision history:
This patch was originally sent as an RFC in 2 patch series:
https://lore.kernel.org/all/[email protected]/
Changes from the RFC:
- Use spi_get_device_match_data()
- Slightly reword commit message

---
 drivers/iio/adc/ad7476.c | 296 +++++++++++++++++++--------------------
 1 file changed, 146 insertions(+), 150 deletions(-)
drivers/iio/adc/ad7476.c +146 -150
index aea734aa06bd..9869526c1524 100644
@@ -52,29 +52,6 @@ struct ad7476_state {
 	unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
 };
 
-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)
@@ -190,102 +167,119 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
 #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 ad7476_chip_info ad7091_chip_info = {
+	.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,
+};
+
+static const struct ad7476_chip_info ad7091r_chip_info = {
+	.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,
+};
+
+static const struct ad7476_chip_info ad7273_chip_info = {
+	.channel[0] = AD7940_CHAN(10),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.has_vref = true,
+};
+
+static const struct ad7476_chip_info ad7274_chip_info = {
+	.channel[0] = AD7940_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.has_vref = true,
+};
+
+static const struct ad7476_chip_info ad7276_chip_info = {
+	.channel[0] = AD7940_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7277_chip_info = {
+	.channel[0] = AD7940_CHAN(10),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7278_chip_info = {
+	.channel[0] = AD7940_CHAN(8),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7466_chip_info = {
+	.channel[0] = AD7476_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7467_chip_info = {
+	.channel[0] = AD7476_CHAN(10),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7468_chip_info = {
+	.channel[0] = AD7476_CHAN(8),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ad7475_chip_info = {
+	.channel[0] = AD7476_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.has_vref = true,
+	.has_vdrive = true,
+};
+
+static const struct ad7476_chip_info ad7495_chip_info = {
+	.channel[0] = AD7476_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.int_vref_uv = 2500000,
+	.has_vdrive = true,
+};
+
+static const struct ad7476_chip_info ad7940_chip_info = {
+	.channel[0] = AD7940_CHAN(14),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info adc081s_chip_info = {
+	.channel[0] = ADC081S_CHAN(8),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info adc101s_chip_info = {
+	.channel[0] = ADC081S_CHAN(10),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info adc121s_chip_info = {
+	.channel[0] = ADC081S_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ads7866_chip_info = {
+	.channel[0] = ADS786X_CHAN(12),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ads7867_chip_info = {
+	.channel[0] = ADS786X_CHAN(10),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ads7868_chip_info = {
+	.channel[0] = ADS786X_CHAN(8),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7476_chip_info ltc2314_14_chip_info = {
+	.channel[0] = AD7940_CHAN(14),
+	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.has_vref = true,
 };
 
 static const struct iio_info ad7476_info = {
@@ -311,8 +305,10 @@ static int ad7476_probe(struct spi_device *spi)
 		return -ENOMEM;
 
 	st = iio_priv(indio_dev);
-	st->chip_info =
-		&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+	st->chip_info = spi_get_device_match_data(spi);
+	if (!st->chip_info)
+		return -ENODEV;
 
 	reg = devm_regulator_get(&spi->dev, "vcc");
 	if (IS_ERR(reg))
@@ -408,41 +404,41 @@ static int ad7476_probe(struct spi_device *spi)
 }
 
 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 },
+	{ "ad7091", (kernel_ulong_t)&ad7091_chip_info },
+	{ "ad7091r", (kernel_ulong_t)&ad7091r_chip_info },
+	{ "ad7273", (kernel_ulong_t)&ad7273_chip_info },
+	{ "ad7274", (kernel_ulong_t)&ad7274_chip_info },
+	{ "ad7276", (kernel_ulong_t)&ad7276_chip_info },
+	{ "ad7277", (kernel_ulong_t)&ad7277_chip_info },
+	{ "ad7278", (kernel_ulong_t)&ad7278_chip_info },
+	{ "ad7466", (kernel_ulong_t)&ad7466_chip_info },
+	{ "ad7467", (kernel_ulong_t)&ad7467_chip_info },
+	{ "ad7468", (kernel_ulong_t)&ad7468_chip_info },
+	{ "ad7475", (kernel_ulong_t)&ad7475_chip_info },
+	{ "ad7476", (kernel_ulong_t)&ad7466_chip_info },
+	{ "ad7476a", (kernel_ulong_t)&ad7466_chip_info },
+	{ "ad7477", (kernel_ulong_t)&ad7467_chip_info },
+	{ "ad7477a", (kernel_ulong_t)&ad7467_chip_info },
+	{ "ad7478", (kernel_ulong_t)&ad7468_chip_info },
+	{ "ad7478a", (kernel_ulong_t)&ad7468_chip_info },
+	{ "ad7495", (kernel_ulong_t)&ad7495_chip_info },
+	{ "ad7910", (kernel_ulong_t)&ad7467_chip_info },
+	{ "ad7920", (kernel_ulong_t)&ad7466_chip_info },
+	{ "ad7940", (kernel_ulong_t)&ad7940_chip_info },
+	{ "adc081s", (kernel_ulong_t)&adc081s_chip_info },
+	{ "adc101s", (kernel_ulong_t)&adc101s_chip_info },
+	{ "adc121s", (kernel_ulong_t)&adc121s_chip_info },
+	{ "ads7866", (kernel_ulong_t)&ads7866_chip_info },
+	{ "ads7867", (kernel_ulong_t)&ads7867_chip_info },
+	{ "ads7868", (kernel_ulong_t)&ads7868_chip_info },
 	/*
 	 * The ROHM BU79100G is identical to the TI's ADS7866 from the software
 	 * point of view. The binding document mandates the ADS7866 to be
 	 * marked as a fallback for the BU79100G, but we still need the SPI ID
 	 * here to make the module loading work.
 	 */
-	{ "bu79100g", ID_ADS7866 },
-	{ "ltc2314-14", ID_LTC2314_14 },
+	{ "bu79100g", (kernel_ulong_t)&ads7866_chip_info },
+	{ "ltc2314-14", (kernel_ulong_t)&ltc2314_14_chip_info },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, ad7476_id);
Signature · 1 line
2.50.1
author maintainer
#2 lore raw

PATCH 2/8 iio: adc: ad7476: Simplify scale handling

In reply to Matti Vaittinen

The ad7476 driver supports variants with different amount of supply regulators. On some variants there is only VCC, which is used as a reference voltage. Others have separate VREF regulator, and some rely on internal VREF. Some have both internal VREF and option to connect external one.

The ad7476 driver reads the regulator voltage only when the user asks to get the scale. This means the driver needs to do some dancing while picking the correct reference regulator (or internal reference), and store it for the later use.

According to the discussion:
https://lore.kernel.org/linux-iio/20250331122247.05c6b09d@jic23-huawei/
variable voltage references are rare, making it hard to justify the added complexity for supporting those.

Drop the support for the variable voltage references and simplify things by using the managed regulator get and enable interfaces.

---
Revision history:
This patch was originally sent as an RFC in 2 patch series:
https://lore.kernel.org/all/[email protected]/
Changes from the RFC:
- Fix the faulty conversion from uV to mV as pointed out by David.

---
 drivers/iio/adc/ad7476.c | 84 ++++++++++------------------------------
 1 file changed, 21 insertions(+), 63 deletions(-)
drivers/iio/adc/ad7476.c +21 -63
index 9869526c1524..f117aafd8fad 100644
@@ -39,10 +39,10 @@ struct ad7476_chip_info {
 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;
+	int				scale_mv;
 	/*
 	 * DMA (thus cache coherency maintenance) may require the
 	 * transfer buffers to live in their own cache lines.
@@ -111,7 +111,6 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
 {
 	int ret;
 	struct ad7476_state *st = iio_priv(indio_dev);
-	int scale_uv;
 
 	switch (m) {
 	case IIO_CHAN_INFO_RAW:
@@ -126,14 +125,7 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
 			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;
+		*val = st->scale_mv;
 		*val2 = chan->scan_type.realbits;
 		return IIO_VAL_FRACTIONAL_LOG2;
 	}
@@ -286,18 +278,10 @@ 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));
@@ -310,58 +294,32 @@ static int ad7476_probe(struct spi_device *spi)
 	if (!st->chip_info)
 		return -ENODEV;
 
-	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;
+	/* Use VCC for reference voltage if vref / internal vref aren't used */
+	if (!st->chip_info->int_vref_uv && !st->chip_info->has_vref) {
+		ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc");
+		if (ret < 0)
+			return ret;
+		st->scale_mv = ret / 1000;
+	} else {
+		ret = devm_regulator_get_enable(&spi->dev, "vcc");
+		if (ret < 0)
+			return ret;
+	}
 
 	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)
+		ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref");
+		if (ret < 0) {
+			/* Vref is optional if a device has an internal reference */
+			if (!st->chip_info->int_vref_uv || ret != -ENODEV)
 				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;
+			st->scale_mv = ret / 1000;
 		}
 	}
 
+	if (!st->scale_mv)
+		st->scale_mv = st->chip_info->int_vref_uv / 1000;
+
 	if (st->chip_info->has_vdrive) {
 		ret = devm_regulator_get_enable(&spi->dev, "vdrive");
 		if (ret)
Signature · 1 line
2.50.1
#3 lore raw

Re: [PATCH 2/8] iio: adc: ad7476: Simplify scale handling

Quoted 6 lines · Matti Vaittinen

The ad7476 driver supports variants with different amount of supply regulators. On some variants there is only VCC, which is used as a reference voltage. Others have separate VREF regulator, and some rely on internal VREF. Some have both internal VREF and option to connect external one.

Quoted 7 lines

The ad7476 driver reads the regulator voltage only when the user asks to get the scale. This means the driver needs to do some dancing while picking the correct reference regulator (or internal reference), and store it for the later use.

According to the discussion:
https://lore.kernel.org/linux-iio/20250331122247.05c6b09d@jic23-huawei/

This may be made a Link tag

According to the discussion [1] ...
...
Link: $URL #1

Quoted 5 lines

variable voltage references are rare, making it hard to justify the added complexity for supporting those.

Drop the support for the variable voltage references and simplify things by using the managed regulator get and enable interfaces.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author maintainer
#4 lore raw

Re: [PATCH 2/8] iio: adc: ad7476: Simplify scale handling

Quoted 12 lines · Andy Shevchenko
Quoted 10 lines · Matti Vaittinen

The ad7476 driver supports variants with different amount of supply regulators. On some variants there is only VCC, which is used as a reference voltage. Others have separate VREF regulator, and some rely on internal VREF. Some have both internal VREF and option to connect external one.

The ad7476 driver reads the regulator voltage only when the user asks to get the scale. This means the driver needs to do some dancing while picking the correct reference regulator (or internal reference), and store it for the later use.

Quoted 8 lines
Quoted 2 lines

According to the discussion:
https://lore.kernel.org/linux-iio/20250331122247.05c6b09d@jic23-huawei/

This may be made a Link tag

According to the discussion [1] ...
...
Link: $URL #1

Thanks for the clear example. Will do.

Yours,
-- Matti

author maintainer
#5 lore raw

PATCH 3/8 iio: adc: ad7476: Use mV for internal reference

In reply to Matti Vaittinen

The ad7476 supports some ICs with an internal reference voltage. For those ICs the reference voltage has been hard-coded as micro volts, but the value which is later used in code needs to be milli volts. This results the need to divide hard coded voltage by 1000 before using it.

Simplify code by changing the hard-coded voltage to millivolts and by dropping the division.

---
 drivers/iio/adc/ad7476.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/iio/adc/ad7476.c +6 -6
index f117aafd8fad..7b6d36999afc 100644
@@ -27,7 +27,7 @@
 struct ad7476_state;
 
 struct ad7476_chip_info {
-	unsigned int			int_vref_uv;
+	unsigned int			int_vref_mv;
 	struct iio_chan_spec		channel[2];
 	/* channels used when convst gpio is defined */
 	struct iio_chan_spec		convst_channel[2];
@@ -172,7 +172,7 @@ static const struct ad7476_chip_info ad7091r_chip_info = {
 	.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,
+	.int_vref_mv = 2500,
 	.has_vref = true,
 	.reset = ad7091_reset,
 };
@@ -229,7 +229,7 @@ static const struct ad7476_chip_info ad7475_chip_info = {
 static const struct ad7476_chip_info ad7495_chip_info = {
 	.channel[0] = AD7476_CHAN(12),
 	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
-	.int_vref_uv = 2500000,
+	.int_vref_mv = 2500,
 	.has_vdrive = true,
 };
 
@@ -295,7 +295,7 @@ static int ad7476_probe(struct spi_device *spi)
 		return -ENODEV;
 
 	/* Use VCC for reference voltage if vref / internal vref aren't used */
-	if (!st->chip_info->int_vref_uv && !st->chip_info->has_vref) {
+	if (!st->chip_info->int_vref_mv && !st->chip_info->has_vref) {
 		ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc");
 		if (ret < 0)
 			return ret;
@@ -310,7 +310,7 @@ static int ad7476_probe(struct spi_device *spi)
 		ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref");
 		if (ret < 0) {
 			/* Vref is optional if a device has an internal reference */
-			if (!st->chip_info->int_vref_uv || ret != -ENODEV)
+			if (!st->chip_info->int_vref_mv || ret != -ENODEV)
 				return ret;
 		} else {
 			st->scale_mv = ret / 1000;
@@ -318,7 +318,7 @@ static int ad7476_probe(struct spi_device *spi)
 	}
 
 	if (!st->scale_mv)
-		st->scale_mv = st->chip_info->int_vref_uv / 1000;
+		st->scale_mv = st->chip_info->int_vref_mv;
 
 	if (st->chip_info->has_vdrive) {
 		ret = devm_regulator_get_enable(&spi->dev, "vdrive");
Signature · 1 line
2.50.1
author maintainer
#6 lore raw

PATCH 4/8 iio: adc: ad7476: Use correct channel for bit info

In reply to Matti Vaittinen

The ad7476 supports ADCs which use separate GPIO for starting the conversion. For such devices, the driver uses different channel information if the GPIO is found. The bit information is still always used from the original (non 'convstart') channels.

This has not been causing problems because the bit information for the 'convstart' -channel and the 'normal' -channel is identical. It, however, will cause issues if an IC has different characteristics for an 'convstart' -channel and regular channel. Furthermore, this will cause problems if a device always requires the convstart GPIO and thus only defines the convstart channel.

---
It appears that the _only_ difference between the 'convstart' -channel and the 'normal' channel is a lack of the 'raw-read' support. I might prefer seeing the _same_ channel information being used for 'convstart' and 'normal' channels, just setting the IIO_CHAN_INFO_RAW -bit when the CONVSTART GPIO is found. This would allow getting rid of the 'convstart' -channel spec altogeher. Having only one channel info spec would also help the code-reader to understand that the driver really provides only one data channel to the users. Currently a quick reader may assume the driver for some reason provides both the 'convstart' and the 'normal' channels.

Adding the IIO_CHAN_INFO_RAW when CONVSTART GPIO is obtained would however require the channel information structs to be mutable - which may be seen as a "no, no" by some. Hence this minimally intrusive patch.

---
 drivers/iio/adc/ad7476.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iio/adc/ad7476.c +3 -3
index 7b6d36999afc..fc701267358e 100644
@@ -121,8 +121,8 @@ static int ad7476_read_raw(struct iio_dev *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);
+		*val = (ret >> chan->scan_type.shift) &
+			GENMASK(chan->scan_type.realbits - 1, 0);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		*val = st->scale_mv;
@@ -345,7 +345,7 @@ static int ad7476_probe(struct spi_device *spi)
 	/* Setup default message */
 
 	st->xfer.rx_buf = &st->data;
-	st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
+	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
 
 	spi_message_init(&st->msg);
 	spi_message_add_tail(&st->xfer, &st->msg);
Signature · 1 line
2.50.1
#7 lore raw

Re: [PATCH 4/8] iio: adc: ad7476: Use correct channel for bit info

Quoted 25 lines · On Wed, 6 Aug 2025 10:03:43 +0300 Matti Vaittinen <[email protected]>…

The ad7476 supports ADCs which use separate GPIO for starting the conversion. For such devices, the driver uses different channel information if the GPIO is found. The bit information is still always used from the original (non 'convstart') channels.

This has not been causing problems because the bit information for the 'convstart' -channel and the 'normal' -channel is identical. It, however, will cause issues if an IC has different characteristics for an 'convstart' -channel and regular channel. Furthermore, this will cause problems if a device always requires the convstart GPIO and thus only defines the convstart channel.

---
It appears that the _only_ difference between the 'convstart' -channel and the 'normal' channel is a lack of the 'raw-read' support. I might prefer seeing the _same_ channel information being used for 'convstart' and 'normal' channels, just setting the IIO_CHAN_INFO_RAW -bit when the CONVSTART GPIO is found. This would allow getting rid of the 'convstart' -channel spec altogeher. Having only one channel info spec would also help the code-reader to understand that the driver really provides only one data channel to the users. Currently a quick reader may assume the driver for some reason provides both the 'convstart' and the 'normal' channels.

Quoted 3 lines

Adding the IIO_CHAN_INFO_RAW when CONVSTART GPIO is obtained would however require the channel information structs to be mutable - which may be seen as a "no, no" by some. Hence this minimally intrusive patch.

If you duplicate them before updating that is probably fine, just keep the ones in the chip info static const.

Quoted 28 lines
---
 drivers/iio/adc/ad7476.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iio/adc/ad7476.c +3 -3
index 7b6d36999afc..fc701267358e 100644
@@ -121,8 +121,8 @@ static int ad7476_read_raw(struct iio_dev *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);
+		*val = (ret >> chan->scan_type.shift) &
+			GENMASK(chan->scan_type.realbits - 1, 0);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		*val = st->scale_mv;
@@ -345,7 +345,7 @@ static int ad7476_probe(struct spi_device *spi)
 	/* Setup default message */
 
 	st->xfer.rx_buf = &st->data;
-	st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
+	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
 
 	spi_message_init(&st->msg);
 	spi_message_add_tail(&st->xfer, &st->msg);
author maintainer
#8 lore raw

Re: [PATCH 4/8] iio: adc: ad7476: Use correct channel for bit info

Quoted 28 lines · Jonathan Cameron
Quoted 25 lines · On Wed, 6 Aug 2025 10:03:43 +0300 Matti Vaittinen <[email protected]>…

The ad7476 supports ADCs which use separate GPIO for starting the conversion. For such devices, the driver uses different channel information if the GPIO is found. The bit information is still always used from the original (non 'convstart') channels.

This has not been causing problems because the bit information for the 'convstart' -channel and the 'normal' -channel is identical. It, however, will cause issues if an IC has different characteristics for an 'convstart' -channel and regular channel. Furthermore, this will cause problems if a device always requires the convstart GPIO and thus only defines the convstart channel.

---
It appears that the _only_ difference between the 'convstart' -channel and the 'normal' channel is a lack of the 'raw-read' support. I might prefer seeing the _same_ channel information being used for 'convstart' and 'normal' channels, just setting the IIO_CHAN_INFO_RAW -bit when the CONVSTART GPIO is found. This would allow getting rid of the 'convstart' -channel spec altogeher. Having only one channel info spec would also help the code-reader to understand that the driver really provides only one data channel to the users. Currently a quick reader may assume the driver for some reason provides both the 'convstart' and the 'normal' channels.

Quoted 5 lines
Quoted 3 lines

Adding the IIO_CHAN_INFO_RAW when CONVSTART GPIO is obtained would however require the channel information structs to be mutable - which may be seen as a "no, no" by some. Hence this minimally intrusive patch.

If you duplicate them before updating that is probably fine, just keep the ones in the chip info static const.

This will mean allocating a new channel spec for each instance of this driver. Tradeoff seems to be clarity Vs memory consumption then. Well, I suppose systems don't have that many of these ADCs, right?

Well, I'll do this if no-one objects then.

Quoted 29 lines
Quoted 28 lines
---
  drivers/iio/adc/ad7476.c | 6 +++---
  1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iio/adc/ad7476.c +3 -3
index 7b6d36999afc..fc701267358e 100644
@@ -121,8 +121,8 @@ static int ad7476_read_raw(struct iio_dev *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);
+		*val = (ret >> chan->scan_type.shift) &
+			GENMASK(chan->scan_type.realbits - 1, 0);
  		return IIO_VAL_INT;
  	case IIO_CHAN_INFO_SCALE:
  		*val = st->scale_mv;
@@ -345,7 +345,7 @@ static int ad7476_probe(struct spi_device *spi)
  	/* Setup default message */
 
  	st->xfer.rx_buf = &st->data;
-	st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
+	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
 
  	spi_message_init(&st->msg);
  	spi_message_add_tail(&st->xfer, &st->msg);
author maintainer
#9 lore raw

PATCH 5/8 iio: adc: ad7476: Conditionally call convstart

In reply to Matti Vaittinen

The ad7476 supports two IC variants which may have a 'convstart' -GPIO for starting the conversion. Currently the driver calls a function which tries to access the GPIO for all of the IC variants, whether they support 'convstart' or not. This is not an error because this function returns early if GPIO information is not populated.

We can do a tad better by calling this function only for the ICs which have the 'convstart' by providing a function pointer to the convstart function from the chip_info structure, and calling this function only for the ICs which have the function pointer set.

This does also allow to support ICs which require different convstart handling than the currently supported ICs.

Call convstart function only on the ICs which can support it and allow IC-specific convstart functions for the ICs which require different handling.

---
The follow-up patch adding support for the ROHM BD79105 will bring different 'convstart' functions in use. The IC specific pointer will also prepare the way for this.

---
 drivers/iio/adc/ad7476.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/iio/adc/ad7476.c +5 -1
index fc701267358e..1f736be09663 100644
@@ -32,6 +32,7 @@ struct ad7476_chip_info {
 	/* channels used when convst gpio is defined */
 	struct iio_chan_spec		convst_channel[2];
 	void (*reset)(struct ad7476_state *);
+	void (*conversion_pre_op)(struct ad7476_state *st);
 	bool				has_vref;
 	bool				has_vdrive;
 };
@@ -70,7 +71,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
 	struct ad7476_state *st = iio_priv(indio_dev);
 	int b_sent;
 
-	ad7091_convst(st);
+	if (st->chip_info->conversion_pre_op)
+		st->chip_info->conversion_pre_op(st);
 
 	b_sent = spi_sync(st->spi, &st->msg);
 	if (b_sent < 0)
@@ -164,6 +166,7 @@ static const struct ad7476_chip_info ad7091_chip_info = {
 	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	.convst_channel[0] = AD7091R_CONVST_CHAN(12),
 	.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.conversion_pre_op = ad7091_convst,
 	.reset = ad7091_reset,
 };
 
@@ -172,6 +175,7 @@ static const struct ad7476_chip_info ad7091r_chip_info = {
 	.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	.convst_channel[0] = AD7091R_CONVST_CHAN(12),
 	.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	.conversion_pre_op = ad7091_convst,
 	.int_vref_mv = 2500,
 	.has_vref = true,
 	.reset = ad7091_reset,
Signature · 1 line
2.50.1
author maintainer
#10 lore raw

PATCH 6/8 dt-bindings: iio: adc: ad7476: Add ROHM bd79105

In reply to Matti Vaittinen

The ROHM BD79105 is a simple, 16-bit, 1-channel ADC with a 'CONVSTART' pin used to start the ADC conversion. Other than the 'CONVSTART', there are 3 supply pins (one used as a reference), analog inputs, ground and communication pins. It's worth noting that the pin somewhat confusingly labeled as 'DIN', is a pin which should be used as a chip-select. The IC does not have any writable registers.

The device is designed so that the output pin can, in addition to outputting the data, be used as a 'data-ready'-IRQ. This, however, would require the IRQ to be masked from host side for the duration of the data reads - and it wouldn't also work when the SPI is shared. (As access to the other SPI devices would cause data line changes to be detected as IRQs - and the BD79105 provides no means to detect if it has generated an IRQ).

Hence the device-tree does not contain any IRQ properties.

Add a compatible for the bd79105.

---
 .../devicetree/bindings/iio/adc/adi,ad7476.yaml  | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml +16 -0
index d0cb32f136e5..16d3866dd633 100644
@@ -41,6 +41,7 @@ properties:
               - adi,ad7910
               - adi,ad7920
               - adi,ad7940
+              - rohm,bd79105
               - ti,adc081s
               - ti,adc101s
               - ti,adc121s
@@ -115,6 +116,7 @@ allOf:
               - adi,ad7274
               - adi,ad7475
               - lltc,ltc2314-14
+              - rohm,bd79105
     then:
       properties:
         vref-supply: true
@@ -131,6 +133,7 @@ allOf:
               - adi,ad7274
               - adi,ad7475
               - lltc,ltc2314-14
+              - rohm,bd79105
     then:
       required:
         - vref-supply
@@ -141,6 +144,7 @@ allOf:
             enum:
               - adi,ad7475
               - adi,ad7495
+              - rohm,bd79105
     then:
       properties:
         vdrive-supply: true
@@ -154,6 +158,7 @@ allOf:
             enum:
               - adi,ad7091
               - adi,ad7091r
+              - rohm,bd79105
     then:
       properties:
         adi,conversion-start-gpios: true
@@ -161,6 +166,17 @@ allOf:
       properties:
         adi,conversion-start-gpios: false
 
+  # Devices with a convstart GPIO where it is not optional
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - rohm,bd79105
+    then:
+      required:
+        - adi,conversion-start-gpios
+
 unevaluatedProperties: false
 
 examples:
Signature · 1 line
2.50.1
reviewer
#11 lore raw

Re: [PATCH 6/8] dt-bindings: iio: adc: ad7476: Add ROHM bd79105

Quoted 15 lines · Matti Vaittinen

The ROHM BD79105 is a simple, 16-bit, 1-channel ADC with a 'CONVSTART' pin used to start the ADC conversion. Other than the 'CONVSTART', there are 3 supply pins (one used as a reference), analog inputs, ground and communication pins. It's worth noting that the pin somewhat confusingly labeled as 'DIN', is a pin which should be used as a chip-select. The IC does not have any writable registers.

The device is designed so that the output pin can, in addition to outputting the data, be used as a 'data-ready'-IRQ. This, however, would require the IRQ to be masked from host side for the duration of the data reads - and it wouldn't also work when the SPI is shared. (As access to the other SPI devices would cause data line changes to be detected as IRQs - and the BD79105 provides no means to detect if it has generated an IRQ).

Quoted 1 line

Hence the device-tree does not contain any IRQ properties.

There are lots of other ADC chips that have a ready signal like this and we've made them work. Since devicetree bindings should be as complete as possible even if the driver doesn't use all of the features, I think we should be including the interrupt in the binding. We have also found that some interrupt controllers won't work as you have suggested and in that case we also needed a ready-gpios to be able to read the state of the pin.

author maintainer
#12 lore raw

Re: [PATCH 6/8] dt-bindings: iio: adc: ad7476: Add ROHM bd79105

Quoted 16 lines · David Lechner
Quoted 15 lines · Matti Vaittinen

The ROHM BD79105 is a simple, 16-bit, 1-channel ADC with a 'CONVSTART' pin used to start the ADC conversion. Other than the 'CONVSTART', there are 3 supply pins (one used as a reference), analog inputs, ground and communication pins. It's worth noting that the pin somewhat confusingly labeled as 'DIN', is a pin which should be used as a chip-select. The IC does not have any writable registers.

The device is designed so that the output pin can, in addition to outputting the data, be used as a 'data-ready'-IRQ. This, however, would require the IRQ to be masked from host side for the duration of the data reads - and it wouldn't also work when the SPI is shared. (As access to the other SPI devices would cause data line changes to be detected as IRQs - and the BD79105 provides no means to detect if it has generated an IRQ).

Quoted 4 lines
Quoted 1 line

Hence the device-tree does not contain any IRQ properties.

There are lots of other ADC chips that have a ready signal like this and we've made them work.

Ah. I had no idea. Thanks for the insight!

Quoted 3 lines

Since devicetree bindings should be as
complete as possible even if the driver doesn't use all of the features, I think we should be including the interrupt in the binding.

After what you wrote above, I do agree. There may be systems where the IRQ is usable, so dt should have it even if the Linux driver never used it.

Quoted 3 lines

We have also found that some interrupt controllers won't work as you have suggested and in that case we also needed a ready-gpios to be able to read the state of the pin.

Oh. My thinking was just hard-coding the conversion-time delay, but this can indeed make sense - especially if there are other examples :)

Thanks a lot for the insight!

Yours,
-- Matti

author maintainer
#13 lore raw

PATCH 7/8 iio: adc: ad7476: Support ROHM BD79105

In reply to Matti Vaittinen

The ROHM BD79105 is a simple 16-bit ADC accessible via SPI*.

The BD79105 has a CONVSTART pin, which must be set high to start the ADC conversion. Unlike with the ad7091 and ad7091r which also have a CONVSTART pin, the BD79105 requires that the pin must remain high also for the duration of the SPI access.

(*) Couple of words about the SPI. The BD79105 has pins named as CONVSTART, SCLK, DIN and DOUT. For the curious reader, DIN is not SPI ISO.

DIN is a signal which can be used as a chip-select. When DIN is pulled low, the ADC will output the completed measurement via DOUT as SCLK is clocked. According to the data-sheet, the DIN can also be used for daisy-chaining multiple ADCs. Also, DOUT can be used also for a 'data-ready' -IRQ. These modes aren't supported by this driver.

Support reading ADC scale and data from the BD79105 using SPI, when DIN is used as a chip-select.

---
 drivers/iio/adc/ad7476.c | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)
drivers/iio/adc/ad7476.c +35 -1
index 1f736be09663..fc98aadc4077 100644
@@ -33,6 +33,7 @@ struct ad7476_chip_info {
 	struct iio_chan_spec		convst_channel[2];
 	void (*reset)(struct ad7476_state *);
 	void (*conversion_pre_op)(struct ad7476_state *st);
+	void (*conversion_post_op)(struct ad7476_state *st);
 	bool				has_vref;
 	bool				has_vdrive;
 };
@@ -64,6 +65,23 @@ static void ad7091_convst(struct ad7476_state *st)
 	udelay(1); /* Conversion time: 650 ns max */
 }
 
+static void bd79105_convst_disable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;
+
+	gpiod_set_value(st->convst_gpio, 0);
+}
+
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;
+
+	gpiod_set_value(st->convst_gpio, 1);
+	udelay(1); /* 10ns required for conversion */
+}
+
 static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
 {
 	struct iio_poll_func *pf = p;
@@ -81,6 +99,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
 	iio_push_to_buffers_with_ts(indio_dev, st->data, sizeof(st->data),
 				    iio_get_time_ns(indio_dev));
 done:
+	if (st->chip_info->conversion_post_op)
+		st->chip_info->conversion_post_op(st);
 	iio_trigger_notify_done(indio_dev->trig);
 
 	return IRQ_HANDLED;
@@ -278,6 +298,20 @@ static const struct ad7476_chip_info ltc2314_14_chip_info = {
 	.has_vref = true,
 };
 
+static const struct ad7476_chip_info bd79105_chip_info = {
+	.convst_channel[0] = AD7091R_CONVST_CHAN(16),
+	.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	/*
+	 * The BD79105 starts ADC data conversion when thw CONVSTART is set
+	 * HIGH. The CONVSTART must be kept HIGH until the data has been
+	 * read from the ADC.
+	 */
+	.conversion_pre_op = bd79105_convst_enable,
+	.conversion_post_op = bd79105_convst_disable,
+	.has_vref = true,
+	.has_vdrive = true,
+};
+
 static const struct iio_info ad7476_info = {
 	.read_raw = &ad7476_read_raw,
 };
@@ -347,7 +381,6 @@ static int ad7476_probe(struct spi_device *spi)
 	if (st->convst_gpio)
 		indio_dev->channels = st->chip_info->convst_channel;
 	/* Setup default message */
-
 	st->xfer.rx_buf = &st->data;
 	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
 
@@ -393,6 +426,7 @@ static const struct spi_device_id ad7476_id[] = {
 	{ "ads7866", (kernel_ulong_t)&ads7866_chip_info },
 	{ "ads7867", (kernel_ulong_t)&ads7867_chip_info },
 	{ "ads7868", (kernel_ulong_t)&ads7868_chip_info },
+	{ "bd79105", (kernel_ulong_t)&bd79105_chip_info },
 	/*
 	 * The ROHM BU79100G is identical to the TI's ADS7866 from the software
 	 * point of view. The binding document mandates the ADS7866 to be
Signature · 1 line
2.50.1
reviewer
#14 lore raw

Re: [PATCH 7/8] iio: adc: ad7476: Support ROHM BD79105

Quoted 7 lines · Matti Vaittinen

The ROHM BD79105 is a simple 16-bit ADC accessible via SPI*.

The BD79105 has a CONVSTART pin, which must be set high to start the ADC conversion. Unlike with the ad7091 and ad7091r which also have a CONVSTART pin, the BD79105 requires that the pin must remain high also for the duration of the SPI access.

Quoted 8 lines

(*) Couple of words about the SPI. The BD79105 has pins named as CONVSTART, SCLK, DIN and DOUT. For the curious reader, DIN is not SPI ISO.

DIN is a signal which can be used as a chip-select. When DIN is pulled low, the ADC will output the completed measurement via DOUT as SCLK is clocked. According to the data-sheet, the DIN can also be used for daisy-chaining multiple ADCs. Also, DOUT can be used also for a

Leave out one of the "also"s.

Quoted 31 lines

'data-ready' -IRQ. These modes aren't supported by this driver.

Support reading ADC scale and data from the BD79105 using SPI, when DIN is used as a chip-select.

---
 drivers/iio/adc/ad7476.c | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)
drivers/iio/adc/ad7476.c +6 -0
index 1f736be09663..fc98aadc4077 100644
@@ -33,6 +33,7 @@ struct ad7476_chip_info {
 	struct iio_chan_spec		convst_channel[2];
 	void (*reset)(struct ad7476_state *);
 	void (*conversion_pre_op)(struct ad7476_state *st);
+	void (*conversion_post_op)(struct ad7476_state *st);
 	bool				has_vref;
 	bool				has_vdrive;
 };
@@ -64,6 +65,23 @@ static void ad7091_convst(struct ad7476_state *st)
 	udelay(1); /* Conversion time: 650 ns max */
 }
 
+static void bd79105_convst_disable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;
+

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 82

Quoted 10 lines
drivers/iio/adc/ad7476.c +10 -0
+	gpiod_set_value(st->convst_gpio, 0);
+}
+
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;
+
+	gpiod_set_value(st->convst_gpio, 1);
+	udelay(1); /* 10ns required for conversion */

So ndelay(10)?

Quoted 15 lines
drivers/iio/adc/ad7476.c +4 -0
+}
+
 static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
 {
 	struct iio_poll_func *pf = p;
@@ -81,6 +99,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
 	iio_push_to_buffers_with_ts(indio_dev, st->data, sizeof(st->data),
 				    iio_get_time_ns(indio_dev));
 done:
+	if (st->chip_info->conversion_post_op)
+		st->chip_info->conversion_post_op(st);
 	iio_trigger_notify_done(indio_dev->trig);
 
 	return IRQ_HANDLED;

drivers/iio/adc/ad7476.c static const struct ad7476_chip_info ltc2314_14_chip_info = { · line 305

Quoted 8 lines
drivers/iio/adc/ad7476.c +5 -0
@@ -278,6 +298,20 @@ static const struct ad7476_chip_info ltc2314_14_chip_info = {
 	.has_vref = true,
 };
 
+static const struct ad7476_chip_info bd79105_chip_info = {
+	.convst_channel[0] = AD7091R_CONVST_CHAN(16),
+	.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+	/*
+	 * The BD79105 starts ADC data conversion when thw CONVSTART is set

s/thw/the/

Also s/CONVSTART/CONVSTART line/ would be a bit more clear.

Quoted 13 lines
drivers/iio/adc/ad7476.c +9 -0
+	 * HIGH. The CONVSTART must be kept HIGH until the data has been
+	 * read from the ADC.
+	 */
+	.conversion_pre_op = bd79105_convst_enable,
+	.conversion_post_op = bd79105_convst_disable,
+	.has_vref = true,
+	.has_vdrive = true,
+};
+
 static const struct iio_info ad7476_info = {
 	.read_raw = &ad7476_read_raw,
 };

drivers/iio/adc/ad7476.c static int ad7476_probe(struct spi_device *spi) · line 350

Quoted 4 lines
drivers/iio/adc/ad7476.c +0 -1
@@ -347,7 +381,6 @@ static int ad7476_probe(struct spi_device *spi)
 	if (st->convst_gpio)
 		indio_dev->channels = st->chip_info->convst_channel;
 	/* Setup default message */
-

Random whitespace change.

drivers/iio/adc/ad7476.c static const struct spi_device_id ad7476_id[] = { · line 432

Quoted 11 lines
drivers/iio/adc/ad7476.c +1 -0
 	st->xfer.rx_buf = &st->data;
 	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
 
@@ -393,6 +426,7 @@ static const struct spi_device_id ad7476_id[] = {
 	{ "ads7866", (kernel_ulong_t)&ads7866_chip_info },
 	{ "ads7867", (kernel_ulong_t)&ads7867_chip_info },
 	{ "ads7868", (kernel_ulong_t)&ads7868_chip_info },
+	{ "bd79105", (kernel_ulong_t)&bd79105_chip_info },
 	/*
 	 * The ROHM BU79100G is identical to the TI's ADS7866 from the software
 	 * point of view. The binding document mandates the ADS7866 to be

Unrelated to this patch, but interesting that we don't also have an of_ lookup table.

author maintainer
#15 lore raw

Re: [PATCH 7/8] iio: adc: ad7476: Support ROHM BD79105

Thanks again David.

Quoted 33 lines · David Lechner
Quoted 7 lines · Matti Vaittinen

The ROHM BD79105 is a simple 16-bit ADC accessible via SPI*.

The BD79105 has a CONVSTART pin, which must be set high to start the ADC conversion. Unlike with the ad7091 and ad7091r which also have a CONVSTART pin, the BD79105 requires that the pin must remain high also for the duration of the SPI access.

Quoted 8 lines

(*) Couple of words about the SPI. The BD79105 has pins named as CONVSTART, SCLK, DIN and DOUT. For the curious reader, DIN is not SPI ISO.

DIN is a signal which can be used as a chip-select. When DIN is pulled low, the ADC will output the completed measurement via DOUT as SCLK is clocked. According to the data-sheet, the DIN can also be used for daisy-chaining multiple ADCs. Also, DOUT can be used also for a

Leave out one of the "also"s.

Quoted 14 lines

'data-ready' -IRQ. These modes aren't supported by this driver.

Support reading ADC scale and data from the BD79105 using SPI, when DIN is used as a chip-select.

---
  drivers/iio/adc/ad7476.c | 36 +++++++++++++++++++++++++++++++++++-
  1 file changed, 35 insertions(+), 1 deletion(-)
drivers/iio/adc/ad7476.c +0 -0
index 1f736be09663..fc98aadc4077 100644

...

Quoted 10 lines

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 82

Quoted 8 lines
drivers/iio/adc/ad7476.c +8 -0
+
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;
+
+	gpiod_set_value(st->convst_gpio, 1);
+	udelay(1); /* 10ns required for conversion */

So ndelay(10)?

Thanks for pointing this out. This delay was something I thought I must clarify! This 10nS comment got just copied from the existing convstart, it probably is wrong for the bd79105.

...

Quoted 14 lines

drivers/iio/adc/ad7476.c static const struct spi_device_id ad7476_id[] = { · line 432

Quoted 11 lines
  	st->xfer.rx_buf = &st->data;
  	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
drivers/iio/adc/ad7476.c +1 -0
@@ -393,6 +426,7 @@ static const struct spi_device_id ad7476_id[] = {
  	{ "ads7866", (kernel_ulong_t)&ads7866_chip_info },
  	{ "ads7867", (kernel_ulong_t)&ads7867_chip_info },
  	{ "ads7868", (kernel_ulong_t)&ads7868_chip_info },
+	{ "bd79105", (kernel_ulong_t)&bd79105_chip_info },
  	/*
  	 * The ROHM BU79100G is identical to the TI's ADS7866 from the software
  	 * point of view. The binding document mandates the ADS7866 to be

Unrelated to this patch, but interesting that we don't also have an of_ lookup table.

I am not sure what is the value of having the of_match table with the SPI devices. The SPI-ID will in any case be required for the module loading, and it can be built based on the DT compatible.

I admit I don't really know all the dirty details but, from what I can say, the only potential use would be if this driver supported two variants (which needed to be distinguished) with identical chip-compatible but different vendor part. I accept all education (also) on this matter though :)

Yours,
-- Matti

#16 lore raw

Re: [PATCH 7/8] iio: adc: ad7476: Support ROHM BD79105

In reply to Matti Vaittinen
Quoted 19 lines · Matti Vaittinen

The ROHM BD79105 is a simple 16-bit ADC accessible via SPI*.

The BD79105 has a CONVSTART pin, which must be set high to start the ADC conversion. Unlike with the ad7091 and ad7091r which also have a CONVSTART pin, the BD79105 requires that the pin must remain high also for the duration of the SPI access.

(*) Couple of words about the SPI. The BD79105 has pins named as CONVSTART, SCLK, DIN and DOUT. For the curious reader, DIN is not SPI ISO.

DIN is a signal which can be used as a chip-select. When DIN is pulled low, the ADC will output the completed measurement via DOUT as SCLK is clocked. According to the data-sheet, the DIN can also be used for daisy-chaining multiple ADCs. Also, DOUT can be used also for a 'data-ready' -IRQ. These modes aren't supported by this driver.

Support reading ADC scale and data from the BD79105 using SPI, when DIN is used as a chip-select.

...

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 71

Quoted 4 lines
drivers/iio/adc/ad7476.c +4 -0
+static void bd79105_convst_disable(struct ad7476_state *st)
+{
+	if (!st->convst_gpio)
+		return;

Dup code, please remove.

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 79

Quoted 8 lines
drivers/iio/adc/ad7476.c +7 -0
+	gpiod_set_value(st->convst_gpio, 0);
+}
+
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
 
+	if (!st->convst_gpio)
+		return;

With 10ns sleep in mind this is also unneeded check.

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 82

Quoted 3 lines
drivers/iio/adc/ad7476.c +2 -0
+	gpiod_set_value(st->convst_gpio, 1);
 
+	udelay(1); /* 10ns required for conversion */

We have ndelay(). But I believe toggling GPIO is much longer operation.

Quoted 1 line

+}

...

drivers/iio/adc/ad7476.c static int ad7476_probe(struct spi_device *spi) · line 385

Quoted 7 lines
drivers/iio/adc/ad7476.c +0 -1
@@ -347,7 +381,6 @@ static int ad7476_probe(struct spi_device *spi)
 	if (st->convst_gpio)
 		indio_dev->channels = st->chip_info->convst_channel;
 	/* Setup default message */
-
 	st->xfer.rx_buf = &st->data;
 	st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;

Stray change.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author maintainer
#17 lore raw

Re: [PATCH 7/8] iio: adc: ad7476: Support ROHM BD79105

Quoted 32 lines · Andy Shevchenko
Quoted 19 lines · Matti Vaittinen

The ROHM BD79105 is a simple 16-bit ADC accessible via SPI*.

The BD79105 has a CONVSTART pin, which must be set high to start the ADC conversion. Unlike with the ad7091 and ad7091r which also have a CONVSTART pin, the BD79105 requires that the pin must remain high also for the duration of the SPI access.

(*) Couple of words about the SPI. The BD79105 has pins named as CONVSTART, SCLK, DIN and DOUT. For the curious reader, DIN is not SPI ISO.

DIN is a signal which can be used as a chip-select. When DIN is pulled low, the ADC will output the completed measurement via DOUT as SCLK is clocked. According to the data-sheet, the DIN can also be used for daisy-chaining multiple ADCs. Also, DOUT can be used also for a 'data-ready' -IRQ. These modes aren't supported by this driver.

Support reading ADC scale and data from the BD79105 using SPI, when DIN is used as a chip-select.

...

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 79

Quoted 6 lines
drivers/iio/adc/ad7476.c +5 -0
+
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
 
+	if (!st->convst_gpio)
+		return;

With 10ns sleep in mind this is also unneeded check.

Quoted 5 lines

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 82

Quoted 3 lines
drivers/iio/adc/ad7476.c +2 -0
+	gpiod_set_value(st->convst_gpio, 1);
 
+	udelay(1); /* 10ns required for conversion */

We have ndelay(). But I believe toggling GPIO is much longer operation.

Thanks for the review Andy.

As I replied to David, this 10nS is rubbish. I need to clarify the right value.

Yours,
-- Matti

#18 lore raw

Re: [PATCH 7/8] iio: adc: ad7476: Support ROHM BD79105

Quoted 2 lines · Matti Vaittinen
Quoted 1 line · Andy Shevchenko

On Wed, Aug 06, 2025 at 10:04:43AM +0300, Matti Vaittinen wrote:

...

Quoted 10 lines
Quoted 10 lines

drivers/iio/adc/ad7476.c static void ad7091_convst(struct ad7476_state *st) · line 79

Quoted 5 lines
drivers/iio/adc/ad7476.c +4 -0
+static void bd79105_convst_enable(struct ad7476_state *st)
+{
 
+    if (!st->convst_gpio)
+            return;

With 10ns sleep in mind this is also unneeded check.

Quoted 1 line

+ gpiod_set_value(st->convst_gpio, 1);

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

+ udelay(1); /* 10ns required for conversion */

We have ndelay(). But I believe toggling GPIO is much longer operation.

Thanks for the review Andy.

As I replied to David, this 10nS is rubbish. I need to clarify the right value.

Then probably you want to call fsleep(), and if the value is few / dozens of useconds, I would drop the check for GPIO and leave with a small delay.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author maintainer
#19 lore raw

PATCH 8/8 MAINTAINERS: A driver for simple 1-channel SPI ADCs

In reply to Matti Vaittinen

Add undersigned as a maintainer for the ad7476.c which supports a few simple 1-channel ADC connected to SPI.

---
I'll try to keep this on eye.

I only have access to the ROHM BD79105 and BU79100g. I would welcome anyone with access to other supported ADCs (and time, energy and the knowledge) to join me. :)

---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)
MAINTAINERS +5 -0
index f8c8f682edf6..36fa6333f7b5 100644
@@ -455,6 +455,11 @@ F:	Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
 F:	Documentation/iio/ad7380.rst
 F:	drivers/iio/adc/ad7380.c
 
+AD7476 ADC DRIVER FOR VARIOUS SIMPLE 1-CHANNEL SPI ADCs
+M:	Matti Vaittinen <[email protected]>
+S:	Maintained
+F:	drivers/iio/adc/ad7476.c
+
 AD7877 TOUCHSCREEN DRIVER
 M:	Michael Hennerich <[email protected]>
 S:	Supported
Signature · 1 line
2.50.1