All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging.
@ 2022-04-18 19:28 Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 Jonathan Cameron
                   ` (16 more replies)
  0 siblings, 17 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

This started out as an experiment with Vincent Whitchurch's roadtest
testing framework [1] and that worked well so I carried on cleaning up the
driver.

Mostly this is standard tidy up, move to new interfaces, then move the driver
out of staging, but there are a few other things in here.

1) Precision improvement for IIO_VAL_FRACTIONAL_LOG2.
   The ad7746 is a 24 bit sensor and this highlighted that 9 decimal
   places wasn't enough to keep the error introduced by
   _raw * _scale from growing too large over the whole range (I couldn't
   write a sensible test for it).  So I've proposed increasing the precision
   of the calculation used to provide the sysfs attributes with this value
   type and printing a few more decimal places (12).
2) _inputoffset ABI addition.  This driver had an odd use of _offset for
   the case of differential channels that applied the offset to both
   of the differential pair (hence usespace shouldn't not apply it when
   converting to the base units. That isn't inline with the existing
   documentation for _offset and it wasn't clear to me that it made sense
   at all.  To avoid confusion I've added this new ABI (_inputoffset) for t
3) roadtest file - note this is not a complete test set for the driver and
   mainly focused on the main channel reads and places I thought I might
   have broken things whilst working on the driver.

My conclusion on roadtest - Very useful indeed. I'd encourage others to
consider developing some basic sanity tests for drivers they are working on.
Hopefully my python code isn't too hideous to understand at least!
Vincent, it might be worth thinking about some generic code to handle the
'variants' on correct ABI like I introduce here because I switched from
a shared by type scale to an individual one per channel for the voltages.
Both were ABI compliant so that sort of change is fine most of the time
though we have to be careful with it.

All comments welcome.  Note there may be changes that make more sense
to do after moving this out of staging as long as there are no ABI changes involved
etc.  Feel free to highlight those sorts of changes as well as anything more
significant.

[1] https://lore.kernel.org/all/20220311162445.346685-9-vincent.whitchurch@axis.com/

Jonathan Cameron (17):
  iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2
  iio: ABI: Fix wrong format of differential capacitance channel ABI.
  staging: iio: cdc: ad7746: Use explicit be24 handling.
  staging: iio: cdc: ad7746: Push handling of supply voltage scale to
    userspace.
  staging: iio: cdc: ad7746: Use local buffer for multi byte reads.
  staging: iio: cdc: ad7746: Factor out ad7746_read_channel()
  staging: iio: cdc: ad7764: Push locking down into case statements in
    read/write_raw
  staging: iio: cdc: ad7746: Break up use of chan->address and use
    FIELD_PREP etc
  staging: iio: cdc: ad7746: Drop usused i2c_set_clientdata()
  staging: iio: cdc: ad7746: Use _raw and _scale for temperature
    channels.
  iio: core: Introduce _inputoffset for differential channels
  staging: iio: cdc: ad7746: Switch from _offset to _inputoffset for
    differential channels.
  staging: iio: cdc: ad7746: Use read_avail() rather than opencoding.
  staging: iio: ad7746: White space cleanup
  iio: cdc: ad7746: Add device specific ABI documentation.
  iio: cdc: ad7746: Move driver out of staging.
  RFC: iio: cdc: ad7746: Add roadtest

 Documentation/ABI/testing/sysfs-bus-iio       |  15 +-
 .../ABI/testing/sysfs-bus-iio-cdc-ad7746      |  11 +
 drivers/iio/cdc/Kconfig                       |  10 +
 drivers/iio/cdc/Makefile                      |   1 +
 drivers/iio/cdc/ad7746.c                      | 818 ++++++++++++++++++
 drivers/iio/industrialio-core.c               |  24 +-
 drivers/staging/iio/cdc/ad7746.c              | 767 ----------------
 include/linux/iio/types.h                     |   1 +
 .../roadtest/tests/iio/cdc/__init__.py        |   0
 .../roadtest/roadtest/tests/iio/cdc/config    |   1 +
 .../roadtest/tests/iio/cdc/test_ad7746.py     | 323 +++++++
 11 files changed, 1195 insertions(+), 776 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746
 create mode 100644 drivers/iio/cdc/ad7746.c
 delete mode 100644 drivers/staging/iio/cdc/ad7746.c
 create mode 100644 tools/testing/roadtest/roadtest/tests/iio/cdc/__init__.py
 create mode 100644 tools/testing/roadtest/roadtest/tests/iio/cdc/config
 create mode 100644 tools/testing/roadtest/roadtest/tests/iio/cdc/test_ad7746.py

-- 
2.35.3


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-26 12:00   ` Peter Rosin
  2022-04-18 19:28 ` [PATCH 02/17] iio: ABI: Fix wrong format of differential capacitance channel ABI Jonathan Cameron
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

With some high resolution sensors such as the ad7746 the
build up of error when multiplying the _raw and _scale
values together can be significant.  Reduce this affect by
providing additional resolution in both calculation and
formatting of result.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2f48e9a97274..d831835770da 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -683,14 +683,21 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
 		else
 			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
 					     abs(tmp1));
-	case IIO_VAL_FRACTIONAL_LOG2:
-		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
-		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
-		if (tmp0 == 0 && tmp2 < 0)
-			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
-		else
-			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
-					     abs(tmp1));
+	case IIO_VAL_FRACTIONAL_LOG2: {
+		u64 t1, t2;
+		int integer;
+		bool neg = vals[0] < 0;
+
+		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);
+		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
+		if (integer == 0 && neg)
+			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
+		if (neg)
+			integer *= -1;
+		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
+				     abs(t2));
+		}
+	}
 	case IIO_VAL_INT_MULTIPLE:
 	{
 		int i;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 02/17] iio: ABI: Fix wrong format of differential capacitance channel ABI.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling Jonathan Cameron
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

in_ only occurs once in these attributes.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index d4ccc68fdcf0..b19ff517e5d6 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -188,7 +188,7 @@ Description:
 		Raw capacitance measurement from channel Y. Units after
 		application of scale and offset are nanofarads.
 
-What:		/sys/.../iio:deviceX/in_capacitanceY-in_capacitanceZ_raw
+What:		/sys/.../iio:deviceX/in_capacitanceY-capacitanceZ_raw
 KernelVersion:	3.2
 Contact:	linux-iio@vger.kernel.org
 Description:
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 02/17] iio: ABI: Fix wrong format of differential capacitance channel ABI Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-26 10:58   ` Andy Shevchenko
  2022-04-18 19:28 ` [PATCH 04/17] staging: iio: cdc: ad7746: Push handling of supply voltage scale to userspace Jonathan Cameron
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Change from fiddly local implementation of be24 to cpu endian conversion
by reading into a 3 byte buffer and using get_unaligned_be24()

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 71c709771676..1f510610ed32 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -95,10 +95,7 @@ struct ad7746_chip_info {
 	u8	capdac[2][2];
 	s8	capdac_set;
 
-	union {
-		__be32 d32;
-		u8 d8[4];
-	} data ____cacheline_aligned;
+	u8 data[3] ____cacheline_aligned;
 };
 
 enum ad7746_chan {
@@ -547,12 +544,12 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 
 		ret = i2c_smbus_read_i2c_block_data(chip->client,
 						    chan->address >> 8, 3,
-						    &chip->data.d8[1]);
+						    chip->data);
 
 		if (ret < 0)
 			goto out;
 
-		*val = (be32_to_cpu(chip->data.d32) & 0xFFFFFF) - 0x800000;
+		*val = get_unaligned_be24(chip->data) - 0x800000;
 
 		switch (chan->type) {
 		case IIO_TEMP:
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 04/17] staging: iio: cdc: ad7746: Push handling of supply voltage scale to userspace.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (2 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 05/17] staging: iio: cdc: ad7746: Use local buffer for multi byte reads Jonathan Cameron
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

The supply voltage is attenuated by 6 before being fed to the ADC.
Handle this explcitly rather than premultiplying the _raw value by 6.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 1f510610ed32..1379caf88b83 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -114,9 +114,8 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.type = IIO_VOLTAGE,
 		.indexed = 1,
 		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
-			BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = AD7746_REG_VT_DATA_HIGH << 8 |
 			AD7746_VTSETUP_VTMD_EXT_VIN,
 	},
@@ -125,9 +124,8 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.indexed = 1,
 		.channel = 1,
 		.extend_name = "supply",
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
-			BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = AD7746_REG_VT_DATA_HIGH << 8 |
 			AD7746_VTSETUP_VTMD_VDD_MON,
 	},
@@ -559,10 +557,6 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 			 */
 			*val = (*val * 125) / 256;
 			break;
-		case IIO_VOLTAGE:
-			if (chan->channel == 1) /* supply_raw*/
-				*val = *val * 6;
-			break;
 		default:
 			break;
 		}
@@ -617,6 +611,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 		case IIO_VOLTAGE:
 			/* 1170mV / 2^23 */
 			*val = 1170;
+			if (chan->channel == 1)
+				*val *= 6;
 			*val2 = 23;
 			ret = IIO_VAL_FRACTIONAL_LOG2;
 			break;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 05/17] staging: iio: cdc: ad7746: Use local buffer for multi byte reads.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (3 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 04/17] staging: iio: cdc: ad7746: Push handling of supply voltage scale to userspace Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 06/17] staging: iio: cdc: ad7746: Factor out ad7746_read_channel() Jonathan Cameron
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

I2C does not require DMA safe buffers so there is no need to ensure
the buffers are in their own cacheline. Hence simplify things by
using a local variable instead of embedding the buffer in the chip
info structure.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 1379caf88b83..e4b882586891 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -94,8 +94,6 @@ struct ad7746_chip_info {
 	u8	vt_setup;
 	u8	capdac[2][2];
 	s8	capdac_set;
-
-	u8 data[3] ____cacheline_aligned;
 };
 
 enum ad7746_chan {
@@ -520,6 +518,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 	struct ad7746_chip_info *chip = iio_priv(indio_dev);
 	int ret, delay, idx;
 	u8 regval, reg;
+	u8 data[3];
 
 	mutex_lock(&chip->lock);
 
@@ -542,12 +541,12 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 
 		ret = i2c_smbus_read_i2c_block_data(chip->client,
 						    chan->address >> 8, 3,
-						    chip->data);
+						    data);
 
 		if (ret < 0)
 			goto out;
 
-		*val = get_unaligned_be24(chip->data) - 0x800000;
+		*val = get_unaligned_be24(data) - 0x800000;
 
 		switch (chan->type) {
 		case IIO_TEMP:
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 06/17] staging: iio: cdc: ad7746: Factor out ad7746_read_channel()
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (4 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 05/17] staging: iio: cdc: ad7746: Use local buffer for multi byte reads Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 07/17] staging: iio: cdc: ad7764: Push locking down into case statements in read/write_raw Jonathan Cameron
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Reduce deep indenting and simplify the locking cleanup that follows.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 79 ++++++++++++++++----------------
 1 file changed, 40 insertions(+), 39 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index e4b882586891..6b9530bd12cc 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -500,14 +500,47 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 		default:
 			ret = -EINVAL;
 		}
+static int ad7746_read_channel(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int *val)
+{
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	int ret, delay;
+	u8 data[3];
+	u8 regval;
+
+	ret = ad7746_select_channel(indio_dev, chan);
+	if (ret < 0)
+		return ret;
+	delay = ret;
+
+	regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV;
+	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+	if (ret < 0)
+		return ret;
+
+	msleep(delay);
+	/* Now read the actual register */
+	ret = i2c_smbus_read_i2c_block_data(chip->client,  chan->address >> 8,
+					    3, data);
+	if (ret < 0)
+		return ret;
+
+	*val = get_unaligned_be24(data) - 0x800000;
+
+	switch (chan->type) {
+	case IIO_TEMP:
+		/*
+		 * temperature in milli degrees Celsius
+		 * T = ((*val / 2048) - 4096) * 1000
+		 */
+		*val = (*val  * 125) / 256;
 		break;
 	default:
-		ret = -EINVAL;
+		break;
 	}
 
-out:
-	mutex_unlock(&chip->lock);
-	return ret;
+	return 0;
 }
 
 static int ad7746_read_raw(struct iio_dev *indio_dev,
@@ -516,49 +549,17 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 			   long mask)
 {
 	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	int ret, delay, idx;
-	u8 regval, reg;
-	u8 data[3];
+	int ret, idx;
+	u8 reg;
 
 	mutex_lock(&chip->lock);
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 	case IIO_CHAN_INFO_PROCESSED:
-		ret = ad7746_select_channel(indio_dev, chan);
+		ret = ad7746_read_channel(indio_dev, chan, val);
 		if (ret < 0)
 			goto out;
-		delay = ret;
-
-		regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV;
-		ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG,
-						regval);
-		if (ret < 0)
-			goto out;
-
-		msleep(delay);
-		/* Now read the actual register */
-
-		ret = i2c_smbus_read_i2c_block_data(chip->client,
-						    chan->address >> 8, 3,
-						    data);
-
-		if (ret < 0)
-			goto out;
-
-		*val = get_unaligned_be24(data) - 0x800000;
-
-		switch (chan->type) {
-		case IIO_TEMP:
-			/*
-			 * temperature in milli degrees Celsius
-			 * T = ((*val / 2048) - 4096) * 1000
-			 */
-			*val = (*val * 125) / 256;
-			break;
-		default:
-			break;
-		}
 
 		ret = IIO_VAL_INT;
 		break;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 07/17] staging: iio: cdc: ad7764: Push locking down into case statements in read/write_raw
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (5 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 06/17] staging: iio: cdc: ad7746: Factor out ad7746_read_channel() Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 08/17] staging: iio: cdc: ad7746: Break up use of chan->address and use FIELD_PREP etc Jonathan Cameron
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Not all paths require any locking at all. So to simplify the
removal of such locking push the locks down into the individual
case statements.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 125 ++++++++++++++-----------------
 1 file changed, 58 insertions(+), 67 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 6b9530bd12cc..34e19677d1fe 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -418,14 +418,10 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 	struct ad7746_chip_info *chip = iio_priv(indio_dev);
 	int ret, reg;
 
-	mutex_lock(&chip->lock);
-
 	switch (mask) {
 	case IIO_CHAN_INFO_CALIBSCALE:
-		if (val != 1) {
-			ret = -EINVAL;
-			goto out;
-		}
+		if (val != 1)
+			return -EINVAL;
 
 		val = (val2 * 1024) / 15625;
 
@@ -437,33 +433,31 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 			reg = AD7746_REG_VOLT_GAINH;
 			break;
 		default:
-			ret = -EINVAL;
-			goto out;
+			return -EINVAL;
 		}
 
+		mutex_lock(&chip->lock);
 		ret = i2c_smbus_write_word_swapped(chip->client, reg, val);
+		mutex_unlock(&chip->lock);
 		if (ret < 0)
-			goto out;
+			return ret;
 
-		ret = 0;
-		break;
+		return 0;
 	case IIO_CHAN_INFO_CALIBBIAS:
-		if (val < 0 || val > 0xFFFF) {
-			ret = -EINVAL;
-			goto out;
-		}
+		if (val < 0 || val > 0xFFFF)
+			return -EINVAL;
+
+		mutex_lock(&chip->lock);
 		ret = i2c_smbus_write_word_swapped(chip->client,
 						   AD7746_REG_CAP_OFFH, val);
+		mutex_unlock(&chip->lock);
 		if (ret < 0)
-			goto out;
+			return ret;
 
-		ret = 0;
-		break;
+		return 0;
 	case IIO_CHAN_INFO_OFFSET:
-		if (val < 0 || val > 43008000) { /* 21pF */
-			ret = -EINVAL;
-			goto out;
-		}
+		if (val < 0 || val > 43008000) /* 21pF */
+			return -EINVAL;
 
 		/*
 		 * CAPDAC Scale = 21pF_typ / 127
@@ -472,34 +466,43 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 		 */
 
 		val /= 338646;
-
+		mutex_lock(&chip->lock);
 		chip->capdac[chan->channel][chan->differential] = val > 0 ?
 			AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0;
 
 		ret = ad7746_set_capdac(chip, chan->channel);
-		if (ret < 0)
-			goto out;
+		if (ret < 0) {
+			mutex_unlock(&chip->lock);
+			return ret;
+		}
 
 		chip->capdac_set = chan->channel;
+		mutex_unlock(&chip->lock);
 
-		ret = 0;
-		break;
+		return 0;
 	case IIO_CHAN_INFO_SAMP_FREQ:
-		if (val2) {
-			ret = -EINVAL;
-			goto out;
-		}
+		if (val2)
+			return -EINVAL;
 
 		switch (chan->type) {
 		case IIO_CAPACITANCE:
+			mutex_lock(&chip->lock);
 			ret = ad7746_store_cap_filter_rate_setup(chip, val);
-			break;
+			mutex_unlock(&chip->lock);
+			return ret;
 		case IIO_VOLTAGE:
+			mutex_lock(&chip->lock);
 			ret = ad7746_store_vt_filter_rate_setup(chip, val);
-			break;
+			mutex_unlock(&chip->lock);
+			return ret;
 		default:
-			ret = -EINVAL;
+			return -EINVAL;
 		}
+	default:
+		return -EINVAL;
+	}
+}
+
 static int ad7746_read_channel(struct iio_dev *indio_dev,
 			       struct iio_chan_spec const *chan,
 			       int *val)
@@ -552,17 +555,16 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 	int ret, idx;
 	u8 reg;
 
-	mutex_lock(&chip->lock);
-
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 	case IIO_CHAN_INFO_PROCESSED:
+		mutex_lock(&chip->lock);
 		ret = ad7746_read_channel(indio_dev, chan, val);
+		mutex_unlock(&chip->lock);
 		if (ret < 0)
-			goto out;
+			return ret;
 
-		ret = IIO_VAL_INT;
-		break;
+		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_CALIBSCALE:
 		switch (chan->type) {
 		case IIO_CAPACITANCE:
@@ -572,80 +574,69 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 			reg = AD7746_REG_VOLT_GAINH;
 			break;
 		default:
-			ret = -EINVAL;
-			goto out;
+			return -EINVAL;
 		}
 
+		mutex_lock(&chip->lock);
 		ret = i2c_smbus_read_word_swapped(chip->client, reg);
+		mutex_unlock(&chip->lock);
 		if (ret < 0)
-			goto out;
+			return ret;
 		/* 1 + gain_val / 2^16 */
 		*val = 1;
 		*val2 = (15625 * ret) / 1024;
 
-		ret = IIO_VAL_INT_PLUS_MICRO;
-		break;
+		return IIO_VAL_INT_PLUS_MICRO;
 	case IIO_CHAN_INFO_CALIBBIAS:
+		mutex_lock(&chip->lock);
 		ret = i2c_smbus_read_word_swapped(chip->client,
 						  AD7746_REG_CAP_OFFH);
+		mutex_unlock(&chip->lock);
 		if (ret < 0)
-			goto out;
+			return ret;
 		*val = ret;
 
-		ret = IIO_VAL_INT;
-		break;
+		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_OFFSET:
 		*val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel]
 					  [chan->differential]) * 338646;
 
-		ret = IIO_VAL_INT;
-		break;
+		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		switch (chan->type) {
 		case IIO_CAPACITANCE:
 			/* 8.192pf / 2^24 */
 			*val =  0;
 			*val2 = 488;
-			ret = IIO_VAL_INT_PLUS_NANO;
-			break;
+			return IIO_VAL_INT_PLUS_NANO;
 		case IIO_VOLTAGE:
 			/* 1170mV / 2^23 */
 			*val = 1170;
 			if (chan->channel == 1)
 				*val *= 6;
 			*val2 = 23;
-			ret = IIO_VAL_FRACTIONAL_LOG2;
-			break;
+			return IIO_VAL_FRACTIONAL_LOG2;
 		default:
-			ret = -EINVAL;
-			break;
+			return -EINVAL;
 		}
-
-		break;
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		switch (chan->type) {
 		case IIO_CAPACITANCE:
 			idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
 				AD7746_CONF_CAPFS_SHIFT;
 			*val = ad7746_cap_filter_rate_table[idx][0];
-			ret = IIO_VAL_INT;
-			break;
+			return IIO_VAL_INT;
 		case IIO_VOLTAGE:
 			idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
 				AD7746_CONF_VTFS_SHIFT;
 			*val = ad7746_vt_filter_rate_table[idx][0];
-			ret = IIO_VAL_INT;
-			break;
+			return IIO_VAL_INT;
 		default:
-			ret = -EINVAL;
+			return -EINVAL;
 		}
-		break;
 	default:
-		ret = -EINVAL;
+		return -EINVAL;
 	}
-out:
-	mutex_unlock(&chip->lock);
-	return ret;
 }
 
 static const struct iio_info ad7746_info = {
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 08/17] staging: iio: cdc: ad7746: Break up use of chan->address and use FIELD_PREP etc
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (6 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 07/17] staging: iio: cdc: ad7764: Push locking down into case statements in read/write_raw Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:28 ` [PATCH 09/17] staging: iio: cdc: ad7746: Drop usused i2c_set_clientdata() Jonathan Cameron
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Instead of encoding several different fields into chan->address use
an indirection to a separate per channel structure where the various
fields can be expressed in a more readable form.  This also allows
the register values to be constructed at runtime using FIELD_PREP()

Drop the now redundant _SHIFT macros.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 152 ++++++++++++++++++++-----------
 1 file changed, 99 insertions(+), 53 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 34e19677d1fe..bbb7b7db48a2 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -5,6 +5,7 @@
  * Copyright 2011 Analog Devices Inc.
  */
 
+#include <linux/bitfield.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/i2c.h>
@@ -48,11 +49,12 @@
 #define AD7746_CAPSETUP_CACHOP		BIT(0)
 
 /* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
-#define AD7746_VTSETUP_VTEN		(1 << 7)
-#define AD7746_VTSETUP_VTMD_INT_TEMP	(0 << 5)
-#define AD7746_VTSETUP_VTMD_EXT_TEMP	(1 << 5)
-#define AD7746_VTSETUP_VTMD_VDD_MON	(2 << 5)
-#define AD7746_VTSETUP_VTMD_EXT_VIN	(3 << 5)
+#define AD7746_VTSETUP_VTEN		BIT(7)
+#define AD7746_VTSETUP_VTMD_MASK	GENMASK(6, 5)
+#define AD7746_VTSETUP_VTMD_INT_TEMP	0
+#define AD7746_VTSETUP_VTMD_EXT_TEMP	1
+#define AD7746_VTSETUP_VTMD_VDD_MON	2
+#define AD7746_VTSETUP_VTMD_EXT_VIN	3
 #define AD7746_VTSETUP_EXTREF		BIT(4)
 #define AD7746_VTSETUP_VTSHORT		BIT(1)
 #define AD7746_VTSETUP_VTCHOP		BIT(0)
@@ -64,23 +66,22 @@
 #define AD7746_EXCSETUP_NEXCB		BIT(4)
 #define AD7746_EXCSETUP_EXCA		BIT(3)
 #define AD7746_EXCSETUP_NEXCA		BIT(2)
-#define AD7746_EXCSETUP_EXCLVL(x)	(((x) & 0x3) << 0)
+#define AD7746_EXCSETUP_EXCLVL_MASK	GENMASK(1, 0)
 
 /* Config Register Bit Designations (AD7746_REG_CFG) */
-#define AD7746_CONF_VTFS_SHIFT		6
-#define AD7746_CONF_CAPFS_SHIFT		3
 #define AD7746_CONF_VTFS_MASK		GENMASK(7, 6)
 #define AD7746_CONF_CAPFS_MASK		GENMASK(5, 3)
-#define AD7746_CONF_MODE_IDLE		(0 << 0)
-#define AD7746_CONF_MODE_CONT_CONV	(1 << 0)
-#define AD7746_CONF_MODE_SINGLE_CONV	(2 << 0)
-#define AD7746_CONF_MODE_PWRDN		(3 << 0)
-#define AD7746_CONF_MODE_OFFS_CAL	(5 << 0)
-#define AD7746_CONF_MODE_GAIN_CAL	(6 << 0)
+#define AD7746_CONF_MODE_MASK		GENMASK(2, 0)
+#define AD7746_CONF_MODE_IDLE		0
+#define AD7746_CONF_MODE_CONT_CONV	1
+#define AD7746_CONF_MODE_SINGLE_CONV	2
+#define AD7746_CONF_MODE_PWRDN		3
+#define AD7746_CONF_MODE_OFFS_CAL	5
+#define AD7746_CONF_MODE_GAIN_CAL	6
 
 /* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
 #define AD7746_CAPDAC_DACEN		BIT(7)
-#define AD7746_CAPDAC_DACP(x)		((x) & 0x7F)
+#define AD7746_CAPDAC_DACP_MASK		0x7F
 
 struct ad7746_chip_info {
 	struct i2c_client *client;
@@ -107,6 +108,52 @@ enum ad7746_chan {
 	CIN2_DIFF,
 };
 
+struct ad7746_chan_info {
+	u8 addr;
+	union {
+		u8 vtmd;
+		struct { /* CAP SETUP fields */
+			unsigned int cin2 : 1;
+			unsigned int capdiff : 1;
+		};
+	};
+};
+
+static const struct ad7746_chan_info ad7746_chan_info[] = {
+	[VIN] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_EXT_VIN,
+	},
+	[VIN_VDD] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_VDD_MON,
+	},
+	[TEMP_INT] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_INT_TEMP,
+	},
+	[TEMP_EXT] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP,
+	},
+	[CIN1] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+	},
+	[CIN1_DIFF] = {
+		.addr =  AD7746_REG_CAP_DATA_HIGH,
+		.capdiff = 1,
+	},
+	[CIN2] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+		.cin2 = 1,
+	},
+	[CIN2_DIFF] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+		.cin2 = 1,
+		.capdiff = 1,
+	},
+};
+
 static const struct iio_chan_spec ad7746_channels[] = {
 	[VIN] = {
 		.type = IIO_VOLTAGE,
@@ -114,8 +161,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.channel = 0,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_VT_DATA_HIGH << 8 |
-			AD7746_VTSETUP_VTMD_EXT_VIN,
+		.address = VIN,
 	},
 	[VIN_VDD] = {
 		.type = IIO_VOLTAGE,
@@ -124,24 +170,21 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.extend_name = "supply",
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_VT_DATA_HIGH << 8 |
-			AD7746_VTSETUP_VTMD_VDD_MON,
+		.address = VIN_VDD,
 	},
 	[TEMP_INT] = {
 		.type = IIO_TEMP,
 		.indexed = 1,
 		.channel = 0,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
-		.address = AD7746_REG_VT_DATA_HIGH << 8 |
-			AD7746_VTSETUP_VTMD_INT_TEMP,
+		.address = TEMP_INT,
 	},
 	[TEMP_EXT] = {
 		.type = IIO_TEMP,
 		.indexed = 1,
 		.channel = 1,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
-		.address = AD7746_REG_VT_DATA_HIGH << 8 |
-			AD7746_VTSETUP_VTMD_EXT_TEMP,
+		.address = TEMP_EXT,
 	},
 	[CIN1] = {
 		.type = IIO_CAPACITANCE,
@@ -151,7 +194,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_CAP_DATA_HIGH << 8,
+		.address = CIN1,
 	},
 	[CIN1_DIFF] = {
 		.type = IIO_CAPACITANCE,
@@ -163,8 +206,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_CAP_DATA_HIGH << 8 |
-			AD7746_CAPSETUP_CAPDIFF
+		.address = CIN1_DIFF,
 	},
 	[CIN2] = {
 		.type = IIO_CAPACITANCE,
@@ -174,8 +216,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_CAP_DATA_HIGH << 8 |
-			AD7746_CAPSETUP_CIN2,
+		.address = CIN2,
 	},
 	[CIN2_DIFF] = {
 		.type = IIO_CAPACITANCE,
@@ -187,8 +228,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = AD7746_REG_CAP_DATA_HIGH << 8 |
-			AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2,
+		.address = CIN2_DIFF,
 	}
 };
 
@@ -224,10 +264,13 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
 
 	switch (chan->type) {
 	case IIO_CAPACITANCE:
-		cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN;
+		cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2,
+				       ad7746_chan_info[chan->address].cin2) |
+			FIELD_PREP(AD7746_CAPSETUP_CAPDIFF,
+				   ad7746_chan_info[chan->address].capdiff) |
+			FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1);
 		vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
-		idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
-			AD7746_CONF_CAPFS_SHIFT;
+		idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
 		delay = ad7746_cap_filter_rate_table[idx][1];
 
 		ret = ad7746_set_capdac(chip, chan->channel);
@@ -239,10 +282,11 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
 		break;
 	case IIO_VOLTAGE:
 	case IIO_TEMP:
-		vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN;
+		vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK,
+				      ad7746_chan_info[chan->address].vtmd) |
+			FIELD_PREP(AD7746_VTSETUP_VTEN, 1);
 		cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
-		idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
-			AD7746_CONF_VTFS_SHIFT;
+		idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
 		delay = ad7746_cap_filter_rate_table[idx][1];
 		break;
 	default:
@@ -325,7 +369,8 @@ static ssize_t ad7746_start_offset_calib(struct device *dev,
 		return ret;
 
 	return ad7746_start_calib(dev, attr, buf, len,
-				  AD7746_CONF_MODE_OFFS_CAL);
+				  FIELD_PREP(AD7746_CONF_MODE_MASK,
+					     AD7746_CONF_MODE_OFFS_CAL));
 }
 
 static ssize_t ad7746_start_gain_calib(struct device *dev,
@@ -340,7 +385,8 @@ static ssize_t ad7746_start_gain_calib(struct device *dev,
 		return ret;
 
 	return ad7746_start_calib(dev, attr, buf, len,
-				  AD7746_CONF_MODE_GAIN_CAL);
+				  FIELD_PREP(AD7746_CONF_MODE_MASK,
+					     AD7746_CONF_MODE_GAIN_CAL));
 }
 
 static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
@@ -367,7 +413,7 @@ static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
 		i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
 
 	chip->config &= ~AD7746_CONF_CAPFS_MASK;
-	chip->config |= i << AD7746_CONF_CAPFS_SHIFT;
+	chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i);
 
 	return 0;
 }
@@ -385,7 +431,7 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
 		i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
 
 	chip->config &= ~AD7746_CONF_VTFS_MASK;
-	chip->config |= i << AD7746_CONF_VTFS_SHIFT;
+	chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i);
 
 	return 0;
 }
@@ -468,7 +514,7 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 		val /= 338646;
 		mutex_lock(&chip->lock);
 		chip->capdac[chan->channel][chan->differential] = val > 0 ?
-			AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0;
+			FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0;
 
 		ret = ad7746_set_capdac(chip, chan->channel);
 		if (ret < 0) {
@@ -517,14 +563,16 @@ static int ad7746_read_channel(struct iio_dev *indio_dev,
 		return ret;
 	delay = ret;
 
-	regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV;
+	regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK,
+					   AD7746_CONF_MODE_SINGLE_CONV);
 	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
 	if (ret < 0)
 		return ret;
 
 	msleep(delay);
 	/* Now read the actual register */
-	ret = i2c_smbus_read_i2c_block_data(chip->client,  chan->address >> 8,
+	ret = i2c_smbus_read_i2c_block_data(chip->client,
+					    ad7746_chan_info[chan->address].addr,
 					    3, data);
 	if (ret < 0)
 		return ret;
@@ -598,8 +646,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_OFFSET:
-		*val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel]
-					  [chan->differential]) * 338646;
+		*val = FIELD_GET(AD7746_CAPDAC_DACP_MASK,
+				 chip->capdac[chan->channel][chan->differential]) * 338646;
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
@@ -622,13 +670,11 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		switch (chan->type) {
 		case IIO_CAPACITANCE:
-			idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
-				AD7746_CONF_CAPFS_SHIFT;
+			idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
 			*val = ad7746_cap_filter_rate_table[idx][0];
 			return IIO_VAL_INT;
 		case IIO_VOLTAGE:
-			idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
-				AD7746_CONF_VTFS_SHIFT;
+			idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
 			*val = ad7746_vt_filter_rate_table[idx][0];
 			return IIO_VAL_INT;
 		default:
@@ -694,16 +740,16 @@ static int ad7746_probe(struct i2c_client *client,
 	if (!ret) {
 		switch (vdd_permille) {
 		case 125:
-			regval |= AD7746_EXCSETUP_EXCLVL(0);
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0);
 			break;
 		case 250:
-			regval |= AD7746_EXCSETUP_EXCLVL(1);
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1);
 			break;
 		case 375:
-			regval |= AD7746_EXCSETUP_EXCLVL(2);
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2);
 			break;
 		case 500:
-			regval |= AD7746_EXCSETUP_EXCLVL(3);
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3);
 			break;
 		default:
 			break;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 09/17] staging: iio: cdc: ad7746: Drop usused i2c_set_clientdata()
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (7 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 08/17] staging: iio: cdc: ad7746: Break up use of chan->address and use FIELD_PREP etc Jonathan Cameron
@ 2022-04-18 19:28 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 10/17] staging: iio: cdc: ad7746: Use _raw and _scale for temperature channels Jonathan Cameron
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:28 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

As the comment states, this was only used in remove() and now
there is no explicit remove() function to make use of it.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index bbb7b7db48a2..f6b54e817973 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -706,8 +706,6 @@ static int ad7746_probe(struct i2c_client *client,
 		return -ENOMEM;
 	chip = iio_priv(indio_dev);
 	mutex_init(&chip->lock);
-	/* this is only used for device removal purposes */
-	i2c_set_clientdata(client, indio_dev);
 
 	chip->client = client;
 	chip->capdac_set = -1;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 10/17] staging: iio: cdc: ad7746: Use _raw and _scale for temperature channels.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (8 preceding siblings ...)
  2022-04-18 19:28 ` [PATCH 09/17] staging: iio: cdc: ad7746: Drop usused i2c_set_clientdata() Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 11/17] iio: core: Introduce _inputoffset for differential channels Jonathan Cameron
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Performing the maths to rescale a 24 bit raw reading within the driver
was resulting in precision losses.  So make that userspace's problem
by exporting the scale and letting the maths be done in userspace with
appropriate precision.  Issue identified using roadtester testing
framework.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index f6b54e817973..0dc5630d0655 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -176,14 +176,16 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.type = IIO_TEMP,
 		.indexed = 1,
 		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
 		.address = TEMP_INT,
 	},
 	[TEMP_EXT] = {
 		.type = IIO_TEMP,
 		.indexed = 1,
 		.channel = 1,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
 		.address = TEMP_EXT,
 	},
 	[CIN1] = {
@@ -579,18 +581,6 @@ static int ad7746_read_channel(struct iio_dev *indio_dev,
 
 	*val = get_unaligned_be24(data) - 0x800000;
 
-	switch (chan->type) {
-	case IIO_TEMP:
-		/*
-		 * temperature in milli degrees Celsius
-		 * T = ((*val / 2048) - 4096) * 1000
-		 */
-		*val = (*val  * 125) / 256;
-		break;
-	default:
-		break;
-	}
-
 	return 0;
 }
 
@@ -605,7 +595,6 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-	case IIO_CHAN_INFO_PROCESSED:
 		mutex_lock(&chip->lock);
 		ret = ad7746_read_channel(indio_dev, chan, val);
 		mutex_unlock(&chip->lock);
@@ -664,6 +653,10 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 				*val *= 6;
 			*val2 = 23;
 			return IIO_VAL_FRACTIONAL_LOG2;
+		case IIO_TEMP:
+			*val = 125;
+			*val2 = 8;
+			return IIO_VAL_FRACTIONAL_LOG2;
 		default:
 			return -EINVAL;
 		}
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 11/17] iio: core: Introduce _inputoffset for differential channels
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (9 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 10/17] staging: iio: cdc: ad7746: Use _raw and _scale for temperature channels Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 12/17] staging: iio: cdc: ad7746: Switch from _offset to " Jonathan Cameron
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Address an ABI gap for device where the offset of both lines in a
differential pair may be controlled so as to allow a wider range of
inputs, but without having any direct effect of the differential
measurement.

_offset cannot be used as to remain in line with existing usage,
userspace would be expected to apply it as (_raw + _offset) * scale
whereas _inputoffset is not.
Similarly calibbias is expected to tweak the measurement seen, not
the adjust the two lines of the differential pair.

Needed for in_capacitanceX-capacitanceY_inputoffset for the
AD7746 CDC driver.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

---
I''m not entirely happy with the naming on this one, so if anyone
has a better idea I'm definitely open to it!
---
 Documentation/ABI/testing/sysfs-bus-iio | 13 +++++++++++++
 drivers/iio/industrialio-core.c         |  1 +
 include/linux/iio/types.h               |  1 +
 3 files changed, 15 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index b19ff517e5d6..4fbb5a257ef5 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -199,6 +199,19 @@ Description:
 		is required is a consistent labeling.  Units after application
 		of scale and offset are nanofarads.
 
+What:		/sys/.../iio:deviceX/in_capacitableY-capacitanceZ_inputoffset
+KernelVersion:	5.19
+Contact:	linux-iio@vger.kernel.org
+Description:
+		For differential channels, this an offset that is applied
+		equally to both inputs. As the reading is of the difference
+		between the two inputs this should not be applied to the _raw
+		reading by userspace (unlike _offset) and unlike calibbias
+		it does not affect the differential value measured because
+		the effect of _inputoffset cancels out across the two inputs
+		that make up the differential pair. It is expressed with
+		the same scaling as _raw.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_temp_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_tempX_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_temp_x_raw
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index d831835770da..8aebef08ca1d 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -168,6 +168,7 @@ static const char * const iio_chan_info_postfix[] = {
 	[IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio",
 	[IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type",
 	[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
+	[IIO_CHAN_INFO_INPUTOFFSET] = "inputoffset",
 };
 /**
  * iio_device_id() - query the unique ID for the device
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index a7aa91f3a8dc..c7f79a251a14 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -63,6 +63,7 @@ enum iio_chan_info_enum {
 	IIO_CHAN_INFO_OVERSAMPLING_RATIO,
 	IIO_CHAN_INFO_THERMOCOUPLE_TYPE,
 	IIO_CHAN_INFO_CALIBAMBIENT,
+	IIO_CHAN_INFO_INPUTOFFSET,
 };
 
 #endif /* _IIO_TYPES_H_ */
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 12/17] staging: iio: cdc: ad7746: Switch from _offset to _inputoffset for differential channels.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (10 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 11/17] iio: core: Introduce _inputoffset for differential channels Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 13/17] staging: iio: cdc: ad7746: Use read_avail() rather than opencoding Jonathan Cameron
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

As this offset is applied equally to both lines of the differential
pair, _ofset should not be used. Use the new ABI _inputoffset instead
to avoid userspace software applying this value when calculating
real value = (_raw + _offset) * _scale

Also add a comment to explain why an offset of 0x800000 is applied
within the driver rather than exposed to userspace.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 0dc5630d0655..46cdddab495f 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -205,7 +205,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.channel = 0,
 		.channel2 = 2,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN1_DIFF,
@@ -227,7 +227,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.channel = 1,
 		.channel2 = 3,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN2_DIFF,
@@ -504,6 +504,7 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 
 		return 0;
 	case IIO_CHAN_INFO_OFFSET:
+	case IIO_CHAN_INFO_INPUTOFFSET:
 		if (val < 0 || val > 43008000) /* 21pF */
 			return -EINVAL;
 
@@ -579,6 +580,10 @@ static int ad7746_read_channel(struct iio_dev *indio_dev,
 	if (ret < 0)
 		return ret;
 
+	/*
+	 * Offset applied internally becaue the _offset userspace interface is
+	 * needed for the CAP DACs which apply a controllable offset.
+	 */
 	*val = get_unaligned_be24(data) - 0x800000;
 
 	return 0;
@@ -635,6 +640,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_OFFSET:
+	case IIO_CHAN_INFO_INPUTOFFSET:
 		*val = FIELD_GET(AD7746_CAPDAC_DACP_MASK,
 				 chip->capdac[chan->channel][chan->differential]) * 338646;
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 13/17] staging: iio: cdc: ad7746: Use read_avail() rather than opencoding.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (11 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 12/17] staging: iio: cdc: ad7746: Switch from _offset to " Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 14/17] staging: iio: ad7746: White space cleanup Jonathan Cameron
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Switch over to the IIO core handling for _available attributes
making them available for in kernel users and enforcing correct
naming etc automatically.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 39 +++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 46cdddab495f..6fe3221c8ace 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -161,6 +161,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.channel = 0,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = VIN,
 	},
 	[VIN_VDD] = {
@@ -170,6 +171,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		.extend_name = "supply",
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = VIN_VDD,
 	},
 	[TEMP_INT] = {
@@ -196,6 +198,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN1,
 	},
 	[CIN1_DIFF] = {
@@ -208,6 +211,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN1_DIFF,
 	},
 	[CIN2] = {
@@ -218,6 +222,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN2,
 	},
 	[CIN2_DIFF] = {
@@ -230,6 +235,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
 		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
 		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.address = CIN2_DIFF,
 	}
 };
@@ -438,18 +444,12 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
 	return 0;
 }
 
-static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8");
-static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available,
-		       "91 84 50 26 16 13 11 9");
-
 static struct attribute *ad7746_attributes[] = {
 	&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
 	&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
 	&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
 	&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
 	&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
-	&iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr,
-	&iio_const_attr_in_capacitance_sampling_frequency_available.dev_attr.attr,
 	NULL,
 };
 
@@ -552,6 +552,32 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
 	}
 }
 
+static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, };
+static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, };
+
+static int ad7746_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, const int **vals,
+			     int *type, int *length, long mask)
+{
+	if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+		return -EINVAL;
+
+	switch (chan->type) {
+	case IIO_VOLTAGE:
+		*vals = ad7746_v_samp_freq;
+		*length = ARRAY_SIZE(ad7746_v_samp_freq);
+		break;
+	case IIO_CAPACITANCE:
+		*vals = ad7746_cap_samp_freq;
+		*length = ARRAY_SIZE(ad7746_cap_samp_freq);
+		break;
+	default:
+		return -EINVAL;
+	}
+	*type = IIO_VAL_INT;
+	return IIO_AVAIL_LIST;
+}
+
 static int ad7746_read_channel(struct iio_dev *indio_dev,
 			       struct iio_chan_spec const *chan,
 			       int *val)
@@ -687,6 +713,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
 static const struct iio_info ad7746_info = {
 	.attrs = &ad7746_attribute_group,
 	.read_raw = ad7746_read_raw,
+	.read_avail = ad7746_read_avail,
 	.write_raw = ad7746_write_raw,
 };
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 14/17] staging: iio: ad7746: White space cleanup
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (12 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 13/17] staging: iio: cdc: ad7746: Use read_avail() rather than opencoding Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 15/17] iio: cdc: ad7746: Add device specific ABI documentation Jonathan Cameron
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Tidy up some trivial whitespace issues.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/staging/iio/cdc/ad7746.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index 6fe3221c8ace..058f1b74122e 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -19,9 +19,7 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 
-/*
- * AD7746 Register Definition
- */
+/* AD7746 Register Definition */
 
 #define AD7746_REG_STATUS		0
 #define AD7746_REG_CAP_DATA_HIGH	1
@@ -242,12 +240,12 @@ static const struct iio_chan_spec ad7746_channels[] = {
 
 /* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
 static const unsigned char ad7746_vt_filter_rate_table[][2] = {
-	{50, 20 + 1}, {31, 32 + 1}, {16, 62 + 1}, {8, 122 + 1},
+	{ 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 },
 };
 
 static const unsigned char ad7746_cap_filter_rate_table[][2] = {
-	{91, 11 + 1}, {84, 12 + 1}, {50, 20 + 1}, {26, 38 + 1},
-	{16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1},
+	{ 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 },
+	{ 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 },
 };
 
 static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel)
@@ -730,6 +728,7 @@ static int ad7746_probe(struct i2c_client *client,
 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
 	if (!indio_dev)
 		return -ENOMEM;
+
 	chip = iio_priv(indio_dev);
 	mutex_init(&chip->lock);
 
@@ -780,8 +779,8 @@ static int ad7746_probe(struct i2c_client *client,
 		}
 	}
 
-	ret = i2c_smbus_write_byte_data(chip->client,
-					AD7746_REG_EXC_SETUP, regval);
+	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP,
+					regval);
 	if (ret < 0)
 		return ret;
 
@@ -794,7 +793,6 @@ static const struct i2c_device_id ad7746_id[] = {
 	{ "ad7747", 7747 },
 	{}
 };
-
 MODULE_DEVICE_TABLE(i2c, ad7746_id);
 
 static const struct of_device_id ad7746_of_match[] = {
@@ -803,7 +801,6 @@ static const struct of_device_id ad7746_of_match[] = {
 	{ .compatible = "adi,ad7747" },
 	{ },
 };
-
 MODULE_DEVICE_TABLE(of, ad7746_of_match);
 
 static struct i2c_driver ad7746_driver = {
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 15/17] iio: cdc: ad7746: Add device specific ABI documentation.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (13 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 14/17] staging: iio: ad7746: White space cleanup Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 16/17] iio: cdc: ad7746: Move driver out of staging Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 17/17] RFC: iio: cdc: ad7746: Add roadtest Jonathan Cameron
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

The datasheet description of offset calibration is complex, so for that
on just refer the reader to the device datasheet.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 b/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746
new file mode 100644
index 000000000000..6db81725b5d2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746
@@ -0,0 +1,11 @@
+What:		/sys/.../iio:deviceX/in_capacitableY_calibbias_calibration
+What:		/sys/.../iio:deviceX/in_capacitableY_calibscale_calibration
+KernelVersion:	5.19
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Write 1 to trigger a calibration of the calibbias or
+		calibscale. For calibscale, a fullscale capacitance should
+		be connected to the capacitance input and a
+		calibscale_calibration then started.  For calibbias see
+		the device datasheet section on "capacitive system offset
+		calibration".
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 16/17] iio: cdc: ad7746: Move driver out of staging.
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (14 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 15/17] iio: cdc: ad7746: Add device specific ABI documentation Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  2022-04-18 19:29 ` [PATCH 17/17] RFC: iio: cdc: ad7746: Add roadtest Jonathan Cameron
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

