* [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor
@ 2026-06-11 13:26 Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 1/4] iio: types: add IIO_VOLUMEFLOW channel type Wadim Mueller
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Wadim Mueller @ 2026-06-11 13:26 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar, Wadim Mueller
Hi all,
this series adds a driver for the Sensirion SLF3S family of I2C liquid
flow sensors (SLF3S-0600F / -1300F / -4000B) and a new IIO_VOLUMEFLOW
channel type. As in v3 this is posted as a fresh thread.
Dependency
----------
The volume-flow scale is reported in m^3/s as a 64-bit fixed-point value
(IIO_VAL_DECIMAL64_FEMTO), so this series depends on Rodrigo Alencar's
"ADF41513/ADF41510 PLL frequency synthesizers" series, which adds the
IIO_VAL_DECIMAL64_* core formatting (and the kstrtodec64() and
div64_s64_rem() helpers it builds on):
https://lore.kernel.org/linux-iio/20260604-adf41513-iio-driver-v16-0-1a7d09143bc2@analog.com/
Patch 3/4 adds the IIO_VAL_DECIMAL64_FEMTO format type on top of that.
Changes since v3
----------------
* volumeflow unit switched from l/s to m^3/s (SI), per Jonathan
* scale now reported as IIO_VAL_DECIMAL64_FEMTO instead of
IIO_VAL_FRACTIONAL, so the small m^3/s values (~1.7e-12 m^3/s for the
SLF3S-0600F) keep full precision; this needs the new FEMTO core type
(3/4) and the dependency above
* dt-bindings: sensirion,slf3s-1300f now serves as the fallback
compatible for the other variants (all variants are detectable from
the product-information register), per Krzysztof; Marcelo's
Reviewed-by dropped because of this change
* dt-bindings: add interrupts (maxItems: 1) back
* dt-bindings: reflow the description to 80 columns
* dt-bindings: move "F: drivers/iio/flow/slf3s.c" to the driver patch
* driver: add system PM ops following the scd30/scd4x precedent: stop
the measurement and disable the supply on suspend; power back up,
wait out the power-up time and restart with the previously active
medium on resume
* driver: if switching the medium fails after the stop command, restart
with the previous medium instead of leaving the sensor idle
* driver: a sensor reporting an unknown sub-type now falls back to the
variant named in the device tree instead of failing probe, matching
the fallback-compatible semantics
* driver: serialise the command/response exchanges with a local mutex
instead of iio_device_claim_direct() / release_direct()
* driver: issue a stop-measurement at the start of probe(), so a sensor
left in continuous mode across a warm reboot does not NACK probe
* driver: read only the 6 bytes actually used (flow + temperature) per
measurement frame instead of 9
* driver: trim the per-variant list out of the Kconfig help text
* ABI: in_volumeflow_medium[_available] documented in
Documentation/ABI/testing/sysfs-bus-iio-flow; KernelVersion 7.3
The signaling-flags word (air-in-line / high-flow / smoothing status) in
each measurement frame is intentionally not read; exposing it can be a
later follow-up.
Thanks,
Wadim
Wadim Mueller (4):
iio: types: add IIO_VOLUMEFLOW channel type
dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor
iio: core: add IIO_VAL_DECIMAL64_FEMTO format type
iio: flow: add Sensirion SLF3S liquid flow sensor driver
Documentation/ABI/testing/sysfs-bus-iio | 11 +
Documentation/ABI/testing/sysfs-bus-iio-flow | 21 +
.../bindings/iio/flow/sensirion,slf3s.yaml | 58 ++
MAINTAINERS | 8 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/flow/Kconfig | 22 +
drivers/iio/flow/Makefile | 7 +
drivers/iio/flow/slf3s.c | 521 ++++++++++++++++++
drivers/iio/industrialio-core.c | 3 +
include/linux/iio/types.h | 1 +
include/uapi/linux/iio/types.h | 1 +
tools/iio/iio_event_monitor.c | 2 +
13 files changed, 657 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-flow
create mode 100644 Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
create mode 100644 drivers/iio/flow/Kconfig
create mode 100644 drivers/iio/flow/Makefile
create mode 100644 drivers/iio/flow/slf3s.c
base-commit: 3cd8b194bf3428dfa53120fee47e827a7c495815
prerequisite-patch-id: b51a25b69f7b78155e78d1a3aab809bcb57e11ae
prerequisite-patch-id: 1b26fb01ab41218c214fa58657305437565c06f8
prerequisite-patch-id: 7745e957a25b8673c7f838a9ae7a55269cd21798
prerequisite-patch-id: 5f805ccb0be820042ac732d0d8e1b188bfd2b2bc
prerequisite-patch-id: 80967f95ecb0c10fc66b3d073e99906126d5b40b
prerequisite-patch-id: 729fdedcf2055c506693d28a5dab65a6a3791598
--
2.52.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v4 1/4] iio: types: add IIO_VOLUMEFLOW channel type
2026-06-11 13:26 [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor Wadim Mueller
@ 2026-06-11 13:26 ` Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor Wadim Mueller
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Wadim Mueller @ 2026-06-11 13:26 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar, Wadim Mueller
Add a new IIO channel type for liquid volumetric flow sensors. The
unit exposed via the standard _scale attribute is cubic metres per
second (m^3/s), following the SI convention used by the other IIO
channel types.
Update iio-core's name table, the iio_event_monitor whitelist and
the sysfs-bus-iio ABI document to match. The new _scale attribute is
folded into the existing shared _scale block; only the per-type _raw
needs a fresh entry.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++
drivers/iio/industrialio-core.c | 1 +
include/uapi/linux/iio/types.h | 1 +
tools/iio/iio_event_monitor.c | 2 ++
4 files changed, 15 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 5f87dcee7..e278fda4b 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -507,6 +507,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@@ -2458,3 +2460,12 @@ Description:
seconds, expressed as:
- a range specified as "[min step max]"
+
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_raw
+KernelVersion: 7.3
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw (unscaled) volumetric flow rate reading from the channel.
+ To convert to standard units (cubic metres per second, m^3/s)
+ apply the channel's _scale (and _offset, when present).
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 93c7b8c46..571b8ba4e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -98,6 +98,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CHROMATICITY] = "chromaticity",
[IIO_ATTENTION] = "attention",
[IIO_ALTCURRENT] = "altcurrent",
+ [IIO_VOLUMEFLOW] = "volumeflow",
};
static const char * const iio_modifier_names[] = {
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 6d269b844..49480f321 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -53,6 +53,7 @@ enum iio_chan_type {
IIO_CHROMATICITY,
IIO_ATTENTION,
IIO_ALTCURRENT,
+ IIO_VOLUMEFLOW,
};
enum iio_modifier {
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index 03ca33869..078004750 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CHROMATICITY] = "chromaticity",
[IIO_ATTENTION] = "attention",
[IIO_ALTCURRENT] = "altcurrent",
+ [IIO_VOLUMEFLOW] = "volumeflow",
};
static const char * const iio_ev_type_text[] = {
@@ -193,6 +194,7 @@ static bool event_is_known(struct iio_event_data *event)
case IIO_CHROMATICITY:
case IIO_ATTENTION:
case IIO_ALTCURRENT:
+ case IIO_VOLUMEFLOW:
break;
default:
return false;
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor
2026-06-11 13:26 [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 1/4] iio: types: add IIO_VOLUMEFLOW channel type Wadim Mueller
@ 2026-06-11 13:26 ` Wadim Mueller
2026-06-11 14:01 ` Krzysztof Kozlowski
2026-06-11 13:26 ` [PATCH v4 3/4] iio: core: add IIO_VAL_DECIMAL64_FEMTO format type Wadim Mueller
2026-06-11 13:27 ` [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver Wadim Mueller
3 siblings, 1 reply; 8+ messages in thread
From: Wadim Mueller @ 2026-06-11 13:26 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar, Wadim Mueller
Document the bindings for the Sensirion SLF3S family of digital
liquid-flow sensors on I2C. The family currently covers the
SLF3S-0600F, SLF3S-1300F and SLF3S-4000B variants.
All variants share the same register map and are fully detectable
from the product-information register at probe time, so
sensirion,slf3s-1300f serves as the fallback compatible for the
other variants.
The active calibration medium (water / IPA) is runtime-switchable
via the in_volumeflow_medium sysfs attribute and therefore not a
DT property.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
.../bindings/iio/flow/sensirion,slf3s.yaml | 58 +++++++++++++++++++
MAINTAINERS | 7 +++
2 files changed, 65 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
diff --git a/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
new file mode 100644
index 000000000..c054a505b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/flow/sensirion,slf3s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sensirion SLF3S liquid flow sensor
+
+maintainers:
+ - Wadim Mueller <wafgo01@gmail.com>
+
+description:
+ Family of digital liquid-flow sensors from Sensirion with I2C interface. All
+ family members share the same register map; sub-types differ only in the flow
+ scale factor and the calibrated measurement range. The sub-type can be
+ identified from the product-information register.
+
+properties:
+ compatible:
+ oneOf:
+ - const: sensirion,slf3s-1300f
+ - items:
+ - enum:
+ - sensirion,slf3s-0600f
+ - sensirion,slf3s-4000b
+ - const: sensirion,slf3s-1300f
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flow-sensor@8 {
+ compatible = "sensirion,slf3s-0600f", "sensirion,slf3s-1300f";
+ reg = <0x08>;
+ vdd-supply = <®_3v3>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 06a8c7457..cdc18a601 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24187,6 +24187,13 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40
F: drivers/iio/chemical/sgp40.c
+SENSIRION SLF3S LIQUID FLOW SENSOR DRIVER
+M: Wadim Mueller <wafgo01@gmail.com>
+R: Maxwell Doose <m32285159@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
+
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
S: Maintained
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 3/4] iio: core: add IIO_VAL_DECIMAL64_FEMTO format type
2026-06-11 13:26 [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 1/4] iio: types: add IIO_VOLUMEFLOW channel type Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor Wadim Mueller
@ 2026-06-11 13:26 ` Wadim Mueller
2026-06-11 13:27 ` [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver Wadim Mueller
3 siblings, 0 replies; 8+ messages in thread
From: Wadim Mueller @ 2026-06-11 13:26 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar, Wadim Mueller
Extend the IIO_VAL_DECIMAL64_* family with a femto-scaled variant
(scale 15), following the existing MILLI/MICRO/NANO/PICO pattern. Both
the read formatting path in __iio_format_value() and the write parsing
path in iio_write_channel_info() (via kstrtodec64()) already derive
their scale from "type - IIO_VAL_DECIMAL64_BASE", so the new type only
needs to be added to the respective switch cases.
This is needed by drivers reporting very small SI quantities where the
existing pico scale loses precision. For example the Sensirion SLF3S
liquid flow sensor reports its volume-flow scale in m^3/s, where the
SLF3S-0600F scale is ~1.667e-12 m^3/s: at pico scale only a single
significant digit survives, whereas femto scale preserves the full
sensor resolution.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
drivers/iio/industrialio-core.c | 2 ++
include/linux/iio/types.h | 1 +
2 files changed, 3 insertions(+)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 571b8ba4e..685661cd8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -713,6 +713,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
case IIO_VAL_DECIMAL64_MICRO:
case IIO_VAL_DECIMAL64_NANO:
case IIO_VAL_DECIMAL64_PICO:
+ case IIO_VAL_DECIMAL64_FEMTO:
{
int scale = type - IIO_VAL_DECIMAL64_BASE;
s64 frac;
@@ -1030,6 +1031,7 @@ static ssize_t iio_write_channel_info(struct device *dev,
case IIO_VAL_DECIMAL64_MICRO:
case IIO_VAL_DECIMAL64_NANO:
case IIO_VAL_DECIMAL64_PICO:
+ case IIO_VAL_DECIMAL64_FEMTO:
dec_scale = type - IIO_VAL_DECIMAL64_BASE;
fallthrough;
case IIO_VAL_INT_64:
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 3b8a2d82f..1f2f03d5d 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -41,6 +41,7 @@ enum iio_event_info {
#define IIO_VAL_DECIMAL64_MICRO (IIO_VAL_DECIMAL64_BASE + 6)
#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)
static inline s64 iio_val_s64_compose(s32 val0, s32 val1)
{
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver
2026-06-11 13:26 [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor Wadim Mueller
` (2 preceding siblings ...)
2026-06-11 13:26 ` [PATCH v4 3/4] iio: core: add IIO_VAL_DECIMAL64_FEMTO format type Wadim Mueller
@ 2026-06-11 13:27 ` Wadim Mueller
2026-06-11 14:01 ` Krzysztof Kozlowski
2026-06-11 19:18 ` Andy Shevchenko
3 siblings, 2 replies; 8+ messages in thread
From: Wadim Mueller @ 2026-06-11 13:27 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar, Wadim Mueller
Add a driver for the Sensirion SLF3S family of digital
liquid-flow sensors on I2C. Currently supported variants are
SLF3S-0600F, SLF3S-1300F and SLF3S-4000B; they share the same
register map and differ only in flow-scale factor and calibrated
measurement range. The variant (and therefore the scale) is
auto-detected from the product-information register at probe time;
a sensor reporting an unknown sub-type falls back to the variant
named in the device tree, as promised by the fallback compatible.
Each measurement frame returns a 16-bit signed flow value, a
16-bit signed temperature reading and a status word, each
protected by a CRC-8 byte. The driver exposes the flow rate as
IIO_VOLUMEFLOW and the temperature as IIO_TEMP via the standard
IIO read_raw / read_scale interface.
The volume-flow scale is reported in m^3/s. As the per-LSB scale
is on the order of 1e-12 m^3/s, it is emitted as a 64-bit
fixed-point value with femto (1e-15) resolution
(IIO_VAL_DECIMAL64_FEMTO) so the small SI value keeps full
precision. This relies on the IIO_VAL_DECIMAL64_FEMTO format type
added earlier in this series, which extends the IIO_VAL_DECIMAL64
core formatting introduced by Rodrigo Alencar's ADF41513 series.
The active calibration medium can be switched at runtime between
the factory-calibrated water and isopropyl-alcohol modes via the
in_volumeflow_medium sysfs attribute; the sensor starts in water
mode after probe.
The sensor has no low-power state of its own, so system suspend
stops the measurement and disables the vdd supply; resume powers
the sensor back up, waits out the power-up time and restarts the
measurement with the previously active medium, following the
scd30/scd4x precedent.
This driver also creates the drivers/iio/flow/ subdirectory and
the corresponding Kconfig/Makefile glue.
Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
Documentation/ABI/testing/sysfs-bus-iio-flow | 21 +
MAINTAINERS | 1 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/flow/Kconfig | 22 +
drivers/iio/flow/Makefile | 7 +
drivers/iio/flow/slf3s.c | 521 +++++++++++++++++++
7 files changed, 574 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-flow
create mode 100644 drivers/iio/flow/Kconfig
create mode 100644 drivers/iio/flow/Makefile
create mode 100644 drivers/iio/flow/slf3s.c
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-flow b/Documentation/ABI/testing/sysfs-bus-iio-flow
new file mode 100644
index 000000000..fece2ecfa
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-flow
@@ -0,0 +1,21 @@
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium
+KernelVersion: 7.3
+Contact: linux-iio@vger.kernel.org
+Description:
+ The calibration medium the flow sensor uses to convert its
+ raw reading into a volumetric flow rate. Liquid flow sensors
+ are factory-calibrated per medium, so the selected medium has
+ to match the fluid actually flowing through the sensor for the
+ reported flow rate to be correct.
+
+ Reading returns the currently active medium; writing one of the
+ strings listed in in_volumeflow_medium_available selects it.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium_available
+What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium_available
+KernelVersion: 7.3
+Contact: linux-iio@vger.kernel.org
+Description:
+ Space separated list of the calibration media supported by the
+ device, e.g. "water ipa".
diff --git a/MAINTAINERS b/MAINTAINERS
index cdc18a601..222a03b6d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24193,6 +24193,7 @@ R: Maxwell Doose <m32285159@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
+F: drivers/iio/flow/slf3s.c
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 661127aed..652557a5b 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -92,6 +92,7 @@ source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/filter/Kconfig"
+source "drivers/iio/flow/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
source "drivers/iio/health/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index cb80ef837..f03a4100c 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -29,6 +29,7 @@ obj-y += dac/
obj-y += dummy/
obj-y += gyro/
obj-y += filter/
+obj-y += flow/
obj-y += frequency/
obj-y += health/
obj-y += humidity/
diff --git a/drivers/iio/flow/Kconfig b/drivers/iio/flow/Kconfig
new file mode 100644
index 000000000..e0e1a8e36
--- /dev/null
+++ b/drivers/iio/flow/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Liquid / gas flow sensor drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Flow sensors"
+
+config SENSIRION_SLF3S
+ tristate "Sensirion SLF3S liquid flow sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say yes here to build support for the Sensirion SLF3S family of
+ digital liquid-flow sensors (SLF3S-0600F, SLF3S-1300F and
+ SLF3S-4000B). The driver reports the volumetric flow rate and the
+ embedded temperature reading via the standard IIO interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called slf3s.
+
+endmenu
diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile
new file mode 100644
index 000000000..3cf4ab95c
--- /dev/null
+++ b/drivers/iio/flow/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for industrial I/O flow sensor drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_SENSIRION_SLF3S) += slf3s.o
diff --git a/drivers/iio/flow/slf3s.c b/drivers/iio/flow/slf3s.c
new file mode 100644
index 000000000..ed7b89e8e
--- /dev/null
+++ b/drivers/iio/flow/slf3s.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SLF3S liquid flow sensor driver.
+ *
+ * Supports the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B liquid-flow
+ * sensors over I2C. Each measurement frame returns a 16-bit signed
+ * flow value, a 16-bit signed temperature value and a status word,
+ * each protected by a CRC-8 byte.
+ *
+ * The active calibration medium (water or isopropyl alcohol) is
+ * runtime-switchable via the in_volumeflow_medium sysfs attribute and
+ * defaults to water.
+ *
+ * Datasheet: https://sensirion.com/products/catalog/SLF3S-0600F/
+ *
+ * Copyright (C) 2026 CMBlu Energy GmbH
+ * Author: Wadim Mueller <wafgo01@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#define SLF3S_CRC8_POLY 0x31
+#define SLF3S_CRC8_INIT 0xff
+
+#define SLF3S_PRODUCT_ID_LEN 18
+#define SLF3S_PRODUCT_FAMILY_BYTE 1
+#define SLF3S_PRODUCT_SUBTYPE_BYTE 3
+#define SLF3S_PRODUCT_FAMILY_ID 0x03
+
+/* Datasheet section 2.2: tPU = 25 ms max from power-on to first cmd. */
+#define SLF3S_POWER_UP_DELAY_US (25 * USEC_PER_MSEC)
+/* Datasheet section 2.2: tw = 60 ms typical until first valid sample. */
+#define SLF3S_MEAS_START_DELAY_US (60 * USEC_PER_MSEC)
+
+static const u8 slf3s_cmd_prep_pid[] = { 0x36, 0x7c };
+static const u8 slf3s_cmd_read_pid[] = { 0xe1, 0x02 };
+static const u8 slf3s_cmd_start_water[] = { 0x36, 0x08 };
+static const u8 slf3s_cmd_start_ipa[] = { 0x36, 0x15 };
+static const u8 slf3s_cmd_stop_meas[] = { 0x3f, 0xf9 };
+
+enum slf3s_medium {
+ SLF3S_MEDIUM_WATER,
+ SLF3S_MEDIUM_IPA,
+};
+
+static const char * const slf3s_medium_modes[] = {
+ [SLF3S_MEDIUM_WATER] = "water",
+ [SLF3S_MEDIUM_IPA] = "ipa",
+};
+
+/**
+ * struct slf3s_variant - per-variant calibration constants
+ * @sub_type: product-info sub-type byte returned by the sensor
+ * @name: name reported via @iio_dev.name
+ * @scale_num: flow scale numerator (l/s per LSB)
+ * @scale_den: flow scale denominator (l/s per LSB)
+ */
+struct slf3s_variant {
+ u8 sub_type;
+ const char *name;
+ int scale_num;
+ int scale_den;
+};
+
+static const struct slf3s_variant slf3s_variants[] = {
+ [0] = {
+ .sub_type = 0x03,
+ .name = "slf3s-0600f",
+ .scale_num = 1,
+ .scale_den = 600 * MICRO,
+ },
+ [1] = {
+ .sub_type = 0x02,
+ .name = "slf3s-1300f",
+ .scale_num = 1,
+ .scale_den = 30 * MICRO,
+ },
+ [2] = {
+ .sub_type = 0x05,
+ .name = "slf3s-4000b",
+ .scale_num = 1,
+ .scale_den = 1920 * MILLI,
+ },
+};
+
+/**
+ * struct slf3s_data - per-device state
+ * @client: I2C client this instance is bound to
+ * @vdd: supply regulator, disabled while suspended
+ * @variant: pointer into @slf3s_variants for the detected device
+ * @medium: currently active calibration medium
+ * @lock: serialises the multi-step command/response exchanges
+ * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY
+ */
+struct slf3s_data {
+ struct i2c_client *client;
+ struct regulator *vdd;
+ const struct slf3s_variant *variant;
+ enum slf3s_medium medium;
+ struct mutex lock; /* serialises command/response exchanges */
+ u8 crc_table[CRC8_TABLE_SIZE];
+};
+
+static int slf3s_send_cmd(struct i2c_client *client, const u8 cmd[at_least 2])
+{
+ int ret = i2c_master_send(client, cmd, 2);
+
+ if (ret == 2)
+ return 0;
+
+ return ret < 0 ? ret : -EIO;
+}
+
+/* Start continuous measurement and wait until the first sample is valid. */
+static int slf3s_start_meas(struct slf3s_data *sf, enum slf3s_medium medium)
+{
+ const u8 *cmd = (medium == SLF3S_MEDIUM_IPA) ? slf3s_cmd_start_ipa
+ : slf3s_cmd_start_water;
+ int ret;
+
+ ret = slf3s_send_cmd(sf->client, cmd);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_MEAS_START_DELAY_US);
+
+ return 0;
+}
+
+static bool slf3s_crc_valid(const struct slf3s_data *sf, const u8 *block)
+{
+ return crc8(sf->crc_table, block, 2, SLF3S_CRC8_INIT) == block[2];
+}
+
+/*
+ * Read the product-info block and pick the matching variant. The
+ * sub-type byte returned by the sensor is the source of truth; a
+ * DT-supplied compatible only seeds an initial guess and is overridden
+ * on mismatch (with an informational message so misconfigured device
+ * trees are easy to spot).
+ *
+ * Bus / CRC failures are real errors and fail probe. An unknown
+ * sub-type byte falls back to the variant named in the device tree /
+ * I2C table: the fallback compatible promises that future family
+ * members work as an SLF3S-1300F, so do not reject them. Without any
+ * match data probe fails since no meaningful scale can be published.
+ */
+static int slf3s_detect_variant(struct slf3s_data *sf)
+{
+ struct i2c_client *client = sf->client;
+ u8 buf[SLF3S_PRODUCT_ID_LEN];
+ int ret;
+
+ ret = slf3s_send_cmd(client, slf3s_cmd_prep_pid);
+ if (ret)
+ return ret;
+
+ ret = slf3s_send_cmd(client, slf3s_cmd_read_pid);
+ if (ret)
+ return ret;
+
+ ret = i2c_master_recv(client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ for (unsigned int i = 0; i < SLF3S_PRODUCT_ID_LEN; i += 3) {
+ if (!slf3s_crc_valid(sf, &buf[i]))
+ return -EIO;
+ }
+
+ if (buf[SLF3S_PRODUCT_FAMILY_BYTE] != SLF3S_PRODUCT_FAMILY_ID)
+ dev_info(&client->dev,
+ "unexpected family byte 0x%02x (expected 0x%02x)\n",
+ buf[SLF3S_PRODUCT_FAMILY_BYTE],
+ SLF3S_PRODUCT_FAMILY_ID);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(slf3s_variants); i++) {
+ if (buf[SLF3S_PRODUCT_SUBTYPE_BYTE] !=
+ slf3s_variants[i].sub_type)
+ continue;
+
+ if (sf->variant && sf->variant != &slf3s_variants[i])
+ dev_info(&client->dev,
+ "DT compatible says %s but sensor reports %s; using %s\n",
+ sf->variant->name,
+ slf3s_variants[i].name,
+ slf3s_variants[i].name);
+
+ sf->variant = &slf3s_variants[i];
+
+ return 0;
+ }
+
+ if (sf->variant) {
+ dev_warn(&client->dev,
+ "unknown SLF3S sub-type 0x%02x, assuming %s\n",
+ buf[SLF3S_PRODUCT_SUBTYPE_BYTE], sf->variant->name);
+ return 0;
+ }
+
+ dev_err(&client->dev, "unknown SLF3S sub-type 0x%02x\n",
+ buf[SLF3S_PRODUCT_SUBTYPE_BYTE]);
+
+ return -ENODEV;
+}
+
+static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp)
+{
+ /*
+ * A measurement frame is flow, temperature and a signaling-flags
+ * word, each followed by a CRC byte. Only flow and temperature are
+ * used, so the read is stopped after their two words (6 bytes).
+ */
+ u8 buf[6];
+ int ret;
+
+ ret = i2c_master_recv(sf->client, buf, ARRAY_SIZE(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(buf))
+ return -EIO;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(buf); i += 3) {
+ if (!slf3s_crc_valid(sf, &buf[i]))
+ return -EIO;
+ }
+
+ *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15);
+ *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15);
+
+ return 0;
+}
+
+static int slf3s_get_medium(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+
+ return sf->medium;
+}
+
+static int slf3s_set_medium(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+ if (ret)
+ return ret;
+
+ ret = slf3s_start_meas(sf, mode);
+ if (ret) {
+ /*
+ * Try to restart with the previous medium so the sensor is
+ * not left idle, which would fail all subsequent reads.
+ */
+ if (slf3s_start_meas(sf, sf->medium))
+ dev_warn(&sf->client->dev,
+ "failed to restart measurement, reads will fail until a medium is set\n");
+ return ret;
+ }
+
+ sf->medium = mode;
+
+ return 0;
+}
+
+static const struct iio_enum slf3s_medium_enum = {
+ .items = slf3s_medium_modes,
+ .num_items = ARRAY_SIZE(slf3s_medium_modes),
+ .get = slf3s_get_medium,
+ .set = slf3s_set_medium,
+};
+
+static const struct iio_chan_spec_ext_info slf3s_ext_info[] = {
+ IIO_ENUM("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum),
+ IIO_ENUM_AVAILABLE("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum),
+ { }
+};
+
+static const struct iio_chan_spec slf3s_channels[] = {
+ {
+ .type = IIO_VOLUMEFLOW,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = slf3s_ext_info,
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int slf3s_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int flow, temp, ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ scoped_guard(mutex, &sf->lock)
+ ret = slf3s_read_sample(sf, &flow, &temp);
+ if (ret)
+ return ret;
+
+ *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLUMEFLOW) {
+ /*
+ * scale_num/scale_den is the flow per LSB in l/s, but
+ * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3).
+ * These values are tiny (~1.67e-12 m^3/s for the
+ * SLF3S-0600F), so emit a 64-bit fixed-point value with
+ * femto (1e-15) resolution to preserve precision.
+ * Converting l/s to m^3/s (/ MILLI) and scaling to femto
+ * (* FEMTO) leaves a net * (FEMTO / MILLI) factor.
+ */
+ const struct slf3s_variant *v = sf->variant;
+ s64 num = (s64)v->scale_num * FEMTO / MILLI;
+ s64 scale = DIV_S64_ROUND_CLOSEST(num, v->scale_den);
+
+ iio_val_s64_decompose(scale, val, val2);
+
+ return IIO_VAL_DECIMAL64_FEMTO;
+ }
+ /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */
+ *val = 1000 / 200;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info slf3s_info = {
+ .read_raw = slf3s_read_raw,
+};
+
+static void slf3s_stop_meas(void *data)
+{
+ struct slf3s_data *sf = data;
+
+ slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+}
+
+static void slf3s_disable_vdd(void *data)
+{
+ struct slf3s_data *sf = data;
+
+ regulator_disable(sf->vdd);
+}
+
+static int slf3s_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct slf3s_data *sf;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sf));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sf = iio_priv(indio_dev);
+ sf->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ sf->variant = i2c_get_match_data(client);
+ sf->medium = SLF3S_MEDIUM_WATER;
+ crc8_populate_msb(sf->crc_table, SLF3S_CRC8_POLY);
+
+ ret = devm_mutex_init(dev, &sf->lock);
+ if (ret)
+ return ret;
+
+ sf->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(sf->vdd))
+ return dev_err_probe(dev, PTR_ERR(sf->vdd),
+ "failed to get vdd supply\n");
+
+ ret = regulator_enable(sf->vdd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vdd supply\n");
+
+ ret = devm_add_action_or_reset(dev, slf3s_disable_vdd, sf);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_POWER_UP_DELAY_US);
+
+ /*
+ * The sensor may still be in continuous measurement mode from a
+ * previous boot (warm reboot / kexec); in that case it would NACK
+ * the product-id command below. Stop it first and ignore the error
+ * if it was already idle.
+ */
+ slf3s_send_cmd(client, slf3s_cmd_stop_meas);
+
+ ret = slf3s_detect_variant(sf);
+ if (ret)
+ return dev_err_probe(dev, ret, "product info read failed\n");
+
+ ret = slf3s_start_meas(sf, sf->medium);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to start measurement\n");
+
+ ret = devm_add_action_or_reset(dev, slf3s_stop_meas, sf);
+ if (ret)
+ return ret;
+
+ indio_dev->name = sf->variant->name;
+ indio_dev->channels = slf3s_channels;
+ indio_dev->num_channels = ARRAY_SIZE(slf3s_channels);
+ indio_dev->info = &slf3s_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+/*
+ * The sensor has no low-power state of its own, so stop the measurement
+ * and cut the supply while suspended. Resume powers it back up, waits
+ * out the power-up time and restarts with the medium that was active
+ * before.
+ */
+static int slf3s_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+ if (ret)
+ return ret;
+
+ return regulator_disable(sf->vdd);
+}
+
+static int slf3s_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = regulator_enable(sf->vdd);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_POWER_UP_DELAY_US);
+
+ return slf3s_start_meas(sf, sf->medium);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(slf3s_pm_ops, slf3s_suspend, slf3s_resume);
+
+static const struct i2c_device_id slf3s_id[] = {
+ { .name = "slf3s-0600f",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[0] },
+ { .name = "slf3s-1300f",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[1] },
+ { .name = "slf3s-4000b",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[2] },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, slf3s_id);
+
+static const struct of_device_id slf3s_of_match[] = {
+ { .compatible = "sensirion,slf3s-0600f", .data = &slf3s_variants[0] },
+ { .compatible = "sensirion,slf3s-1300f", .data = &slf3s_variants[1] },
+ { .compatible = "sensirion,slf3s-4000b", .data = &slf3s_variants[2] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, slf3s_of_match);
+
+static struct i2c_driver slf3s_driver = {
+ .driver = {
+ .name = "slf3s",
+ .of_match_table = slf3s_of_match,
+ .pm = pm_sleep_ptr(&slf3s_pm_ops),
+ },
+ .probe = slf3s_probe,
+ .id_table = slf3s_id,
+};
+module_i2c_driver(slf3s_driver);
+
+MODULE_AUTHOR("Wadim Mueller <wafgo01@gmail.com>");
+MODULE_DESCRIPTION("Sensirion SLF3S liquid flow sensor driver");
+MODULE_LICENSE("GPL");
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver
2026-06-11 13:27 ` [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver Wadim Mueller
@ 2026-06-11 14:01 ` Krzysztof Kozlowski
2026-06-11 19:18 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-11 14:01 UTC (permalink / raw)
To: Wadim Mueller, Jonathan Cameron, Krzysztof Kozlowski, Rob Herring,
Conor Dooley, David Lechner, Nuno Sá, Andy Shevchenko,
Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar
On 11/06/2026 15:27, Wadim Mueller wrote:
> +
> +static const struct of_device_id slf3s_of_match[] = {
> + { .compatible = "sensirion,slf3s-0600f", .data = &slf3s_variants[0] },
> + { .compatible = "sensirion,slf3s-1300f", .data = &slf3s_variants[1] },
> + { .compatible = "sensirion,slf3s-4000b", .data = &slf3s_variants[2] },
You should have only 1300f here and detect the variants. That was my
point when I suggested to use the fallback.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor
2026-06-11 13:26 ` [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor Wadim Mueller
@ 2026-06-11 14:01 ` Krzysztof Kozlowski
0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-11 14:01 UTC (permalink / raw)
To: Wadim Mueller, Jonathan Cameron, Krzysztof Kozlowski, Rob Herring,
Conor Dooley, David Lechner, Nuno Sá, Andy Shevchenko,
Maxwell Doose
Cc: linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar
On 11/06/2026 15:26, Wadim Mueller wrote:
> Document the bindings for the Sensirion SLF3S family of digital
> liquid-flow sensors on I2C. The family currently covers the
> SLF3S-0600F, SLF3S-1300F and SLF3S-4000B variants.
>
> All variants share the same register map and are fully detectable
> from the product-information register at probe time, so
> sensirion,slf3s-1300f serves as the fallback compatible for the
> other variants.
>
> The active calibration medium (water / IPA) is runtime-switchable
> via the in_volumeflow_medium sysfs attribute and therefore not a
> DT property.
>
> Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
> ---
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver
2026-06-11 13:27 ` [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver Wadim Mueller
2026-06-11 14:01 ` Krzysztof Kozlowski
@ 2026-06-11 19:18 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: Andy Shevchenko @ 2026-06-11 19:18 UTC (permalink / raw)
To: Wadim Mueller
Cc: Jonathan Cameron, Krzysztof Kozlowski, Rob Herring, Conor Dooley,
David Lechner, Nuno Sá, Andy Shevchenko, Maxwell Doose,
linux-iio, devicetree, linux-kernel, Marcelo Schmitt,
Rodrigo Alencar
On Thu, Jun 11, 2026 at 03:27:00PM +0200, Wadim Mueller wrote:
> Add a driver for the Sensirion SLF3S family of digital
> liquid-flow sensors on I2C. Currently supported variants are
> SLF3S-0600F, SLF3S-1300F and SLF3S-4000B; they share the same
> register map and differ only in flow-scale factor and calibrated
> measurement range. The variant (and therefore the scale) is
> auto-detected from the product-information register at probe time;
> a sensor reporting an unknown sub-type falls back to the variant
> named in the device tree, as promised by the fallback compatible.
>
> Each measurement frame returns a 16-bit signed flow value, a
> 16-bit signed temperature reading and a status word, each
> protected by a CRC-8 byte. The driver exposes the flow rate as
> IIO_VOLUMEFLOW and the temperature as IIO_TEMP via the standard
> IIO read_raw / read_scale interface.
>
> The volume-flow scale is reported in m^3/s. As the per-LSB scale
> is on the order of 1e-12 m^3/s, it is emitted as a 64-bit
> fixed-point value with femto (1e-15) resolution
> (IIO_VAL_DECIMAL64_FEMTO) so the small SI value keeps full
> precision. This relies on the IIO_VAL_DECIMAL64_FEMTO format type
> added earlier in this series, which extends the IIO_VAL_DECIMAL64
> core formatting introduced by Rodrigo Alencar's ADF41513 series.
>
> The active calibration medium can be switched at runtime between
> the factory-calibrated water and isopropyl-alcohol modes via the
> in_volumeflow_medium sysfs attribute; the sensor starts in water
> mode after probe.
>
> The sensor has no low-power state of its own, so system suspend
> stops the measurement and disables the vdd supply; resume powers
> the sensor back up, waits out the power-up time and restarts the
> measurement with the previously active medium, following the
> scd30/scd4x precedent.
>
> This driver also creates the drivers/iio/flow/ subdirectory and
> the corresponding Kconfig/Makefile glue.
...
> +#include <linux/array_size.h>
> +#include <linux/bitops.h>
> +#include <linux/cleanup.h>
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/dev_printk.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
Should be err.h // PTR_ERR(), et cetera
> +#include <linux/i2c.h>
> +#include <linux/math64.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +#include <linux/unaligned.h>
> +#include <linux/units.h>
...
> +/**
> + * struct slf3s_variant - per-variant calibration constants
> + * @sub_type: product-info sub-type byte returned by the sensor
> + * @name: name reported via @iio_dev.name
> + * @scale_num: flow scale numerator (l/s per LSB)
> + * @scale_den: flow scale denominator (l/s per LSB)
> + */
> +struct slf3s_variant {
> + u8 sub_type;
> + const char *name;
> + int scale_num;
> + int scale_den;
struct s32_fract scale;
> +};
...
> +static const struct slf3s_variant slf3s_variants[] = {
> + [0] = {
> + .sub_type = 0x03,
> + .name = "slf3s-0600f",
> + .scale_num = 1,
> + .scale_den = 600 * MICRO,
> + },
> + [1] = {
> + .sub_type = 0x02,
> + .name = "slf3s-1300f",
> + .scale_num = 1,
> + .scale_den = 30 * MICRO,
> + },
> + [2] = {
> + .sub_type = 0x05,
> + .name = "slf3s-4000b",
> + .scale_num = 1,
> + .scale_den = 1920 * MILLI,
> + },
Either split this to per HW structures, or introduce a enum for having these be
robust against any indices shuffling. The plain numbers are semantic-less, easy
to mess up with them.
> +};
...
> +/**
> + * struct slf3s_data - per-device state
> + * @client: I2C client this instance is bound to
> + * @vdd: supply regulator, disabled while suspended
> + * @variant: pointer into @slf3s_variants for the detected device
> + * @medium: currently active calibration medium
> + * @lock: serialises the multi-step command/response exchanges
> + * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY
> + */
> +struct slf3s_data {
> + struct i2c_client *client;
> + struct regulator *vdd;
> + const struct slf3s_variant *variant;
> + enum slf3s_medium medium;
> + struct mutex lock; /* serialises command/response exchanges */
> + u8 crc_table[CRC8_TABLE_SIZE];
Does `pahole` agree with the layout?
> +};
> +
> +static int slf3s_send_cmd(struct i2c_client *client, const u8 cmd[at_least 2])
Hmm... Do we really need to be overprotective here?
> +{
> + int ret = i2c_master_send(client, cmd, 2);
> +
> + if (ret == 2)
In long-term this is hard to maintain. The preferred way is to decouple the
assignment and the definition as the value is getting validated in the code.
> + return 0;
> +
> + return ret < 0 ? ret : -EIO;
The usual pattern is to check for errors first
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;
return 0;
> +}
...
> +static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp)
> +{
> + /*
> + * A measurement frame is flow, temperature and a signaling-flags
> + * word, each followed by a CRC byte. Only flow and temperature are
> + * used, so the read is stopped after their two words (6 bytes).
> + */
> + u8 buf[6];
> + int ret;
> +
> + ret = i2c_master_recv(sf->client, buf, ARRAY_SIZE(buf));
sizeof() will do the job.
> + if (ret < 0)
> + return ret;
> + if (ret != ARRAY_SIZE(buf))
Ditto.
> + return -EIO;
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(buf); i += 3) {
Ditto.
> + if (!slf3s_crc_valid(sf, &buf[i]))
> + return -EIO;
> + }
> +
> + *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15);
> + *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15);
> +
> + return 0;
> +}
...
> +static int slf3s_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + struct slf3s_data *sf = iio_priv(indio_dev);
> + int flow, temp, ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + scoped_guard(mutex, &sf->lock)
> + ret = slf3s_read_sample(sf, &flow, &temp);
> + if (ret)
> + return ret;
> +
> + *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + if (chan->type == IIO_VOLUMEFLOW) {
> + /*
> + * scale_num/scale_den is the flow per LSB in l/s, but
> + * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3).
> + * These values are tiny (~1.67e-12 m^3/s for the
> + * SLF3S-0600F), so emit a 64-bit fixed-point value with
> + * femto (1e-15) resolution to preserve precision.
> + * Converting l/s to m^3/s (/ MILLI) and scaling to femto
> + * (* FEMTO) leaves a net * (FEMTO / MILLI) factor.
> + */
> + const struct slf3s_variant *v = sf->variant;
> + s64 num = (s64)v->scale_num * FEMTO / MILLI;
Since the FEMTO and MILLI are of the same base, there might be better to use
them in parentheses. Can you check if that affects code generation? (I hope
compiler is smart enough to prove the above and basically simply multiply the
scale_num.)
> + s64 scale = DIV_S64_ROUND_CLOSEST(num, v->scale_den);
> +
> + iio_val_s64_decompose(scale, val, val2);
> +
> + return IIO_VAL_DECIMAL64_FEMTO;
> + }
> + /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */
> + *val = 1000 / 200;
MILLIDEGREE_PER_DEGREE ?
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-11 19:19 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-11 13:26 [PATCH v4 0/4] iio: flow: Sensirion SLF3S liquid flow sensor Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 1/4] iio: types: add IIO_VOLUMEFLOW channel type Wadim Mueller
2026-06-11 13:26 ` [PATCH v4 2/4] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor Wadim Mueller
2026-06-11 14:01 ` Krzysztof Kozlowski
2026-06-11 13:26 ` [PATCH v4 3/4] iio: core: add IIO_VAL_DECIMAL64_FEMTO format type Wadim Mueller
2026-06-11 13:27 ` [PATCH v4 4/4] iio: flow: add Sensirion SLF3S liquid flow sensor driver Wadim Mueller
2026-06-11 14:01 ` Krzysztof Kozlowski
2026-06-11 19:18 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox