* [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver
@ 2026-10-31 15:13 Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats Victor Chernyakin
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Victor Chernyakin @ 2026-10-31 15:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, linux-kernel, Victor Chernyakin
This series adds support for the FDC1004 4-channel I2C capacitive sensor.
This sensor can report single-ended measurements of any one of its
channels, as well as differential measurements between any pair
of them. It has a measurement range of +-15 pF and a resolution of 0.5 fF.
Patch 1 prepares iio_format_value to handle IIO_VAL_DECIMAL64_* formats
introduced in Patch 2.
Patch 2 adds higher-precision IIO_VAL_DECIMAL64_* formats, used
in the driver.
Patch 3 adds the devicetree bindings.
Patch 4 adds the driver itself.
Tested with the sensor connected over an i2c-tiny-usb adapter.
---
Changes in v4:
- Patch 1
- Moved the floor(log10(S64_MAX)) constant to iio.h, renamed it to IIO_INTLOG10_S64_MAX
- Added corresponding floor(log10(U64_MAX)) constant
- Patch 4
- Added "select REGMAP_I2C" to Kconfig
- Style/whitespace tweaks
- Link to v3: https://lore.kernel.org/SN1PR12MB252882C0E885BF3368E036E5A5612@SN1PR12MB2528.namprd12.prod.outlook.com
---
Changes in v3:
Split Patch 1 into two patches.
- Patch 1
- Expanded the comment above abs_s64_no_overflow()
- Patch 4 (previously Patch 3)
- Added comment on what a negative capacitance bias is used for
- Reduced some line lengths by introducing a variable with a short name
- Joined consecutive assignments into single ones
- Fixed indentation
- Link to v2: https://lore.kernel.org/SN1PR12MB2528DD1E6067CC4C0B76746FA5622@SN1PR12MB2528.namprd12.prod.outlook.com/
---
Changes in v2:
- Patch 1
- Added tests
- Fixed overflow in iio_format_value on 10^scale and UB on abs(LLONG_MIN)
- Patch 2
- Made the power supply mandatory
- Patch 3
- Switched from direct I2C calls to regmap
- Added of device table
- Removed scan indexes
- Build up complex values incrementally
- Adjusted list of includes
- More designed initializers
- Various style/whitespace tweaks
- Link to v1: https://lore.kernel.org/SN1PR12MB25286BE4E1EA3B75F8E563A9A5652@SN1PR12MB2528.namprd12.prod.outlook.com
---
Victor Chernyakin (4):
iio: prepare iio_format_value to handle higher-precision
IIO_VAL_DECIMAL64_* formats
iio: add IIO_VAL_DECIMAL64_{ATTO,ZEPTO,YOCTO} formats
dt-bindings: iio: cdc: add Texas Instruments FDC1004
iio: cdc: add Texas Instruments FDC1004 driver
.../bindings/iio/cdc/ti,fdc1004.yaml | 46 ++
MAINTAINERS | 6 +
drivers/iio/cdc/Kconfig | 11 +
drivers/iio/cdc/Makefile | 1 +
drivers/iio/cdc/fdc1004.c | 490 ++++++++++++++++++
drivers/iio/industrialio-core.c | 30 +-
drivers/iio/test/iio-test-format.c | 16 +
include/linux/iio/iio.h | 3 +
include/linux/iio/types.h | 3 +
9 files changed, 602 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
create mode 100644 drivers/iio/cdc/fdc1004.c
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats
2026-10-31 15:13 [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
@ 2026-10-31 15:13 ` Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 2/4] iio: add IIO_VAL_DECIMAL64_{ATTO,ZEPTO,YOCTO} formats Victor Chernyakin
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Victor Chernyakin @ 2026-10-31 15:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, linux-kernel, Victor Chernyakin
Prepare iio_format_value for an upcoming patch that adds formats up
to IIO_VAL_DECIMAL64_YOCTO (a scale of 24 digits) by avoiding
computing 10^scale when doing so would overflow, and by avoiding
undefined behavior due to abs(LLONG_MIN) (which couldn't happen
previously).
Signed-off-by: Victor Chernyakin <chernyakin.victor.j@outlook.com>
---
drivers/iio/industrialio-core.c | 24 ++++++++++++++++++++----
include/linux/iio/iio.h | 3 +++
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 767a7794624a..42e92a79d1ee 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -653,6 +653,16 @@ int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix)
}
EXPORT_SYMBOL(iio_read_mount_matrix);
+/*
+ * Regular abs(LLONG_MIN) causes signed overflow, which is undefined
+ * behavior. This variant avoids that by performing the computation in an
+ * unsigned type, for which negation is always well-defined.
+ */
+static inline u64 abs_s64_no_overflow(s64 x)
+{
+ return x < 0 ? -(u64)x : (u64)x;
+}
+
static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
int size, const int *vals)
{
@@ -720,13 +730,19 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
int scale = type - IIO_VAL_DECIMAL64_BASE;
s64 frac;
- tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]),
- int_pow(10, scale), &frac);
+ if (scale <= IIO_INTLOG10_S64_MAX) {
+ tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]),
+ int_pow(10, scale), &frac);
+ } else {
+ tmp2 = 0;
+ frac = iio_val_s64_compose(vals[0], vals[1]);
+ }
+
if (tmp2 == 0 && frac < 0)
l += sysfs_emit_at(buf, offset, "-");
- l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale,
- abs(frac));
+ l += sysfs_emit_at(buf, offset + l, "%lld.%0*llu", tmp2, scale,
+ abs_s64_no_overflow(frac));
return l;
}
default:
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 711c00f67371..7cb72ae282b6 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -1132,4 +1132,7 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
*/
#define IIO_M_S_2_TO_G(ms2) (((ms2) * 100000ULL + 980665ULL / 2) / 980665ULL)
+#define IIO_INTLOG10_S64_MAX 18
+#define IIO_INTLOG10_U64_MAX 19
+
#endif /* _INDUSTRIAL_IO_H_ */
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 2/4] iio: add IIO_VAL_DECIMAL64_{ATTO,ZEPTO,YOCTO} formats
2026-10-31 15:13 [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats Victor Chernyakin
@ 2026-10-31 15:13 ` Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 3/4] dt-bindings: iio: cdc: add Texas Instruments FDC1004 Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
3 siblings, 0 replies; 6+ messages in thread
From: Victor Chernyakin @ 2026-10-31 15:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, linux-kernel, Victor Chernyakin
The highest-precision format IIO has right now is IIO_VAL_DECIMAL64_FEMTO,
which gives 15 decimal digits of precision after the decimal point. This
is not enough for the upcoming TI FDC1004 driver, which needs 22 digits
of precision to exactly express its scale value. This patch adds higher-
precision formats, up to IIO_VAL_DECIMAL64_YOCTO (24 digits of precision).
Signed-off-by: Victor Chernyakin <chernyakin.victor.j@outlook.com>
---
drivers/iio/industrialio-core.c | 6 ++++++
drivers/iio/test/iio-test-format.c | 16 ++++++++++++++++
include/linux/iio/types.h | 3 +++
3 files changed, 25 insertions(+)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 42e92a79d1ee..21594318bb9e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -726,6 +726,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
case IIO_VAL_DECIMAL64_NANO:
case IIO_VAL_DECIMAL64_PICO:
case IIO_VAL_DECIMAL64_FEMTO:
+ case IIO_VAL_DECIMAL64_ATTO:
+ case IIO_VAL_DECIMAL64_ZEPTO:
+ case IIO_VAL_DECIMAL64_YOCTO:
{
int scale = type - IIO_VAL_DECIMAL64_BASE;
s64 frac;
@@ -1050,6 +1053,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
case IIO_VAL_DECIMAL64_NANO:
case IIO_VAL_DECIMAL64_PICO:
case IIO_VAL_DECIMAL64_FEMTO:
+ case IIO_VAL_DECIMAL64_ATTO:
+ case IIO_VAL_DECIMAL64_ZEPTO:
+ case IIO_VAL_DECIMAL64_YOCTO:
dec_scale = type - IIO_VAL_DECIMAL64_BASE;
fallthrough;
case IIO_VAL_INT_64:
diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c
index e5d0a2ac4670..43a80952a50f 100644
--- a/drivers/iio/test/iio-test-format.c
+++ b/drivers/iio/test/iio-test-format.c
@@ -286,6 +286,14 @@ static void iio_test_iio_format_value_decimal_64(struct kunit *test)
/* limits */
iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_YOCTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000009223372036854775807\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_ZEPTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.009223372036854775807\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_ATTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9.223372036854775807\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_FEMTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223.372036854775807\n");
ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372.036854775807\n");
ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values);
@@ -294,6 +302,14 @@ static void iio_test_iio_format_value_decimal_64(struct kunit *test)
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854.775807\n");
iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_YOCTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000009223372036854775808\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_ZEPTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.009223372036854775808\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_ATTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9.223372036854775808\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_FEMTO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223.372036854775808\n");
ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372.036854775808\n");
ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values);
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index d8944c9e5e90..2ef9b2a8b119 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -43,6 +43,9 @@ enum iio_event_info {
#define IIO_VAL_DECIMAL64_NANO (IIO_VAL_DECIMAL64_BASE + 9)
#define IIO_VAL_DECIMAL64_PICO (IIO_VAL_DECIMAL64_BASE + 12)
#define IIO_VAL_DECIMAL64_FEMTO (IIO_VAL_DECIMAL64_BASE + 15)
+#define IIO_VAL_DECIMAL64_ATTO (IIO_VAL_DECIMAL64_BASE + 18)
+#define IIO_VAL_DECIMAL64_ZEPTO (IIO_VAL_DECIMAL64_BASE + 21)
+#define IIO_VAL_DECIMAL64_YOCTO (IIO_VAL_DECIMAL64_BASE + 24)
static inline s64 iio_val_s64_compose(s32 val0, s32 val1)
{
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 3/4] dt-bindings: iio: cdc: add Texas Instruments FDC1004
2026-10-31 15:13 [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 2/4] iio: add IIO_VAL_DECIMAL64_{ATTO,ZEPTO,YOCTO} formats Victor Chernyakin
@ 2026-10-31 15:13 ` Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
3 siblings, 0 replies; 6+ messages in thread
From: Victor Chernyakin @ 2026-10-31 15:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-iio, devicetree, linux-kernel, Victor Chernyakin,
Krzysztof Kozlowski
Add device tree bindings for the Texas Instruments FDC1004
capacitive sensor.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Victor Chernyakin <chernyakin.victor.j@outlook.com>
---
.../bindings/iio/cdc/ti,fdc1004.yaml | 46 +++++++++++++++++++
MAINTAINERS | 5 ++
2 files changed, 51 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
diff --git a/Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml b/Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
new file mode 100644
index 000000000000..8366afd025a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/cdc/ti,fdc1004.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments FDC1004 capacitive sensor
+
+maintainers:
+ - Victor Chernyakin <chernyakin.victor.j@outlook.com>
+
+description: |
+ The FDC1004 is a high-resolution 4-channel capacitance to digital converter
+ that works over I2C.
+
+ https://www.ti.com/product/FDC1004
+
+properties:
+ compatible:
+ const: ti,fdc1004
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cdc@50 {
+ compatible = "ti,fdc1004";
+ reg = <0x50>;
+ vdd-supply = <&vdd_3v3>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 360977678f70..69c23ae21016 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27388,6 +27388,11 @@ S: Maintained
F: drivers/net/ethernet/ti/cpsw*
F: drivers/net/ethernet/ti/davinci*
+TI FDC1004 DRIVER
+M: Victor Chernyakin <chernyakin.victor.j@outlook.com>
+S: Maintained
+F: Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
+
TI FLASH MEDIA MEMORYSTICK/MMC DRIVERS
M: Alex Dubov <oakad@yahoo.com>
S: Maintained
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver
2026-10-31 15:13 [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
` (2 preceding siblings ...)
2026-10-31 15:13 ` [PATCH v4 3/4] dt-bindings: iio: cdc: add Texas Instruments FDC1004 Victor Chernyakin
@ 2026-10-31 15:13 ` Victor Chernyakin
2026-11-02 2:57 ` kernel test robot
3 siblings, 1 reply; 6+ messages in thread
From: Victor Chernyakin @ 2026-10-31 15:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, linux-kernel, Victor Chernyakin
The FDC1004 is a high-precision 4-channel capacitive sensor that talks
over I2C. It can report the values of each of its 4 channels individually,
as well as the difference between any two of them.
This initial driver allows performing single reads and configuring
the sensor's various settings (sample frequency, calibration
gain/offset, etc.). It could be extended with support for triggered
buffers, repeated measurement mode, and/or power management.
Signed-off-by: Victor Chernyakin <chernyakin.victor.j@outlook.com>
---
MAINTAINERS | 1 +
drivers/iio/cdc/Kconfig | 11 +
drivers/iio/cdc/Makefile | 1 +
drivers/iio/cdc/fdc1004.c | 490 ++++++++++++++++++++++++++++++++++++++
4 files changed, 503 insertions(+)
create mode 100644 drivers/iio/cdc/fdc1004.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 69c23ae21016..4d926c47ad8d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27392,6 +27392,7 @@ TI FDC1004 DRIVER
M: Victor Chernyakin <chernyakin.victor.j@outlook.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/cdc/ti,fdc1004.yaml
+F: drivers/iio/cdc/fdc1004.c
TI FLASH MEDIA MEMORYSTICK/MMC DRIVERS
M: Alex Dubov <oakad@yahoo.com>
diff --git a/drivers/iio/cdc/Kconfig b/drivers/iio/cdc/Kconfig
index e0a5ce66a984..1e9015fb5ae6 100644
--- a/drivers/iio/cdc/Kconfig
+++ b/drivers/iio/cdc/Kconfig
@@ -24,4 +24,15 @@ config AD7746
To compile this driver as a module, choose M here: the
module will be called ad7746.
+config FDC1004
+ tristate "Texas Instruments FDC1004 capacitive sensor driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the Texas Instruments FDC1004
+ capacitive sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fdc1004.
+
endmenu
diff --git a/drivers/iio/cdc/Makefile b/drivers/iio/cdc/Makefile
index 41db756d8020..88253f33c5a7 100644
--- a/drivers/iio/cdc/Makefile
+++ b/drivers/iio/cdc/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_AD7150) += ad7150.o
obj-$(CONFIG_AD7746) += ad7746.o
+obj-$(CONFIG_FDC1004) += fdc1004.o
diff --git a/drivers/iio/cdc/fdc1004.c b/drivers/iio/cdc/fdc1004.c
new file mode 100644
index 000000000000..442aa020ffe9
--- /dev/null
+++ b/drivers/iio/cdc/fdc1004.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments FDC1004 capacitive sensor driver
+ *
+ * Copyright (c) 2026 Victor Chernyakin <chernyakin.victor.j@outlook.com>
+ *
+ * Datasheet: https://www.ti.com/lit/gpn/FDC1004
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define FDC1004_REG_MEASn_MSB(n) (2 * (n))
+#define FDC1004_REG_MEASn_LSB(n) (2 * (n) + 1)
+#define FDC1004_REG_CONF_MEASn(n) (0x08 + (n))
+#define FDC1004_REG_FDC_CONF 0x0C
+#define FDC1004_REG_OFFSET_CAL_CINn(n) (0x0D + (n))
+#define FDC1004_REG_GAIN_CAL_CINn(n) (0x11 + (n))
+
+#define FDC1004_CONF_MEAS_CHA_MASK GENMASK(15, 13)
+#define FDC1004_CONF_MEAS_CHB_MASK GENMASK(12, 10)
+#define FDC1004_CONF_MEAS_CHB_CAPDAC 0b100
+#define FDC1004_CONF_MEAS_CAPDAC_MASK GENMASK(9, 5)
+
+#define FDC1004_FDC_CONF_DONE_n_MASK(n) BIT(3 - (n))
+#define FDC1004_FDC_CONF_MEAS_n_MASK(n) BIT(7 - (n))
+#define FDC1004_FDC_CONF_RATE_MASK GENMASK(11, 10)
+#define FDC1004_FDC_CONF_RST_MASK BIT(15)
+
+static const int fdc1004_samp_freqs[] = { 100, 200, 400 };
+
+/*
+ * MEASn_{MSB,LSB}: 24 bits, signed. Format: IIO_VAL_DECIMAL64_YOCTO.
+ * 1 step = 1/(2^19) pF = 1/(1000 * 2^19) nF = 0.000000001907348632812500 nF.
+ */
+static const s64 fdc1004_scale = 1907348632812500;
+
+/*
+ * CAPDAC field: 5 bits, unsigned. Format: IIO_VAL_INT.
+ * 1 step = 3.125 pF = 3.125 / 1/(2^19) raw value steps = 1638400 raw value steps.
+ */
+static const int fdc1004_offset[] = {
+ 0x00 * 1638400, /* min */
+ 0x01 * 1638400, /* step */
+ 0x1F * 1638400, /* max */
+};
+
+/*
+ * OFFSET_CAL_CINn: 16 bits, signed. Format: IIO_VAL_DECIMAL64_FEMTO.
+ * 1 step = 1/(2^11) pF = 1/(1000 * 2^11) nF = 0.000000488281250 nF.
+ * Negative values are used to cancel out any parasitic capacitance
+ * of the sensor's external circuitry.
+ */
+static const s64 fdc1004_calibbias[] = {
+ -0x8000 * 488281250UL, /* min */
+ +0x0001 * 488281250UL, /* step */
+ +0x7FFF * 488281250UL, /* max */
+};
+
+/*
+ * GAIN_CAL_CINn: 16 bits, unsigned. Format: IIO_VAL_DECIMAL64_FEMTO.
+ * 1 step = 1/(2^14) pF/pF = 0.000061035156250 pF/pF.
+ */
+static const s64 fdc1004_calibscale[] = {
+ 0x0000 * 61035156250UL, /* min */
+ 0x0001 * 61035156250UL, /* step */
+ 0xFFFF * 61035156250UL, /* max */
+};
+
+static const struct regmap_range fdc1004_readable_reg_ranges[] = {
+ regmap_reg_range(FDC1004_REG_MEASn_MSB(0), FDC1004_REG_GAIN_CAL_CINn(3)),
+};
+
+static const struct regmap_access_table fdc1004_readable_reg_table = {
+ .yes_ranges = fdc1004_readable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fdc1004_readable_reg_ranges),
+};
+
+static const struct regmap_range fdc1004_writeable_reg_ranges[] = {
+ regmap_reg_range(FDC1004_REG_CONF_MEASn(0), FDC1004_REG_GAIN_CAL_CINn(3)),
+};
+
+static const struct regmap_access_table fdc1004_writeable_reg_table = {
+ .yes_ranges = fdc1004_writeable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fdc1004_writeable_reg_ranges),
+};
+
+static const struct regmap_range fdc1004_volatile_reg_ranges[] = {
+ regmap_reg_range(FDC1004_REG_MEASn_MSB(0), FDC1004_REG_MEASn_LSB(3)),
+ regmap_reg_range(FDC1004_REG_FDC_CONF, FDC1004_REG_FDC_CONF),
+};
+
+static const struct regmap_access_table fdc1004_volatile_reg_table = {
+ .yes_ranges = fdc1004_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fdc1004_volatile_reg_ranges),
+};
+
+static const struct regmap_config fdc1004_regmap_config = {
+ .name = "fdc1004",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = FDC1004_REG_GAIN_CAL_CINn(3),
+ .rd_table = &fdc1004_readable_reg_table,
+ .wr_table = &fdc1004_writeable_reg_table,
+ .volatile_table = &fdc1004_volatile_reg_table,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+struct fdc1004_data {
+ struct regmap *regmap;
+
+ /*
+ * Serializes accesses to the chip's measurement and config
+ * registers, and to the other struct members below.
+ */
+ struct mutex lock;
+
+ u8 offsets[4];
+ u8 samp_freq_index;
+};
+
+static int fdc1004_soft_reset(struct fdc1004_data *data)
+{
+ struct regmap *map = data->regmap;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_write(map, FDC1004_REG_FDC_CONF, FDC1004_FDC_CONF_RST_MASK);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(map, FDC1004_REG_FDC_CONF, reg_val,
+ !(ret & FDC1004_FDC_CONF_RST_MASK),
+ 1 * USEC_PER_MSEC, 200 * USEC_PER_MSEC);
+}
+
+static int fdc1004_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct fdc1004_data *data = iio_priv(indio_dev);
+ struct regmap *map = data->regmap;
+ unsigned int reg_val;
+ u16 reg_vals[2];
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ iio_val_s64_decompose(fdc1004_scale, val, val2);
+ return IIO_VAL_DECIMAL64_YOCTO;
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&data->lock);
+
+ /* Configure the measurement we want. */
+ reg_val = FIELD_PREP(FDC1004_CONF_MEAS_CHA_MASK, chan->channel) |
+ FIELD_PREP(FDC1004_CONF_MEAS_CHB_MASK, chan->channel2) |
+ FIELD_PREP(FDC1004_CONF_MEAS_CAPDAC_MASK,
+ data->offsets[chan->channel]);
+ ret = regmap_write(map, FDC1004_REG_CONF_MEASn(0), reg_val);
+ if (ret)
+ return ret;
+
+ /*
+ * Start the measurement. The "+ 1" is because the chip
+ * wants a 1-indexed sampling frequency.
+ */
+ reg_val = FDC1004_FDC_CONF_MEAS_n_MASK(0) |
+ FIELD_PREP(FDC1004_FDC_CONF_RATE_MASK,
+ data->samp_freq_index + 1);
+ ret = regmap_write(map, FDC1004_REG_FDC_CONF, reg_val);
+ if (ret)
+ return ret;
+
+ /*
+ * Wait for the measurement to complete.
+ * The slowest sampling frequency the chip supports is 100 SPS,
+ * i.e. 1 sample every 10 ms, so our timeout can't be shorter
+ * than that. Go with 40 ms to be safe.
+ */
+ ret = regmap_read_poll_timeout(map, FDC1004_REG_FDC_CONF, reg_val,
+ reg_val & FDC1004_FDC_CONF_DONE_n_MASK(0),
+ 1 * USEC_PER_MSEC, 40 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ /*
+ * Now read the two registers making up the measurement.
+ * The datasheet says to read MSB first, LSB second.
+ */
+ ret = regmap_bulk_read(map, FDC1004_REG_MEASn_MSB(0),
+ reg_vals, ARRAY_SIZE(reg_vals));
+ if (ret)
+ return ret;
+
+ *val = (sign_extend32(reg_vals[0], 15) << 8) | (reg_vals[1] >> 8);
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&data->lock);
+
+ *val = fdc1004_samp_freqs[data->samp_freq_index];
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_OFFSET: {
+ guard(mutex)(&data->lock);
+
+ *val = data->offsets[chan->channel] * fdc1004_offset[1];
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(map, FDC1004_REG_OFFSET_CAL_CINn(chan->channel),
+ ®_val);
+ if (ret)
+ return ret;
+
+ iio_val_s64_decompose(sign_extend64(reg_val, 15) *
+ fdc1004_calibbias[1], val, val2);
+ return IIO_VAL_DECIMAL64_FEMTO;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = regmap_read(map, FDC1004_REG_GAIN_CAL_CINn(chan->channel),
+ ®_val);
+ if (ret)
+ return ret;
+
+ iio_val_s64_decompose(reg_val * fdc1004_calibscale[1], val, val2);
+ return IIO_VAL_DECIMAL64_FEMTO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const int fdc1004_calibbias_decomposed[] = {
+ lower_32_bits(fdc1004_calibbias[0]), upper_32_bits(fdc1004_calibbias[0]),
+ lower_32_bits(fdc1004_calibbias[1]), upper_32_bits(fdc1004_calibbias[1]),
+ lower_32_bits(fdc1004_calibbias[2]), upper_32_bits(fdc1004_calibbias[2]),
+};
+
+static const int fdc1004_calibscale_decomposed[] = {
+ lower_32_bits(fdc1004_calibscale[0]), upper_32_bits(fdc1004_calibscale[0]),
+ lower_32_bits(fdc1004_calibscale[1]), upper_32_bits(fdc1004_calibscale[1]),
+ lower_32_bits(fdc1004_calibscale[2]), upper_32_bits(fdc1004_calibscale[2]),
+};
+
+static int fdc1004_read_avail(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = fdc1004_samp_freqs;
+ *length = ARRAY_SIZE(fdc1004_samp_freqs);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OFFSET:
+ *vals = fdc1004_offset;
+ *length = ARRAY_SIZE(fdc1004_offset);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = fdc1004_calibbias_decomposed;
+ *length = ARRAY_SIZE(fdc1004_calibbias_decomposed);
+ *type = IIO_VAL_DECIMAL64_FEMTO;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *vals = fdc1004_calibscale_decomposed;
+ *length = ARRAY_SIZE(fdc1004_calibscale_decomposed);
+ *type = IIO_VAL_DECIMAL64_FEMTO;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fdc1004_write_raw_get_fmt(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ case IIO_CHAN_INFO_OFFSET:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return IIO_VAL_DECIMAL64_FEMTO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fdc1004_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct fdc1004_data *data = iio_priv(indio_dev);
+ struct regmap *map = data->regmap;
+ s64 val64;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (unsigned int i = 0; i < ARRAY_SIZE(fdc1004_samp_freqs); i++) {
+ if (fdc1004_samp_freqs[i] == val) {
+ guard(mutex)(&data->lock);
+
+ data->samp_freq_index = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_OFFSET: {
+ if (val < fdc1004_offset[0] || val > fdc1004_offset[2] ||
+ val % fdc1004_offset[1])
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+
+ data->offsets[chan->channel] = val / fdc1004_offset[1];
+ return 0;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val64 = iio_val_s64_compose(val, val2);
+ if (val64 < fdc1004_calibbias[0] || val64 > fdc1004_calibbias[2])
+ return -EINVAL;
+
+ return regmap_write(map, FDC1004_REG_OFFSET_CAL_CINn(chan->channel),
+ DIV_ROUND_CLOSEST(val64, fdc1004_calibbias[1]));
+ case IIO_CHAN_INFO_CALIBSCALE:
+ val64 = iio_val_s64_compose(val, val2);
+ if (val64 < fdc1004_calibscale[0] || val64 > fdc1004_calibscale[2])
+ return -EINVAL;
+
+ return regmap_write(map, FDC1004_REG_GAIN_CAL_CINn(chan->channel),
+ DIV_ROUND_CLOSEST(val64, fdc1004_calibscale[1]));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fdc1004_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct fdc1004_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+
+ if (readval)
+ return regmap_read(data->regmap, reg, readval);
+
+ return regmap_write(data->regmap, reg, writeval);
+}
+
+#define FDC1004_SINGLE_ENDED_CAPACITANCE_CHANNEL(_channel) \
+ { \
+ .type = IIO_CAPACITANCE, \
+ .indexed = true, \
+ .channel = _channel, \
+ .channel2 = FDC1004_CONF_MEAS_CHB_CAPDAC, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ }
+
+#define FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(_chan1, _chan2) \
+ { \
+ .type = IIO_CAPACITANCE, \
+ .indexed = true, \
+ .differential = true, \
+ .channel = _chan1, \
+ .channel2 = _chan2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ }
+
+static const struct iio_chan_spec fdc1004_channels[] = {
+ FDC1004_SINGLE_ENDED_CAPACITANCE_CHANNEL(0),
+ FDC1004_SINGLE_ENDED_CAPACITANCE_CHANNEL(1),
+ FDC1004_SINGLE_ENDED_CAPACITANCE_CHANNEL(2),
+ FDC1004_SINGLE_ENDED_CAPACITANCE_CHANNEL(3),
+
+ /* The sensor only supports differential measurements where CHA < CHB. */
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(0, 1),
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(0, 2),
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(0, 3),
+
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(1, 2),
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(1, 3),
+
+ FDC1004_DIFFERENTIAL_CAPACITANCE_CHANNEL(2, 3),
+};
+
+static const struct iio_info fdc1004_info = {
+ .read_raw = fdc1004_read_raw,
+ .read_avail = fdc1004_read_avail,
+ .write_raw = fdc1004_write_raw,
+ .write_raw_get_fmt = fdc1004_write_raw_get_fmt,
+ .debugfs_reg_access = fdc1004_debugfs_reg_access,
+};
+
+static int fdc1004_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct fdc1004_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &fdc1004_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ ret = devm_mutex_init(&client->dev, &data->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable(&client->dev, "vdd");
+ if (ret)
+ return ret;
+
+ ret = fdc1004_soft_reset(data);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "fdc1004";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &fdc1004_info;
+ indio_dev->channels = fdc1004_channels;
+ indio_dev->num_channels = ARRAY_SIZE(fdc1004_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id fdc1004_id[] = {
+ { .name = "fdc1004" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fdc1004_id);
+
+static const struct of_device_id fdc1004_of_match[] = {
+ { .compatible = "ti,fdc1004" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fdc1004_of_match);
+
+static struct i2c_driver fdc1004_driver = {
+ .driver = {
+ .name = "fdc1004",
+ .of_match_table = fdc1004_of_match,
+ },
+ .probe = fdc1004_probe,
+ .id_table = fdc1004_id,
+};
+module_i2c_driver(fdc1004_driver);
+
+MODULE_AUTHOR("Victor Chernyakin <chernyakin.victor.j@outlook.com>");
+MODULE_DESCRIPTION("Texas Instruments FDC1004 capacitive sensor driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver
2026-10-31 15:13 ` [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
@ 2026-11-02 2:57 ` kernel test robot
0 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-11-02 2:57 UTC (permalink / raw)
To: Victor Chernyakin, Jonathan Cameron
Cc: oe-kbuild-all, David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, linux-kernel, Victor Chernyakin
Hi Victor,
kernel test robot noticed the following build errors:
[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on linus/master v7.3 next-20261030]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Victor-Chernyakin/iio-prepare-iio_format_value-to-handle-higher-precision-IIO_VAL_DECIMAL64_-formats/20261031-101327
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/SN1PR12MB25281F8D83D65C87BF4B6A43A56C2%40SN1PR12MB2528.namprd12.prod.outlook.com
patch subject: [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20261102/202611021049.CWQj3jvL-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261102/202611021049.CWQj3jvL-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611021049.CWQj3jvL-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: drivers/iio/cdc/fdc1004.ko: symbol '__udivdi3' undefined!
>> ERROR: modpost: drivers/iio/cdc/fdc1004.ko: symbol '__divdi3' undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-02 2:58 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-31 15:13 [PATCH v4 0/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 2/4] iio: add IIO_VAL_DECIMAL64_{ATTO,ZEPTO,YOCTO} formats Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 3/4] dt-bindings: iio: cdc: add Texas Instruments FDC1004 Victor Chernyakin
2026-10-31 15:13 ` [PATCH v4 4/4] iio: cdc: add Texas Instruments FDC1004 driver Victor Chernyakin
2026-11-02 2:57 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®