All known major issues with this driver resolved so time to move
it out of staging.

Note this cleanup work was done using the roadtest framework.
https://lore.kernel.org/all/20220311162445.346685-1-vincent.whitchurch@axis.com/

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/iio/cdc/Kconfig          |  10 +
 drivers/iio/cdc/Makefile         |   1 +
 drivers/iio/cdc/ad7746.c         | 818 +++++++++++++++++++++++++++++++
 drivers/staging/iio/cdc/ad7746.c | 818 -------------------------------
 4 files changed, 829 insertions(+), 818 deletions(-)

diff --git a/drivers/iio/cdc/Kconfig b/drivers/iio/cdc/Kconfig
index 5e3319a3ff48..e0a5ce66a984 100644
--- a/drivers/iio/cdc/Kconfig
+++ b/drivers/iio/cdc/Kconfig
@@ -14,4 +14,14 @@ config AD7150
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7150.
 
+config AD7746
+	tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (AD7745, AD7746, AD7747) Provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7746.
+
 endmenu
diff --git a/drivers/iio/cdc/Makefile b/drivers/iio/cdc/Makefile
index ee490637b032..41db756d8020 100644
--- a/drivers/iio/cdc/Makefile
+++ b/drivers/iio/cdc/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_AD7150) += ad7150.o
+obj-$(CONFIG_AD7746) += ad7746.o
diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c
new file mode 100644
index 000000000000..058f1b74122e
--- /dev/null
+++ b/drivers/iio/cdc/ad7746.c
@@ -0,0 +1,818 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* AD7746 Register Definition */
+
+#define AD7746_REG_STATUS		0
+#define AD7746_REG_CAP_DATA_HIGH	1
+#define AD7746_REG_VT_DATA_HIGH		4
+#define AD7746_REG_CAP_SETUP		7
+#define AD7746_REG_VT_SETUP		8
+#define AD7746_REG_EXC_SETUP		9
+#define AD7746_REG_CFG			10
+#define AD7746_REG_CAPDACA		11
+#define AD7746_REG_CAPDACB		12
+#define AD7746_REG_CAP_OFFH		13
+#define AD7746_REG_CAP_GAINH		15
+#define AD7746_REG_VOLT_GAINH		17
+
+/* Status Register Bit Designations (AD7746_REG_STATUS) */
+#define AD7746_STATUS_EXCERR		BIT(3)
+#define AD7746_STATUS_RDY		BIT(2)
+#define AD7746_STATUS_RDYVT		BIT(1)
+#define AD7746_STATUS_RDYCAP		BIT(0)
+
+/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */
+#define AD7746_CAPSETUP_CAPEN		BIT(7)
+#define AD7746_CAPSETUP_CIN2		BIT(6) /* AD7746 only */
+#define AD7746_CAPSETUP_CAPDIFF		BIT(5)
+#define AD7746_CAPSETUP_CACHOP		BIT(0)
+
+/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
+#define AD7746_VTSETUP_VTEN		BIT(7)
+#define AD7746_VTSETUP_VTMD_MASK	GENMASK(6, 5)
+#define AD7746_VTSETUP_VTMD_INT_TEMP	0
+#define AD7746_VTSETUP_VTMD_EXT_TEMP	1
+#define AD7746_VTSETUP_VTMD_VDD_MON	2
+#define AD7746_VTSETUP_VTMD_EXT_VIN	3
+#define AD7746_VTSETUP_EXTREF		BIT(4)
+#define AD7746_VTSETUP_VTSHORT		BIT(1)
+#define AD7746_VTSETUP_VTCHOP		BIT(0)
+
+/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
+#define AD7746_EXCSETUP_CLKCTRL		BIT(7)
+#define AD7746_EXCSETUP_EXCON		BIT(6)
+#define AD7746_EXCSETUP_EXCB		BIT(5)
+#define AD7746_EXCSETUP_NEXCB		BIT(4)
+#define AD7746_EXCSETUP_EXCA		BIT(3)
+#define AD7746_EXCSETUP_NEXCA		BIT(2)
+#define AD7746_EXCSETUP_EXCLVL_MASK	GENMASK(1, 0)
+
+/* Config Register Bit Designations (AD7746_REG_CFG) */
+#define AD7746_CONF_VTFS_MASK		GENMASK(7, 6)
+#define AD7746_CONF_CAPFS_MASK		GENMASK(5, 3)
+#define AD7746_CONF_MODE_MASK		GENMASK(2, 0)
+#define AD7746_CONF_MODE_IDLE		0
+#define AD7746_CONF_MODE_CONT_CONV	1
+#define AD7746_CONF_MODE_SINGLE_CONV	2
+#define AD7746_CONF_MODE_PWRDN		3
+#define AD7746_CONF_MODE_OFFS_CAL	5
+#define AD7746_CONF_MODE_GAIN_CAL	6
+
+/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
+#define AD7746_CAPDAC_DACEN		BIT(7)
+#define AD7746_CAPDAC_DACP_MASK		0x7F
+
+struct ad7746_chip_info {
+	struct i2c_client *client;
+	struct mutex lock; /* protect sensor state */
+	/*
+	 * Capacitive channel digital filter setup;
+	 * conversion time/update rate setup per channel
+	 */
+	u8	config;
+	u8	cap_setup;
+	u8	vt_setup;
+	u8	capdac[2][2];
+	s8	capdac_set;
+};
+
+enum ad7746_chan {
+	VIN,
+	VIN_VDD,
+	TEMP_INT,
+	TEMP_EXT,
+	CIN1,
+	CIN1_DIFF,
+	CIN2,
+	CIN2_DIFF,
+};
+
+struct ad7746_chan_info {
+	u8 addr;
+	union {
+		u8 vtmd;
+		struct { /* CAP SETUP fields */
+			unsigned int cin2 : 1;
+			unsigned int capdiff : 1;
+		};
+	};
+};
+
+static const struct ad7746_chan_info ad7746_chan_info[] = {
+	[VIN] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_EXT_VIN,
+	},
+	[VIN_VDD] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_VDD_MON,
+	},
+	[TEMP_INT] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_INT_TEMP,
+	},
+	[TEMP_EXT] = {
+		.addr = AD7746_REG_VT_DATA_HIGH,
+		.vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP,
+	},
+	[CIN1] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+	},
+	[CIN1_DIFF] = {
+		.addr =  AD7746_REG_CAP_DATA_HIGH,
+		.capdiff = 1,
+	},
+	[CIN2] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+		.cin2 = 1,
+	},
+	[CIN2_DIFF] = {
+		.addr = AD7746_REG_CAP_DATA_HIGH,
+		.cin2 = 1,
+		.capdiff = 1,
+	},
+};
+
+static const struct iio_chan_spec ad7746_channels[] = {
+	[VIN] = {
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = VIN,
+	},
+	[VIN_VDD] = {
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 1,
+		.extend_name = "supply",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = VIN_VDD,
+	},
+	[TEMP_INT] = {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.address = TEMP_INT,
+	},
+	[TEMP_EXT] = {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.address = TEMP_EXT,
+	},
+	[CIN1] = {
+		.type = IIO_CAPACITANCE,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = CIN1,
+	},
+	[CIN1_DIFF] = {
+		.type = IIO_CAPACITANCE,
+		.differential = 1,
+		.indexed = 1,
+		.channel = 0,
+		.channel2 = 2,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = CIN1_DIFF,
+	},
+	[CIN2] = {
+		.type = IIO_CAPACITANCE,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = CIN2,
+	},
+	[CIN2_DIFF] = {
+		.type = IIO_CAPACITANCE,
+		.differential = 1,
+		.indexed = 1,
+		.channel = 1,
+		.channel2 = 3,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = CIN2_DIFF,
+	}
+};
+
+/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
+static const unsigned char ad7746_vt_filter_rate_table[][2] = {
+	{ 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 },
+};
+
+static const unsigned char ad7746_cap_filter_rate_table[][2] = {
+	{ 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 },
+	{ 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 },
+};
+
+static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel)
+{
+	int ret = i2c_smbus_write_byte_data(chip->client,
+					    AD7746_REG_CAPDACA,
+					    chip->capdac[channel][0]);
+	if (ret < 0)
+		return ret;
+
+	return i2c_smbus_write_byte_data(chip->client,
+					  AD7746_REG_CAPDACB,
+					  chip->capdac[channel][1]);
+}
+
+static int ad7746_select_channel(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan)
+{
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	u8 vt_setup, cap_setup;
+	int ret, delay, idx;
+
+	switch (chan->type) {
+	case IIO_CAPACITANCE:
+		cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2,
+				       ad7746_chan_info[chan->address].cin2) |
+			FIELD_PREP(AD7746_CAPSETUP_CAPDIFF,
+				   ad7746_chan_info[chan->address].capdiff) |
+			FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1);
+		vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
+		idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
+		delay = ad7746_cap_filter_rate_table[idx][1];
+
+		ret = ad7746_set_capdac(chip, chan->channel);
+		if (ret < 0)
+			return ret;
+
+		if (chip->capdac_set != chan->channel)
+			chip->capdac_set = chan->channel;
+		break;
+	case IIO_VOLTAGE:
+	case IIO_TEMP:
+		vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK,
+				      ad7746_chan_info[chan->address].vtmd) |
+			FIELD_PREP(AD7746_VTSETUP_VTEN, 1);
+		cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
+		idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
+		delay = ad7746_cap_filter_rate_table[idx][1];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (chip->cap_setup != cap_setup) {
+		ret = i2c_smbus_write_byte_data(chip->client,
+						AD7746_REG_CAP_SETUP,
+						cap_setup);
+		if (ret < 0)
+			return ret;
+
+		chip->cap_setup = cap_setup;
+	}
+
+	if (chip->vt_setup != vt_setup) {
+		ret = i2c_smbus_write_byte_data(chip->client,
+						AD7746_REG_VT_SETUP,
+						vt_setup);
+		if (ret < 0)
+			return ret;
+
+		chip->vt_setup = vt_setup;
+	}
+
+	return delay;
+}
+
+static inline ssize_t ad7746_start_calib(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len,
+					 u8 regval)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	int ret, timeout = 10;
+	bool doit;
+
+	ret = strtobool(buf, &doit);
+	if (ret < 0)
+		return ret;
+
+	if (!doit)
+		return 0;
+
+	mutex_lock(&chip->lock);
+	regval |= chip->config;
+	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+	if (ret < 0)
+		goto unlock;
+
+	do {
+		msleep(20);
+		ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
+		if (ret < 0)
+			goto unlock;
+
+	} while ((ret == regval) && timeout--);
+
+	mutex_unlock(&chip->lock);
+
+	return len;
+
+unlock:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static ssize_t ad7746_start_offset_calib(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	int ret = ad7746_select_channel(indio_dev,
+			      &ad7746_channels[to_iio_dev_attr(attr)->address]);
+	if (ret < 0)
+		return ret;
+
+	return ad7746_start_calib(dev, attr, buf, len,
+				  FIELD_PREP(AD7746_CONF_MODE_MASK,
+					     AD7746_CONF_MODE_OFFS_CAL));
+}
+
+static ssize_t ad7746_start_gain_calib(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	int ret = ad7746_select_channel(indio_dev,
+			      &ad7746_channels[to_iio_dev_attr(attr)->address]);
+	if (ret < 0)
+		return ret;
+
+	return ad7746_start_calib(dev, attr, buf, len,
+				  FIELD_PREP(AD7746_CONF_MODE_MASK,
+					     AD7746_CONF_MODE_GAIN_CAL));
+}
+
+static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
+		       0200, NULL, ad7746_start_offset_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
+		       0200, NULL, ad7746_start_offset_calib, CIN2);
+static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
+		       0200, NULL, ad7746_start_gain_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
+		       0200, NULL, ad7746_start_gain_calib, CIN2);
+static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
+		       0200, NULL, ad7746_start_gain_calib, VIN);
+
+static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
+					      int val)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
+		if (val >= ad7746_cap_filter_rate_table[i][0])
+			break;
+
+	if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
+		i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
+
+	chip->config &= ~AD7746_CONF_CAPFS_MASK;
+	chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i);
+
+	return 0;
+}
+
+static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
+					     int val)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
+		if (val >= ad7746_vt_filter_rate_table[i][0])
+			break;
+
+	if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
+		i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
+
+	chip->config &= ~AD7746_CONF_VTFS_MASK;
+	chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i);
+
+	return 0;
+}
+
+static struct attribute *ad7746_attributes[] = {
+	&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
+	&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
+	&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
+	&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
+	&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7746_attribute_group = {
+	.attrs = ad7746_attributes,
+};
+
+static int ad7746_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val,
+			    int val2,
+			    long mask)
+{
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	int ret, reg;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (val != 1)
+			return -EINVAL;
+
+		val = (val2 * 1024) / 15625;
+
+		switch (chan->type) {
+		case IIO_CAPACITANCE:
+			reg = AD7746_REG_CAP_GAINH;
+			break;
+		case IIO_VOLTAGE:
+			reg = AD7746_REG_VOLT_GAINH;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		mutex_lock(&chip->lock);
+		ret = i2c_smbus_write_word_swapped(chip->client, reg, val);
+		mutex_unlock(&chip->lock);
+		if (ret < 0)
+			return ret;
+
+		return 0;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (val < 0 || val > 0xFFFF)
+			return -EINVAL;
+
+		mutex_lock(&chip->lock);
+		ret = i2c_smbus_write_word_swapped(chip->client,
+						   AD7746_REG_CAP_OFFH, val);
+		mutex_unlock(&chip->lock);
+		if (ret < 0)
+			return ret;
+
+		return 0;
+	case IIO_CHAN_INFO_OFFSET:
+	case IIO_CHAN_INFO_INPUTOFFSET:
+		if (val < 0 || val > 43008000) /* 21pF */
+			return -EINVAL;
+
+		/*
+		 * CAPDAC Scale = 21pF_typ / 127
+		 * CIN Scale = 8.192pF / 2^24
+		 * Offset Scale = CAPDAC Scale / CIN Scale = 338646
+		 */
+
+		val /= 338646;
+		mutex_lock(&chip->lock);
+		chip->capdac[chan->channel][chan->differential] = val > 0 ?
+			FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0;
+
+		ret = ad7746_set_capdac(chip, chan->channel);
+		if (ret < 0) {
+			mutex_unlock(&chip->lock);
+			return ret;
+		}
+
+		chip->capdac_set = chan->channel;
+		mutex_unlock(&chip->lock);
+
+		return 0;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (val2)
+			return -EINVAL;
+
+		switch (chan->type) {
+		case IIO_CAPACITANCE:
+			mutex_lock(&chip->lock);
+			ret = ad7746_store_cap_filter_rate_setup(chip, val);
+			mutex_unlock(&chip->lock);
+			return ret;
+		case IIO_VOLTAGE:
+			mutex_lock(&chip->lock);
+			ret = ad7746_store_vt_filter_rate_setup(chip, val);
+			mutex_unlock(&chip->lock);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, };
+static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, };
+
+static int ad7746_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, const int **vals,
+			     int *type, int *length, long mask)
+{
+	if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+		return -EINVAL;
+
+	switch (chan->type) {
+	case IIO_VOLTAGE:
+		*vals = ad7746_v_samp_freq;
+		*length = ARRAY_SIZE(ad7746_v_samp_freq);
+		break;
+	case IIO_CAPACITANCE:
+		*vals = ad7746_cap_samp_freq;
+		*length = ARRAY_SIZE(ad7746_cap_samp_freq);
+		break;
+	default:
+		return -EINVAL;
+	}
+	*type = IIO_VAL_INT;
+	return IIO_AVAIL_LIST;
+}
+
+static int ad7746_read_channel(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int *val)
+{
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	int ret, delay;
+	u8 data[3];
+	u8 regval;
+
+	ret = ad7746_select_channel(indio_dev, chan);
+	if (ret < 0)
+		return ret;
+	delay = ret;
+
+	regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK,
+					   AD7746_CONF_MODE_SINGLE_CONV);
+	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+	if (ret < 0)
+		return ret;
+
+	msleep(delay);
+	/* Now read the actual register */
+	ret = i2c_smbus_read_i2c_block_data(chip->client,
+					    ad7746_chan_info[chan->address].addr,
+					    3, data);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Offset applied internally becaue the _offset userspace interface is
+	 * needed for the CAP DACs which apply a controllable offset.
+	 */
+	*val = get_unaligned_be24(data) - 0x800000;
+
+	return 0;
+}
+
+static int ad7746_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2,
+			   long mask)
+{
+	struct ad7746_chip_info *chip = iio_priv(indio_dev);
+	int ret, idx;
+	u8 reg;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&chip->lock);
+		ret = ad7746_read_channel(indio_dev, chan, val);
+		mutex_unlock(&chip->lock);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		switch (chan->type) {
+		case IIO_CAPACITANCE:
+			reg = AD7746_REG_CAP_GAINH;
+			break;
+		case IIO_VOLTAGE:
+			reg = AD7746_REG_VOLT_GAINH;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		mutex_lock(&chip->lock);
+		ret = i2c_smbus_read_word_swapped(chip->client, reg);
+		mutex_unlock(&chip->lock);
+		if (ret < 0)
+			return ret;
+		/* 1 + gain_val / 2^16 */
+		*val = 1;
+		*val2 = (15625 * ret) / 1024;
+
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		mutex_lock(&chip->lock);
+		ret = i2c_smbus_read_word_swapped(chip->client,
+						  AD7746_REG_CAP_OFFH);
+		mutex_unlock(&chip->lock);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_OFFSET:
+	case IIO_CHAN_INFO_INPUTOFFSET:
+		*val = FIELD_GET(AD7746_CAPDAC_DACP_MASK,
+				 chip->capdac[chan->channel][chan->differential]) * 338646;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_CAPACITANCE:
+			/* 8.192pf / 2^24 */
+			*val =  0;
+			*val2 = 488;
+			return IIO_VAL_INT_PLUS_NANO;
+		case IIO_VOLTAGE:
+			/* 1170mV / 2^23 */
+			*val = 1170;
+			if (chan->channel == 1)
+				*val *= 6;
+			*val2 = 23;
+			return IIO_VAL_FRACTIONAL_LOG2;
+		case IIO_TEMP:
+			*val = 125;
+			*val2 = 8;
+			return IIO_VAL_FRACTIONAL_LOG2;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		switch (chan->type) {
+		case IIO_CAPACITANCE:
+			idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
+			*val = ad7746_cap_filter_rate_table[idx][0];
+			return IIO_VAL_INT;
+		case IIO_VOLTAGE:
+			idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
+			*val = ad7746_vt_filter_rate_table[idx][0];
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info ad7746_info = {
+	.attrs = &ad7746_attribute_group,
+	.read_raw = ad7746_read_raw,
+	.read_avail = ad7746_read_avail,
+	.write_raw = ad7746_write_raw,
+};
+
+static int ad7746_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ad7746_chip_info *chip;
+	struct iio_dev *indio_dev;
+	unsigned char regval = 0;
+	unsigned int vdd_permille;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip = iio_priv(indio_dev);
+	mutex_init(&chip->lock);
+
+	chip->client = client;
+	chip->capdac_set = -1;
+
+	indio_dev->name = id->name;
+	indio_dev->info = &ad7746_info;
+	indio_dev->channels = ad7746_channels;
+	if (id->driver_data == 7746)
+		indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+	else
+		indio_dev->num_channels =  ARRAY_SIZE(ad7746_channels) - 2;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	if (device_property_read_bool(dev, "adi,exca-output-en")) {
+		if (device_property_read_bool(dev, "adi,exca-output-invert"))
+			regval |= AD7746_EXCSETUP_NEXCA;
+		else
+			regval |= AD7746_EXCSETUP_EXCA;
+	}
+
+	if (device_property_read_bool(dev, "adi,excb-output-en")) {
+		if (device_property_read_bool(dev, "adi,excb-output-invert"))
+			regval |= AD7746_EXCSETUP_NEXCB;
+		else
+			regval |= AD7746_EXCSETUP_EXCB;
+	}
+
+	ret = device_property_read_u32(dev, "adi,excitation-vdd-permille",
+				       &vdd_permille);
+	if (!ret) {
+		switch (vdd_permille) {
+		case 125:
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0);
+			break;
+		case 250:
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1);
+			break;
+		case 375:
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2);
+			break;
+		case 500:
+			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3);
+			break;
+		default:
+			break;
+		}
+	}
+
+	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP,
+					regval);
+	if (ret < 0)
+		return ret;
+
+	return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+}
+
+static const struct i2c_device_id ad7746_id[] = {
+	{ "ad7745", 7745 },
+	{ "ad7746", 7746 },
+	{ "ad7747", 7747 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ad7746_id);
+
+static const struct of_device_id ad7746_of_match[] = {
+	{ .compatible = "adi,ad7745" },
+	{ .compatible = "adi,ad7746" },
+	{ .compatible = "adi,ad7747" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ad7746_of_match);
+
+static struct i2c_driver ad7746_driver = {
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.of_match_table = ad7746_of_match,
+	},
+	.probe = ad7746_probe,
+	.id_table = ad7746_id,
+};
+module_i2c_driver(ad7746_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
deleted file mode 100644
index 058f1b74122e..000000000000
--- a/drivers/staging/iio/cdc/ad7746.c
+++ /dev/null
@@ -1,818 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747
- *
- * Copyright 2011 Analog Devices Inc.
- */
-
-#include <linux/bitfield.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/sysfs.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
-
-/* AD7746 Register Definition */
-
-#define AD7746_REG_STATUS		0
-#define AD7746_REG_CAP_DATA_HIGH	1
-#define AD7746_REG_VT_DATA_HIGH		4
-#define AD7746_REG_CAP_SETUP		7
-#define AD7746_REG_VT_SETUP		8
-#define AD7746_REG_EXC_SETUP		9
-#define AD7746_REG_CFG			10
-#define AD7746_REG_CAPDACA		11
-#define AD7746_REG_CAPDACB		12
-#define AD7746_REG_CAP_OFFH		13
-#define AD7746_REG_CAP_GAINH		15
-#define AD7746_REG_VOLT_GAINH		17
-
-/* Status Register Bit Designations (AD7746_REG_STATUS) */
-#define AD7746_STATUS_EXCERR		BIT(3)
-#define AD7746_STATUS_RDY		BIT(2)
-#define AD7746_STATUS_RDYVT		BIT(1)
-#define AD7746_STATUS_RDYCAP		BIT(0)
-
-/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */
-#define AD7746_CAPSETUP_CAPEN		BIT(7)
-#define AD7746_CAPSETUP_CIN2		BIT(6) /* AD7746 only */
-#define AD7746_CAPSETUP_CAPDIFF		BIT(5)
-#define AD7746_CAPSETUP_CACHOP		BIT(0)
-
-/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
-#define AD7746_VTSETUP_VTEN		BIT(7)
-#define AD7746_VTSETUP_VTMD_MASK	GENMASK(6, 5)
-#define AD7746_VTSETUP_VTMD_INT_TEMP	0
-#define AD7746_VTSETUP_VTMD_EXT_TEMP	1
-#define AD7746_VTSETUP_VTMD_VDD_MON	2
-#define AD7746_VTSETUP_VTMD_EXT_VIN	3
-#define AD7746_VTSETUP_EXTREF		BIT(4)
-#define AD7746_VTSETUP_VTSHORT		BIT(1)
-#define AD7746_VTSETUP_VTCHOP		BIT(0)
-
-/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
-#define AD7746_EXCSETUP_CLKCTRL		BIT(7)
-#define AD7746_EXCSETUP_EXCON		BIT(6)
-#define AD7746_EXCSETUP_EXCB		BIT(5)
-#define AD7746_EXCSETUP_NEXCB		BIT(4)
-#define AD7746_EXCSETUP_EXCA		BIT(3)
-#define AD7746_EXCSETUP_NEXCA		BIT(2)
-#define AD7746_EXCSETUP_EXCLVL_MASK	GENMASK(1, 0)
-
-/* Config Register Bit Designations (AD7746_REG_CFG) */
-#define AD7746_CONF_VTFS_MASK		GENMASK(7, 6)
-#define AD7746_CONF_CAPFS_MASK		GENMASK(5, 3)
-#define AD7746_CONF_MODE_MASK		GENMASK(2, 0)
-#define AD7746_CONF_MODE_IDLE		0
-#define AD7746_CONF_MODE_CONT_CONV	1
-#define AD7746_CONF_MODE_SINGLE_CONV	2
-#define AD7746_CONF_MODE_PWRDN		3
-#define AD7746_CONF_MODE_OFFS_CAL	5
-#define AD7746_CONF_MODE_GAIN_CAL	6
-
-/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
-#define AD7746_CAPDAC_DACEN		BIT(7)
-#define AD7746_CAPDAC_DACP_MASK		0x7F
-
-struct ad7746_chip_info {
-	struct i2c_client *client;
-	struct mutex lock; /* protect sensor state */
-	/*
-	 * Capacitive channel digital filter setup;
-	 * conversion time/update rate setup per channel
-	 */
-	u8	config;
-	u8	cap_setup;
-	u8	vt_setup;
-	u8	capdac[2][2];
-	s8	capdac_set;
-};
-
-enum ad7746_chan {
-	VIN,
-	VIN_VDD,
-	TEMP_INT,
-	TEMP_EXT,
-	CIN1,
-	CIN1_DIFF,
-	CIN2,
-	CIN2_DIFF,
-};
-
-struct ad7746_chan_info {
-	u8 addr;
-	union {
-		u8 vtmd;
-		struct { /* CAP SETUP fields */
-			unsigned int cin2 : 1;
-			unsigned int capdiff : 1;
-		};
-	};
-};
-
-static const struct ad7746_chan_info ad7746_chan_info[] = {
-	[VIN] = {
-		.addr = AD7746_REG_VT_DATA_HIGH,
-		.vtmd = AD7746_VTSETUP_VTMD_EXT_VIN,
-	},
-	[VIN_VDD] = {
-		.addr = AD7746_REG_VT_DATA_HIGH,
-		.vtmd = AD7746_VTSETUP_VTMD_VDD_MON,
-	},
-	[TEMP_INT] = {
-		.addr = AD7746_REG_VT_DATA_HIGH,
-		.vtmd = AD7746_VTSETUP_VTMD_INT_TEMP,
-	},
-	[TEMP_EXT] = {
-		.addr = AD7746_REG_VT_DATA_HIGH,
-		.vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP,
-	},
-	[CIN1] = {
-		.addr = AD7746_REG_CAP_DATA_HIGH,
-	},
-	[CIN1_DIFF] = {
-		.addr =  AD7746_REG_CAP_DATA_HIGH,
-		.capdiff = 1,
-	},
-	[CIN2] = {
-		.addr = AD7746_REG_CAP_DATA_HIGH,
-		.cin2 = 1,
-	},
-	[CIN2_DIFF] = {
-		.addr = AD7746_REG_CAP_DATA_HIGH,
-		.cin2 = 1,
-		.capdiff = 1,
-	},
-};
-
-static const struct iio_chan_spec ad7746_channels[] = {
-	[VIN] = {
-		.type = IIO_VOLTAGE,
-		.indexed = 1,
-		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = VIN,
-	},
-	[VIN_VDD] = {
-		.type = IIO_VOLTAGE,
-		.indexed = 1,
-		.channel = 1,
-		.extend_name = "supply",
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = VIN_VDD,
-	},
-	[TEMP_INT] = {
-		.type = IIO_TEMP,
-		.indexed = 1,
-		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
-		.address = TEMP_INT,
-	},
-	[TEMP_EXT] = {
-		.type = IIO_TEMP,
-		.indexed = 1,
-		.channel = 1,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
-		.address = TEMP_EXT,
-	},
-	[CIN1] = {
-		.type = IIO_CAPACITANCE,
-		.indexed = 1,
-		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
-		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = CIN1,
-	},
-	[CIN1_DIFF] = {
-		.type = IIO_CAPACITANCE,
-		.differential = 1,
-		.indexed = 1,
-		.channel = 0,
-		.channel2 = 2,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
-		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = CIN1_DIFF,
-	},
-	[CIN2] = {
-		.type = IIO_CAPACITANCE,
-		.indexed = 1,
-		.channel = 1,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
-		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = CIN2,
-	},
-	[CIN2_DIFF] = {
-		.type = IIO_CAPACITANCE,
-		.differential = 1,
-		.indexed = 1,
-		.channel = 1,
-		.channel2 = 3,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-		BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INPUTOFFSET),
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
-		BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-		.address = CIN2_DIFF,
-	}
-};
-
-/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
-static const unsigned char ad7746_vt_filter_rate_table[][2] = {
-	{ 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 },
-};
-
-static const unsigned char ad7746_cap_filter_rate_table[][2] = {
-	{ 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 },
-	{ 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 },
-};
-
-static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel)
-{
-	int ret = i2c_smbus_write_byte_data(chip->client,
-					    AD7746_REG_CAPDACA,
-					    chip->capdac[channel][0]);
-	if (ret < 0)
-		return ret;
-
-	return i2c_smbus_write_byte_data(chip->client,
-					  AD7746_REG_CAPDACB,
-					  chip->capdac[channel][1]);
-}
-
-static int ad7746_select_channel(struct iio_dev *indio_dev,
-				 struct iio_chan_spec const *chan)
-{
-	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	u8 vt_setup, cap_setup;
-	int ret, delay, idx;
-
-	switch (chan->type) {
-	case IIO_CAPACITANCE:
-		cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2,
-				       ad7746_chan_info[chan->address].cin2) |
-			FIELD_PREP(AD7746_CAPSETUP_CAPDIFF,
-				   ad7746_chan_info[chan->address].capdiff) |
-			FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1);
-		vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
-		idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
-		delay = ad7746_cap_filter_rate_table[idx][1];
-
-		ret = ad7746_set_capdac(chip, chan->channel);
-		if (ret < 0)
-			return ret;
-
-		if (chip->capdac_set != chan->channel)
-			chip->capdac_set = chan->channel;
-		break;
-	case IIO_VOLTAGE:
-	case IIO_TEMP:
-		vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK,
-				      ad7746_chan_info[chan->address].vtmd) |
-			FIELD_PREP(AD7746_VTSETUP_VTEN, 1);
-		cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
-		idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
-		delay = ad7746_cap_filter_rate_table[idx][1];
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	if (chip->cap_setup != cap_setup) {
-		ret = i2c_smbus_write_byte_data(chip->client,
-						AD7746_REG_CAP_SETUP,
-						cap_setup);
-		if (ret < 0)
-			return ret;
-
-		chip->cap_setup = cap_setup;
-	}
-
-	if (chip->vt_setup != vt_setup) {
-		ret = i2c_smbus_write_byte_data(chip->client,
-						AD7746_REG_VT_SETUP,
-						vt_setup);
-		if (ret < 0)
-			return ret;
-
-		chip->vt_setup = vt_setup;
-	}
-
-	return delay;
-}
-
-static inline ssize_t ad7746_start_calib(struct device *dev,
-					 struct device_attribute *attr,
-					 const char *buf,
-					 size_t len,
-					 u8 regval)
-{
-	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	int ret, timeout = 10;
-	bool doit;
-
-	ret = strtobool(buf, &doit);
-	if (ret < 0)
-		return ret;
-
-	if (!doit)
-		return 0;
-
-	mutex_lock(&chip->lock);
-	regval |= chip->config;
-	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
-	if (ret < 0)
-		goto unlock;
-
-	do {
-		msleep(20);
-		ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
-		if (ret < 0)
-			goto unlock;
-
-	} while ((ret == regval) && timeout--);
-
-	mutex_unlock(&chip->lock);
-
-	return len;
-
-unlock:
-	mutex_unlock(&chip->lock);
-	return ret;
-}
-
-static ssize_t ad7746_start_offset_calib(struct device *dev,
-					 struct device_attribute *attr,
-					 const char *buf,
-					 size_t len)
-{
-	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-	int ret = ad7746_select_channel(indio_dev,
-			      &ad7746_channels[to_iio_dev_attr(attr)->address]);
-	if (ret < 0)
-		return ret;
-
-	return ad7746_start_calib(dev, attr, buf, len,
-				  FIELD_PREP(AD7746_CONF_MODE_MASK,
-					     AD7746_CONF_MODE_OFFS_CAL));
-}
-
-static ssize_t ad7746_start_gain_calib(struct device *dev,
-				       struct device_attribute *attr,
-				       const char *buf,
-				       size_t len)
-{
-	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-	int ret = ad7746_select_channel(indio_dev,
-			      &ad7746_channels[to_iio_dev_attr(attr)->address]);
-	if (ret < 0)
-		return ret;
-
-	return ad7746_start_calib(dev, attr, buf, len,
-				  FIELD_PREP(AD7746_CONF_MODE_MASK,
-					     AD7746_CONF_MODE_GAIN_CAL));
-}
-
-static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
-		       0200, NULL, ad7746_start_offset_calib, CIN1);
-static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
-		       0200, NULL, ad7746_start_offset_calib, CIN2);
-static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
-		       0200, NULL, ad7746_start_gain_calib, CIN1);
-static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
-		       0200, NULL, ad7746_start_gain_calib, CIN2);
-static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
-		       0200, NULL, ad7746_start_gain_calib, VIN);
-
-static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
-					      int val)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
-		if (val >= ad7746_cap_filter_rate_table[i][0])
-			break;
-
-	if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
-		i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
-
-	chip->config &= ~AD7746_CONF_CAPFS_MASK;
-	chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i);
-
-	return 0;
-}
-
-static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
-					     int val)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
-		if (val >= ad7746_vt_filter_rate_table[i][0])
-			break;
-
-	if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
-		i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
-
-	chip->config &= ~AD7746_CONF_VTFS_MASK;
-	chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i);
-
-	return 0;
-}
-
-static struct attribute *ad7746_attributes[] = {
-	&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
-	&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
-	&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
-	&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
-	&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group ad7746_attribute_group = {
-	.attrs = ad7746_attributes,
-};
-
-static int ad7746_write_raw(struct iio_dev *indio_dev,
-			    struct iio_chan_spec const *chan,
-			    int val,
-			    int val2,
-			    long mask)
-{
-	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	int ret, reg;
-
-	switch (mask) {
-	case IIO_CHAN_INFO_CALIBSCALE:
-		if (val != 1)
-			return -EINVAL;
-
-		val = (val2 * 1024) / 15625;
-
-		switch (chan->type) {
-		case IIO_CAPACITANCE:
-			reg = AD7746_REG_CAP_GAINH;
-			break;
-		case IIO_VOLTAGE:
-			reg = AD7746_REG_VOLT_GAINH;
-			break;
-		default:
-			return -EINVAL;
-		}
-
-		mutex_lock(&chip->lock);
-		ret = i2c_smbus_write_word_swapped(chip->client, reg, val);
-		mutex_unlock(&chip->lock);
-		if (ret < 0)
-			return ret;
-
-		return 0;
-	case IIO_CHAN_INFO_CALIBBIAS:
-		if (val < 0 || val > 0xFFFF)
-			return -EINVAL;
-
-		mutex_lock(&chip->lock);
-		ret = i2c_smbus_write_word_swapped(chip->client,
-						   AD7746_REG_CAP_OFFH, val);
-		mutex_unlock(&chip->lock);
-		if (ret < 0)
-			return ret;
-
-		return 0;
-	case IIO_CHAN_INFO_OFFSET:
-	case IIO_CHAN_INFO_INPUTOFFSET:
-		if (val < 0 || val > 43008000) /* 21pF */
-			return -EINVAL;
-
-		/*
-		 * CAPDAC Scale = 21pF_typ / 127
-		 * CIN Scale = 8.192pF / 2^24
-		 * Offset Scale = CAPDAC Scale / CIN Scale = 338646
-		 */
-
-		val /= 338646;
-		mutex_lock(&chip->lock);
-		chip->capdac[chan->channel][chan->differential] = val > 0 ?
-			FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0;
-
-		ret = ad7746_set_capdac(chip, chan->channel);
-		if (ret < 0) {
-			mutex_unlock(&chip->lock);
-			return ret;
-		}
-
-		chip->capdac_set = chan->channel;
-		mutex_unlock(&chip->lock);
-
-		return 0;
-	case IIO_CHAN_INFO_SAMP_FREQ:
-		if (val2)
-			return -EINVAL;
-
-		switch (chan->type) {
-		case IIO_CAPACITANCE:
-			mutex_lock(&chip->lock);
-			ret = ad7746_store_cap_filter_rate_setup(chip, val);
-			mutex_unlock(&chip->lock);
-			return ret;
-		case IIO_VOLTAGE:
-			mutex_lock(&chip->lock);
-			ret = ad7746_store_vt_filter_rate_setup(chip, val);
-			mutex_unlock(&chip->lock);
-			return ret;
-		default:
-			return -EINVAL;
-		}
-	default:
-		return -EINVAL;
-	}
-}
-
-static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, };
-static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, };
-
-static int ad7746_read_avail(struct iio_dev *indio_dev,
-			     struct iio_chan_spec const *chan, const int **vals,
-			     int *type, int *length, long mask)
-{
-	if (mask != IIO_CHAN_INFO_SAMP_FREQ)
-		return -EINVAL;
-
-	switch (chan->type) {
-	case IIO_VOLTAGE:
-		*vals = ad7746_v_samp_freq;
-		*length = ARRAY_SIZE(ad7746_v_samp_freq);
-		break;
-	case IIO_CAPACITANCE:
-		*vals = ad7746_cap_samp_freq;
-		*length = ARRAY_SIZE(ad7746_cap_samp_freq);
-		break;
-	default:
-		return -EINVAL;
-	}
-	*type = IIO_VAL_INT;
-	return IIO_AVAIL_LIST;
-}
-
-static int ad7746_read_channel(struct iio_dev *indio_dev,
-			       struct iio_chan_spec const *chan,
-			       int *val)
-{
-	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	int ret, delay;
-	u8 data[3];
-	u8 regval;
-
-	ret = ad7746_select_channel(indio_dev, chan);
-	if (ret < 0)
-		return ret;
-	delay = ret;
-
-	regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK,
-					   AD7746_CONF_MODE_SINGLE_CONV);
-	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
-	if (ret < 0)
-		return ret;
-
-	msleep(delay);
-	/* Now read the actual register */
-	ret = i2c_smbus_read_i2c_block_data(chip->client,
-					    ad7746_chan_info[chan->address].addr,
-					    3, data);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * Offset applied internally becaue the _offset userspace interface is
-	 * needed for the CAP DACs which apply a controllable offset.
-	 */
-	*val = get_unaligned_be24(data) - 0x800000;
-
-	return 0;
-}
-
-static int ad7746_read_raw(struct iio_dev *indio_dev,
-			   struct iio_chan_spec const *chan,
-			   int *val, int *val2,
-			   long mask)
-{
-	struct ad7746_chip_info *chip = iio_priv(indio_dev);
-	int ret, idx;
-	u8 reg;
-
-	switch (mask) {
-	case IIO_CHAN_INFO_RAW:
-		mutex_lock(&chip->lock);
-		ret = ad7746_read_channel(indio_dev, chan, val);
-		mutex_unlock(&chip->lock);
-		if (ret < 0)
-			return ret;
-
-		return IIO_VAL_INT;
-	case IIO_CHAN_INFO_CALIBSCALE:
-		switch (chan->type) {
-		case IIO_CAPACITANCE:
-			reg = AD7746_REG_CAP_GAINH;
-			break;
-		case IIO_VOLTAGE:
-			reg = AD7746_REG_VOLT_GAINH;
-			break;
-		default:
-			return -EINVAL;
-		}
-
-		mutex_lock(&chip->lock);
-		ret = i2c_smbus_read_word_swapped(chip->client, reg);
-		mutex_unlock(&chip->lock);
-		if (ret < 0)
-			return ret;
-		/* 1 + gain_val / 2^16 */
-		*val = 1;
-		*val2 = (15625 * ret) / 1024;
-
-		return IIO_VAL_INT_PLUS_MICRO;
-	case IIO_CHAN_INFO_CALIBBIAS:
-		mutex_lock(&chip->lock);
-		ret = i2c_smbus_read_word_swapped(chip->client,
-						  AD7746_REG_CAP_OFFH);
-		mutex_unlock(&chip->lock);
-		if (ret < 0)
-			return ret;
-		*val = ret;
-
-		return IIO_VAL_INT;
-	case IIO_CHAN_INFO_OFFSET:
-	case IIO_CHAN_INFO_INPUTOFFSET:
-		*val = FIELD_GET(AD7746_CAPDAC_DACP_MASK,
-				 chip->capdac[chan->channel][chan->differential]) * 338646;
-
-		return IIO_VAL_INT;
-	case IIO_CHAN_INFO_SCALE:
-		switch (chan->type) {
-		case IIO_CAPACITANCE:
-			/* 8.192pf / 2^24 */
-			*val =  0;
-			*val2 = 488;
-			return IIO_VAL_INT_PLUS_NANO;
-		case IIO_VOLTAGE:
-			/* 1170mV / 2^23 */
-			*val = 1170;
-			if (chan->channel == 1)
-				*val *= 6;
-			*val2 = 23;
-			return IIO_VAL_FRACTIONAL_LOG2;
-		case IIO_TEMP:
-			*val = 125;
-			*val2 = 8;
-			return IIO_VAL_FRACTIONAL_LOG2;
-		default:
-			return -EINVAL;
-		}
-	case IIO_CHAN_INFO_SAMP_FREQ:
-		switch (chan->type) {
-		case IIO_CAPACITANCE:
-			idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
-			*val = ad7746_cap_filter_rate_table[idx][0];
-			return IIO_VAL_INT;
-		case IIO_VOLTAGE:
-			idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
-			*val = ad7746_vt_filter_rate_table[idx][0];
-			return IIO_VAL_INT;
-		default:
-			return -EINVAL;
-		}
-	default:
-		return -EINVAL;
-	}
-}
-
-static const struct iio_info ad7746_info = {
-	.attrs = &ad7746_attribute_group,
-	.read_raw = ad7746_read_raw,
-	.read_avail = ad7746_read_avail,
-	.write_raw = ad7746_write_raw,
-};
-
-static int ad7746_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
-{
-	struct device *dev = &client->dev;
-	struct ad7746_chip_info *chip;
-	struct iio_dev *indio_dev;
-	unsigned char regval = 0;
-	unsigned int vdd_permille;
-	int ret;
-
-	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
-	if (!indio_dev)
-		return -ENOMEM;
-
-	chip = iio_priv(indio_dev);
-	mutex_init(&chip->lock);
-
-	chip->client = client;
-	chip->capdac_set = -1;
-
-	indio_dev->name = id->name;
-	indio_dev->info = &ad7746_info;
-	indio_dev->channels = ad7746_channels;
-	if (id->driver_data == 7746)
-		indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
-	else
-		indio_dev->num_channels =  ARRAY_SIZE(ad7746_channels) - 2;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-
-	if (device_property_read_bool(dev, "adi,exca-output-en")) {
-		if (device_property_read_bool(dev, "adi,exca-output-invert"))
-			regval |= AD7746_EXCSETUP_NEXCA;
-		else
-			regval |= AD7746_EXCSETUP_EXCA;
-	}
-
-	if (device_property_read_bool(dev, "adi,excb-output-en")) {
-		if (device_property_read_bool(dev, "adi,excb-output-invert"))
-			regval |= AD7746_EXCSETUP_NEXCB;
-		else
-			regval |= AD7746_EXCSETUP_EXCB;
-	}
-
-	ret = device_property_read_u32(dev, "adi,excitation-vdd-permille",
-				       &vdd_permille);
-	if (!ret) {
-		switch (vdd_permille) {
-		case 125:
-			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0);
-			break;
-		case 250:
-			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1);
-			break;
-		case 375:
-			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2);
-			break;
-		case 500:
-			regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3);
-			break;
-		default:
-			break;
-		}
-	}
-
-	ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP,
-					regval);
-	if (ret < 0)
-		return ret;
-
-	return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
-}
-
-static const struct i2c_device_id ad7746_id[] = {
-	{ "ad7745", 7745 },
-	{ "ad7746", 7746 },
-	{ "ad7747", 7747 },
-	{}
-};
-MODULE_DEVICE_TABLE(i2c, ad7746_id);
-
-static const struct of_device_id ad7746_of_match[] = {
-	{ .compatible = "adi,ad7745" },
-	{ .compatible = "adi,ad7746" },
-	{ .compatible = "adi,ad7747" },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, ad7746_of_match);
-
-static struct i2c_driver ad7746_driver = {
-	.driver = {
-		.name = KBUILD_MODNAME,
-		.of_match_table = ad7746_of_match,
-	},
-	.probe = ad7746_probe,
-	.id_table = ad7746_id,
-};
-module_i2c_driver(ad7746_driver);
-
-MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
-MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver");
-MODULE_LICENSE("GPL v2");
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 17/17] RFC: iio: cdc: ad7746: Add roadtest
  2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
                   ` (15 preceding siblings ...)
  2022-04-18 19:29 ` [PATCH 16/17] iio: cdc: ad7746: Move driver out of staging Jonathan Cameron
@ 2022-04-18 19:29 ` Jonathan Cameron
  16 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-18 19:29 UTC (permalink / raw)
  To: linux-iio
  Cc: Michael Hennerich, Lars-Peter Clausen, Vincent Whitchurch,
	Jonathan Cameron

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

The recent work to cleanup and migrate this driver out of staging
relied on using roadtest to verify that the various refactoring
and fixes did not cause any regressions.

Note I am far from an experienced python coder so a large part of
this exercise was to establish if roadtest was useful / usable
without that particular skill set.  Conclusion yes it is :)

Apologies for the code!

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 .../roadtest/tests/iio/cdc/__init__.py        |   0
 .../roadtest/roadtest/tests/iio/cdc/config    |   1 +
 .../roadtest/tests/iio/cdc/test_ad7746.py     | 323 ++++++++++++++++++
 3 files changed, 324 insertions(+)

diff --git a/tools/testing/roadtest/roadtest/tests/iio/cdc/__init__.py b/tools/testing/roadtest/roadtest/tests/iio/cdc/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/tools/testing/roadtest/roadtest/tests/iio/cdc/config b/tools/testing/roadtest/roadtest/tests/iio/cdc/config
new file mode 100644
index 000000000000..6e9397325918
--- /dev/null
+++ b/tools/testing/roadtest/roadtest/tests/iio/cdc/config
@@ -0,0 +1 @@
+CONFIG_AD7746=m
diff --git a/tools/testing/roadtest/roadtest/tests/iio/cdc/test_ad7746.py b/tools/testing/roadtest/roadtest/tests/iio/cdc/test_ad7746.py
new file mode 100644
index 000000000000..60d75c0f4fe8
--- /dev/null
+++ b/tools/testing/roadtest/roadtest/tests/iio/cdc/test_ad7746.py
@@ -0,0 +1,323 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Based on test_vncl4010 Copyright Axis Communications AB
+# TODO
+# - Test read temperatures
+# - Test read capacitance
+#
+
+from typing import Any, Final
+
+from roadtest.backend.i2c import SMBusModel
+from roadtest.core.devicetree import DtFragment, DtVar
+from roadtest.core.hardware import Hardware
+from roadtest.core.suite import UMLTestCase
+from roadtest.core.modules import insmod, rmmod
+from roadtest.core.sysfs import (
+    I2CDriver,
+    read_float,
+    read_int,
+    read_str,
+    write_int,
+    write_str,
+)
+
+REG_STATUS: Final = 0x00
+REG_CAP_DATA_H: Final = 0x01
+REG_CAP_DATA_M: Final = 0x02
+REG_CAP_DATA_L: Final = 0x03
+REG_VT_DATA_H: Final = 0x04
+REG_VT_DATA_M: Final = 0x05
+REG_VT_DATA_L: Final = 0x06
+REG_CAP_SETUP: Final = 0x07
+REG_VT_SETUP: Final = 0x08
+REG_EXC_SETUP: Final = 0x09
+REG_CONFIG: Final = 0x0a
+REG_CAP_DAC_A: Final = 0x0b
+REG_CAP_DAC_B: Final = 0x0c
+REG_CAP_OFFSET_H: Final = 0x0d
+REG_CAP_OFFSET_L: Final = 0x0e
+REG_CAP_GAIN_H: Final = 0x0f
+REG_CAP_GAIN_L: Final = 0x10
+REG_VOLT_GAIN_H: Final = 0x11
+REG_VOLT_GAIN_L: Final = 0x12
+
+class AD7746(SMBusModel):
+    def __init__(self, **kwargs: Any) -> None:
+        super().__init__(regbytes=1, **kwargs)
+        self.regs = {
+            REG_STATUS: 0x07,
+            REG_CAP_DATA_H: 0x00,
+            REG_CAP_DATA_M: 0x00,
+            REG_CAP_DATA_L: 0x00,
+            REG_VT_DATA_H: 0x00,
+            REG_VT_DATA_M: 0x00,
+            REG_VT_DATA_L: 0x00,
+            REG_CAP_SETUP: 0x00,
+            REG_VT_SETUP: 0x00,
+            REG_EXC_SETUP: 0x03,
+            REG_CONFIG: 0xa0,
+            REG_CAP_DAC_A: 0x00,
+            REG_CAP_DAC_B: 0x00,
+            REG_CAP_OFFSET_H: 0x80,
+            REG_CAP_OFFSET_L: 0x00,
+            REG_CAP_GAIN_H: 0x00,
+            REG_CAP_GAIN_L: 0x00,
+            REG_VOLT_GAIN_H: 0x00,
+            REG_VOLT_GAIN_L: 0x00,
+        }
+
+    def reg_read(self, addr: int) -> int:
+        val = self.regs[addr]
+        return val;
+
+    def reg_write(self, addr: int, val: int) -> None:
+        assert addr in self.regs
+        assert addr not in ( REG_STATUS,
+                             REG_CAP_DATA_H, REG_CAP_DATA_M, REG_CAP_DATA_L,
+                             REG_VT_DATA_H, REG_VT_DATA_M, REG_VT_DATA_L )
+
+        self.regs[addr] = val
+
+    def inject(self, addr: int, val: int, mask : int = ~0) -> None:
+        old = self.regs[addr] & ~mask
+        new = old | (val & mask)
+        self.regs[addr] = new
+
+class TestAD7746(UMLTestCase):
+    dts = DtFragment(
+        src="""
+&i2c {
+    cdc@$addr$ {
+        compatible = "adi,ad7746";
+        reg = <0x$addr$>;
+    };
+};
+        """,
+        variables={
+            "addr": DtVar.I2C_ADDR,
+        },
+    )
+
+    @classmethod
+    def setUpClass(cls) -> None:
+        insmod("ad7746")
+
+    @classmethod
+    def tearDownClass(cls) -> None:
+        rmmod("ad7746")
+
+    def setUp(self) -> None:
+        self.driver = I2CDriver("ad7746")
+        self.hw = Hardware("i2c")
+        self.hw.load_model(AD7746)
+
+    def tearDown(self) -> None:
+        self.hw.close()
+
+    def read_attr(self, dev, type, attr, index, extend_name=None, default_val=0) -> float:
+        name_start = "iio:device0/in_" + type;
+        if extend_name is None:
+            try:
+                scale = read_float(dev.path / (name_start + str(index) + "_" + attr))
+            except:
+                try:
+                    scale = read_float(dev.path / (name_start + "_" + attr))
+                except:
+                    scale = default_val
+        if extend_name is not None:
+            try:
+                scale = read_float(dev.path / (name_start + str(index) + "_" + extend_name + "_" + attr))
+            except:
+                try:
+                    scale = read_float(dev.path / (name_start + str(index) +  "_" + attr))
+                except:
+                    try:
+                        scale = read_float(dev.path / (name_start + "_" + attr))
+                    except:
+                        scale = default_val
+
+        return scale
+
+    def test_scales(self) -> None:
+        with self.driver.bind(self.dts["addr"]) as dev:
+            scale = self.read_attr(dev, "voltage", "scale", 0, default_val=1.0)
+            self.assertAlmostEqual(scale, 1170 / (1 << 23))
+            scale = self.read_attr(dev, "voltage", "scale", 1, "supply", default_val=1.0)
+            self.assertAlmostEqual(scale, 1170 * 6 / ( 1 << 23))
+            scale = self.read_attr(dev, "capacitance", "scale", 0, default_val=1.0)
+            self.assertEqual(scale, 0.000000488)
+
+    def test_read_avail(self) -> None:
+        with self.driver.bind(self.dts["addr"]) as dev:
+            test = read_str(dev.path / "iio:device0/in_capacitance_sampling_frequency_available")
+            assert test == "91 84 50 26 16 13 11 9"
+            test = read_str(dev.path / "iio:device0/in_voltage_sampling_frequency_available")
+            assert test == "50 31 16 8", test
+
+    def test_read_channels(self) -> None:
+        with self.driver.bind(self.dts["addr"]) as dev:
+            testscale = 1170.0 / (1 << 23)
+            # Test endpoints and each of the bytes
+            testvals = (( 0x0, 0x0, 0x0, (-0x800000) * testscale),
+                        ( 0x0, 0x40, 0x0, (-0x800000 + 0x004000) * testscale),
+                        ( 0x80, 0x0, 0x0, (0x0) * testscale),
+                        ( 0x80, 0x0, 0x1, (0x1) * testscale),
+                        ( 0xff, 0xff, 0xff, (0x0 + 0x7fffff) * testscale),
+                        )
+            scale = self.read_attr(dev, "voltage", "scale", 0, default_val=1.0)
+            offset = self.read_attr(dev, "voltage", "offset", 0, default_val=0.0)
+
+            # Should really be testing that the voltage after application of
+            # scale and offset is correct, not raw values.
+            for h, m, l, testval in testvals:
+                # Sequence matters as we will only write registers if the change.
+                # Hence always proceed a vt read with a cap read and visa versa
+                read_int(dev.path / "iio:device0/in_capacitance0_raw")
+                # value is 24 bit and driver offsets it by -0x800000.
+                self.hw.inject(REG_VT_DATA_H, h)
+                self.hw.inject(REG_VT_DATA_M, m)
+                self.hw.inject(REG_VT_DATA_L, l)
+                value = read_int(dev.path / "iio:device0/in_voltage0_raw")
+                self.assertAlmostEqual((value + offset) * scale, testval, 4)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_VT_SETUP, 0x80 | 0x60)
+                #verify capacitance read disabled
+                cap_setup = mock.get_last_reg_write(REG_CAP_SETUP)
+                assert(not (cap_setup & 0x80))
+                mock.reset_mock()
+            testscale = 1170 * 6 / (1 << 23)
+            # Test endpoints and each of the bytes
+            testvals = (( 0x0, 0x0, 0x0, (-0x800000) * testscale),
+                        ( 0x0, 0x40, 0x0, (-0x800000 + 0x004000) * testscale),
+                        ( 0x80, 0x0, 0x0, (0x0) * testscale),
+                        ( 0x80, 0x0, 0x1, (0x1) * testscale),
+                        ( 0xff, 0xff, 0xff, (0x0 + 0x7fffff) * testscale),
+                        )
+            scale = self.read_attr(dev, "voltage", "scale", 1, "supply", default_val = 1.0)
+            for h, m, l, testval in testvals:
+                read_int(dev.path / "iio:device0/in_capacitance0_raw")
+                self.hw.inject(REG_VT_DATA_H, h)
+                self.hw.inject(REG_VT_DATA_M, m)
+                self.hw.inject(REG_VT_DATA_L, l)
+                value = read_int(dev.path/ "iio:device0/in_voltage1_supply_raw")
+                # Supply voltage channel is attenuated by x6
+                self.assertAlmostEqual((value + offset) * scale, testval, 4)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_VT_SETUP, 0x80 | 0x40)
+                #verify capacitance read disabled
+                cap_setup = mock.get_last_reg_write(REG_CAP_SETUP)
+                assert(not (cap_setup & 0x80))
+                mock.reset_mock()
+
+            # Only bother testing one of the temperature channels. They are very similar.
+            testvals = (( 0x0, 0x0, 0x0, (0/2048.0 - 4096) * 1000),
+                        ( 0x0, 0x40, 0x0, (0x004000/2048.0 - 4096) * 1000),
+                        ( 0x80, 0x0, 0x0, (0x800000/2048.0 - 4096) * 1000),
+                        ( 0x80, 0x0, 0x1, (0x800001/2048.0 - 4096) * 1000),
+                        ( 0xff, 0xff, 0xff, (0xffffff/2048.0 - 4096) * 1000),
+                        )
+            scale = self.read_attr(dev, "temp", "scale", 0, default_val = 1.0)
+            for h, m, l, testval in testvals:
+                read_int(dev.path / "iio:device0/in_capacitance0_raw")
+                self.hw.inject(REG_VT_DATA_H, h)
+                self.hw.inject(REG_VT_DATA_M, m)
+                self.hw.inject(REG_VT_DATA_L, l)
+                value = read_int(dev.path/ "iio:device0/in_temp0_raw")
+                self.assertAlmostEqual(value * scale, testval, 4)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_VT_SETUP, 0x80 | 0x00)
+                #verify capacitance read disabled
+                cap_setup = mock.get_last_reg_write(REG_CAP_SETUP)
+                assert(not (cap_setup & 0x80))
+                mock.reset_mock()
+
+            testscale = 0.000000488
+            # Test endpoints and each of the bytes
+            testvals = (( 0x0, 0x0, 0x0, (-0x800000) * testscale),
+                        ( 0x0, 0x40, 0x0, (-0x800000 + 0x004000) * testscale),
+                        ( 0x80, 0x0, 0x0, (0x0) * testscale),
+                        ( 0x80, 0x0, 0x1, (0x1) * testscale),
+                        ( 0xff, 0xff, 0xff, (0x0 + 0x7fffff) * testscale),
+                        )
+
+            scale = self.read_attr(dev, "capacitance", "scale", 0, default_val=1.0)
+            offset = 0.0 #self.read_attr(dev, "capacitance", "offset", 0, default_val=0.0)
+
+            # Should really be testing that the voltage after application of
+            # scale and offset is correct, not raw values.
+            for h, m, l, testval in testvals:
+                # Sequence matters as we will only write registers if the change.
+                # Hence always proceed a vt read with a cap read and visa versa
+                read_int(dev.path / "iio:device0/in_voltage0_raw")
+                # value is 24 bit and driver offsets it by -0x800000.
+                self.hw.inject(REG_CAP_DATA_H, h)
+                self.hw.inject(REG_CAP_DATA_M, m)
+                self.hw.inject(REG_CAP_DATA_L, l)
+                value = read_int(dev.path / "iio:device0/in_capacitance0_raw")
+                self.assertAlmostEqual((value + offset) * scale, testval, 9)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_CAP_SETUP, 0x80)
+                #verify capacitance read disabled
+                vt_setup = mock.get_last_reg_write(REG_VT_SETUP)
+                assert(not (vt_setup & 0x80))
+                mock.reset_mock()
+
+            scale = self.read_attr(dev, "capacitance", "scale", 1, default_val=1.0)
+            offset = 0.0 #self.read_attr(dev, "capacitance", "offset", 0, default_val=0.0)
+
+            # Should really be testing that the voltage after application of
+            # scale and offset is correct, not raw values.
+            for h, m, l, testval in testvals:
+                # Sequence matters as we will only write registers if the change.
+                # Hence always proceed a vt read with a cap read and visa versa
+                read_int(dev.path / "iio:device0/in_voltage0_raw")
+                # value is 24 bit and driver offsets it by -0x800000.
+                self.hw.inject(REG_CAP_DATA_H, h)
+                self.hw.inject(REG_CAP_DATA_M, m)
+                self.hw.inject(REG_CAP_DATA_L, l)
+                value = read_int(dev.path / "iio:device0/in_capacitance1_raw")
+                self.assertAlmostEqual((value + offset) * scale, testval, 9)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_CAP_SETUP, 0x80 | 0x40)
+                #verify capacitance read disabled
+                vt_setup = mock.get_last_reg_write(REG_VT_SETUP)
+                assert(not (vt_setup & 0x80))
+                mock.reset_mock()
+
+            # Should really be testing that the voltage after application of
+            # scale and offset is correct, not raw values.
+            for h, m, l, testval in testvals:
+                # Sequence matters as we will only write registers if the change.
+                # Hence always proceed a vt read with a cap read and visa versa
+                read_int(dev.path / "iio:device0/in_voltage0_raw")
+                # value is 24 bit and driver offsets it by -0x800000.
+                self.hw.inject(REG_CAP_DATA_H, h)
+                self.hw.inject(REG_CAP_DATA_M, m)
+                self.hw.inject(REG_CAP_DATA_L, l)
+                value = read_int(dev.path / "iio:device0/in_capacitance0-capacitance2_raw")
+                self.assertAlmostEqual((value + offset) * scale, testval, 9)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_CAP_SETUP, 0x80 | 0x20)
+                #verify capacitance read disabled
+                vt_setup = mock.get_last_reg_write(REG_VT_SETUP)
+                assert(not (vt_setup & 0x80))
+                mock.reset_mock()
+
+            # Should really be testing that the voltage after application of
+            # scale and offset is correct, not raw values.
+            for h, m, l, testval in testvals:
+                # Sequence matters as we will only write registers if the change.
+                # Hence always proceed a vt read with a cap read and visa versa
+                read_int(dev.path / "iio:device0/in_voltage0_raw")
+                # value is 24 bit and driver offsets it by -0x800000.
+                self.hw.inject(REG_CAP_DATA_H, h)
+                self.hw.inject(REG_CAP_DATA_M, m)
+                self.hw.inject(REG_CAP_DATA_L, l)
+                value = read_int(dev.path / "iio:device0/in_capacitance1-capacitance3_raw")
+                self.assertAlmostEqual((value + offset) * scale, testval, 9)
+                mock = self.hw.update_mock()
+                mock.assert_last_reg_write(self, REG_CAP_SETUP, 0x80 | 0x60)
+                #verify capacitance read disabled
+                vt_setup = mock.get_last_reg_write(REG_VT_SETUP)
+                assert(not (vt_setup & 0x80))
+                mock.reset_mock()
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling.
  2022-04-18 19:28 ` [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling Jonathan Cameron
@ 2022-04-26 10:58   ` Andy Shevchenko
  0 siblings, 0 replies; 21+ messages in thread
From: Andy Shevchenko @ 2022-04-26 10:58 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-iio, Michael Hennerich, Lars-Peter Clausen,
	Vincent Whitchurch, Jonathan Cameron

On Wed, Apr 20, 2022 at 11:23 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
> Change from fiddly local implementation of be24 to cpu endian conversion
> by reading into a 3 byte buffer and using get_unaligned_be24()
>
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  drivers/staging/iio/cdc/ad7746.c | 9 +++------
>  1 file changed, 3 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
> index 71c709771676..1f510610ed32 100644
> --- a/drivers/staging/iio/cdc/ad7746.c
> +++ b/drivers/staging/iio/cdc/ad7746.c
> @@ -95,10 +95,7 @@ struct ad7746_chip_info {
>         u8      capdac[2][2];
>         s8      capdac_set;
>
> -       union {
> -               __be32 d32;
> -               u8 d8[4];
> -       } data ____cacheline_aligned;
> +       u8 data[3] ____cacheline_aligned;
>  };
>
>  enum ad7746_chan {
> @@ -547,12 +544,12 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
>
>                 ret = i2c_smbus_read_i2c_block_data(chip->client,
>                                                     chan->address >> 8, 3,

At the same time it makes now sense to use sizeof() here.

> -                                                   &chip->data.d8[1]);
> +                                                   chip->data);
>
>                 if (ret < 0)
>                         goto out;
>
> -               *val = (be32_to_cpu(chip->data.d32) & 0xFFFFFF) - 0x800000;
> +               *val = get_unaligned_be24(chip->data) - 0x800000;
>
>                 switch (chan->type) {
>                 case IIO_TEMP:
> --
> 2.35.3
>


-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2
  2022-04-18 19:28 ` [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 Jonathan Cameron
@ 2022-04-26 12:00   ` Peter Rosin
  2022-04-28 18:49     ` Jonathan Cameron
  0 siblings, 1 reply; 21+ messages in thread
From: Peter Rosin @ 2022-04-26 12:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan.Cameron, lars, linux-iio, michael.hennerich, vincent.whitchurch

Hi!

> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 
> With some high resolution sensors such as the ad7746 the
> build up of error when multiplying the _raw and _scale
> values together can be significant.  Reduce this affect by
> providing additional resolution in both calculation and
> formatting of result.
> 
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
>  1 file changed, 15 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 2f48e9a97274..d831835770da 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -683,14 +683,21 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
>  		else
>  			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
>  					     abs(tmp1));
> -	case IIO_VAL_FRACTIONAL_LOG2:
> -		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
> -		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
> -		if (tmp0 == 0 && tmp2 < 0)
> -			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
> -		else
> -			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> -					     abs(tmp1));
> +	case IIO_VAL_FRACTIONAL_LOG2: {
> +		u64 t1, t2;
> +		int integer;
> +		bool neg = vals[0] < 0;
> +
> +		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);

While the multiplication is safe if val[0] is 24 bits or less, I
wonder if that's OK for *all* the existing stuff? I would have guessed
that something somewhere needs more bits than that? Did you consider the
rescaler? And if everything currently really does fit in 24 bits, do we
really want to make it difficult to add something beyond 24 bits later
on?

Cheers,
Peter

> +		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
> +		if (integer == 0 && neg)
> +			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
> +		if (neg)
> +			integer *= -1;
> +		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
> +				     abs(t2));
> +		}
> +	}
>  	case IIO_VAL_INT_MULTIPLE:
>  	{
>  		int i;
> -- 
> 2.35.3
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2
  2022-04-26 12:00   ` Peter Rosin
@ 2022-04-28 18:49     ` Jonathan Cameron
  0 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2022-04-28 18:49 UTC (permalink / raw)
  To: Peter Rosin
  Cc: Jonathan.Cameron, lars, linux-iio, michael.hennerich, vincent.whitchurch

On Tue, 26 Apr 2022 14:00:49 +0200
Peter Rosin <peda@axentia.se> wrote:

> Hi!
> 
> > From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > 
> > With some high resolution sensors such as the ad7746 the
> > build up of error when multiplying the _raw and _scale
> > values together can be significant.  Reduce this affect by
> > providing additional resolution in both calculation and
> > formatting of result.
> > 
> > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > ---
> >  drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
> >  1 file changed, 15 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 2f48e9a97274..d831835770da 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -683,14 +683,21 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
> >  		else
> >  			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> >  					     abs(tmp1));
> > -	case IIO_VAL_FRACTIONAL_LOG2:
> > -		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
> > -		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
> > -		if (tmp0 == 0 && tmp2 < 0)
> > -			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
> > -		else
> > -			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> > -					     abs(tmp1));
> > +	case IIO_VAL_FRACTIONAL_LOG2: {
> > +		u64 t1, t2;
> > +		int integer;
> > +		bool neg = vals[0] < 0;
> > +
> > +		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);  
> 
> While the multiplication is safe if val[0] is 24 bits or less, I
> wonder if that's OK for *all* the existing stuff? I would have guessed
> that something somewhere needs more bits than that? Did you consider the
> rescaler? And if everything currently really does fit in 24 bits, do we
> really want to make it difficult to add something beyond 24 bits later
> on?

Good point. Perhaps we can do something a bit nefarious and check that
val[0] is sufficiently small and if not fallback to lower precision
as we had before?  Or if adventurous adjust the precision automatically
so we can get as many digits (at least 9) as can be computed without
overflow...  For now, I think 2 steps is enough though.

Jonathan
 
> 
> Cheers,
> Peter
> 
> > +		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
> > +		if (integer == 0 && neg)
> > +			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
> > +		if (neg)
> > +			integer *= -1;
> > +		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
> > +				     abs(t2));
> > +		}
> > +	}
> >  	case IIO_VAL_INT_MULTIPLE:
> >  	{
> >  		int i;
> > -- 
> > 2.35.3
> >   
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-04-28 18:41 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-18 19:28 [PATCH 00/17] staging/iio: Clean up AD7746 CDC driver and move from staging Jonathan Cameron
2022-04-18 19:28 ` [PATCH 01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 Jonathan Cameron
2022-04-26 12:00   ` Peter Rosin
2022-04-28 18:49     ` Jonathan Cameron
2022-04-18 19:28 ` [PATCH 02/17] iio: ABI: Fix wrong format of differential capacitance channel ABI Jonathan Cameron
2022-04-18 19:28 ` [PATCH 03/17] staging: iio: cdc: ad7746: Use explicit be24 handling Jonathan Cameron
2022-04-26 10:58   ` Andy Shevchenko
2022-04-18 19:28 ` [PATCH 04/17] staging: iio: cdc: ad7746: Push handling of supply voltage scale to userspace Jonathan Cameron
2022-04-18 19:28 ` [PATCH 05/17] staging: iio: cdc: ad7746: Use local buffer for multi byte reads Jonathan Cameron
2022-04-18 19:28 ` [PATCH 06/17] staging: iio: cdc: ad7746: Factor out ad7746_read_channel() Jonathan Cameron
2022-04-18 19:28 ` [PATCH 07/17] staging: iio: cdc: ad7764: Push locking down into case statements in read/write_raw Jonathan Cameron
2022-04-18 19:28 ` [PATCH 08/17] staging: iio: cdc: ad7746: Break up use of chan->address and use FIELD_PREP etc Jonathan Cameron
2022-04-18 19:28 ` [PATCH 09/17] staging: iio: cdc: ad7746: Drop usused i2c_set_clientdata() Jonathan Cameron
2022-04-18 19:29 ` [PATCH 10/17] staging: iio: cdc: ad7746: Use _raw and _scale for temperature channels Jonathan Cameron
2022-04-18 19:29 ` [PATCH 11/17] iio: core: Introduce _inputoffset for differential channels Jonathan Cameron
2022-04-18 19:29 ` [PATCH 12/17] staging: iio: cdc: ad7746: Switch from _offset to " Jonathan Cameron
2022-04-18 19:29 ` [PATCH 13/17] staging: iio: cdc: ad7746: Use read_avail() rather than opencoding Jonathan Cameron
2022-04-18 19:29 ` [PATCH 14/17] staging: iio: ad7746: White space cleanup Jonathan Cameron
2022-04-18 19:29 ` [PATCH 15/17] iio: cdc: ad7746: Add device specific ABI documentation Jonathan Cameron
2022-04-18 19:29 ` [PATCH 16/17] iio: cdc: ad7746: Move driver out of staging Jonathan Cameron
2022-04-18 19:29 ` [PATCH 17/17] RFC: iio: cdc: ad7746: Add roadtest Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.