Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 4/4] iio: light: veml6031x00: add support for events and trigger
From: Javier Carrasco @ 2026-05-24 21:53 UTC (permalink / raw)
  To: Jonathan Cameron, Lars-Peter Clausen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Rishi Gupta, David Lechner,
	Nuno Sá, Andy Shevchenko, Matti Vaittinen
  Cc: linux-iio, devicetree, linux-kernel, Javier Carrasco,
	Jonathan Cameron
In-Reply-To: <20260524-veml6031x00-v3-0-29165609b2b5@gmail.com>

The device provides a shared interrupt line for to notify events and
data ready, which can be used as a trigger. The interrupt line is not a
requirement for the device to work. Implement variants for the cases
whether the interrupt line is provided or not.

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
 drivers/iio/light/veml6031x00.c | 442 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 438 insertions(+), 4 deletions(-)

diff --git a/drivers/iio/light/veml6031x00.c b/drivers/iio/light/veml6031x00.c
index 9968d4414dc9..c57aaa29abf3 100644
--- a/drivers/iio/light/veml6031x00.c
+++ b/drivers/iio/light/veml6031x00.c
@@ -7,12 +7,16 @@
 
 #include <linux/bitfield.h>
 #include <linux/i2c.h>
+#include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/units.h>
 #include <linux/iio/iio.h>
+#include <linux/iio/events.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
 #include <linux/iio/iio-gts-helper.h>
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/trigger_consumer.h>
@@ -20,16 +24,29 @@
 /* Device registers */
 #define VEML6031X00_REG_CONF0       0x00
 #define VEML6031X00_REG_CONF1       0x01
+#define VEML6031X00_REG_WH_L        0x04
+#define VEML6031X00_REG_WH_H        0x05
+#define VEML6031X00_REG_WL_L        0x06
+#define VEML6031X00_REG_WL_H        0x07
 #define VEML6031X00_REG_ALS_L       0x10
 #define VEML6031X00_REG_ALS_H       0x11
 #define VEML6031X00_REG_IR_L        0x12
 #define VEML6031X00_REG_IR_H        0x13
 #define VEML6031X00_REG_ID_L        0x14
 #define VEML6031X00_REG_ID_H        0x15
+#define VEML6031X00_REG_INT         0x17
 
 /* Bit masks for specific functionality */
 #define VEML6031X00_CONF0_SD        BIT(0)
+#define VEML6031X00_CONF0_AF_TRIG   BIT(2)
+#define VEML6031X00_CONF0_AF        BIT(3)
 #define VEML6031X00_CONF1_IR_SD     BIT(7)
+#define VEML6031X00_INT_TH_H        BIT(1)
+#define VEML6031X00_INT_TH_L        BIT(2)
+#define VEML6031X00_INT_DRDY        BIT(3)
+#define VEML6031X00_INT_MASK        (VEML6031X00_INT_TH_L | \
+				     VEML6031X00_INT_TH_H | \
+				     VEML6031X00_INT_DRDY)
 
 enum veml6031x00_scan {
 	VEML6031X00_SCAN_ALS,
@@ -39,8 +56,10 @@ enum veml6031x00_scan {
 
 struct veml6031x00_rf {
 	struct regmap_field *gain;
+	struct regmap_field *int_en;
 	struct regmap_field *it;
 	struct regmap_field *pd_div4;
+	struct regmap_field *pers;
 };
 
 struct veml6031x00_chip {
@@ -52,8 +71,14 @@ struct veml6031x00_data {
 	struct device *dev;
 	struct iio_gts gts;
 	struct regmap *regmap;
+	struct iio_trigger *trig;
 	struct veml6031x00_rf rf;
 	const struct veml6031x00_chip *chip;
+	/* serialize access to irq enable/disable by events and trigger */
+	struct mutex lock;
+	int int_users;
+	bool ev_en;
+	bool trig_en;
 };
 
 static const struct iio_itime_sel_mul veml6031x00_it_sel[] = {
@@ -88,6 +113,17 @@ static const struct iio_gain_sel_pair veml6031x00_gain_sel[] = {
 	GAIN_SCALE_GAIN(16, VEML6031X00_SEL_MILLI_GAIN_X2000),
 };
 
+static IIO_CONST_ATTR(in_illuminance_thresh_either_period_available, "1 2 4 8");
+
+static struct attribute *veml6031x00_event_attributes[] = {
+	&iio_const_attr_in_illuminance_thresh_either_period_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group veml6031x00_event_attr_group = {
+	.attrs = veml6031x00_event_attributes,
+};
+
 /*
  * Two shutdown bits (SD and ALS_IR_SD) must be cleared to power on
  * the device.
@@ -127,6 +163,23 @@ static void veml6031x00_als_shutdown_action(void *data)
 	veml6031x00_als_shutdown(data);
 }
 
+static const struct iio_event_spec veml6031x00_event_spec[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_PERIOD) |
+				 BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
 static const struct iio_chan_spec veml6031x00_channels[] = {
 	{
 		.type = IIO_LIGHT,
@@ -136,6 +189,8 @@ static const struct iio_chan_spec veml6031x00_channels[] = {
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+		.event_spec = veml6031x00_event_spec,
+		.num_event_specs = ARRAY_SIZE(veml6031x00_event_spec),
 		.scan_index = VEML6031X00_SCAN_ALS,
 		.scan_type = {
 			.sign = 'u',
@@ -166,7 +221,9 @@ static const struct iio_chan_spec veml6031x00_channels[] = {
 
 static const struct regmap_range veml6031x00_readable_ranges[] = {
 	regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1),
+	regmap_reg_range(VEML6031X00_REG_WH_L, VEML6031X00_REG_WL_H),
 	regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_ID_H),
+	regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT),
 };
 
 static const struct regmap_access_table veml6031x00_readable_table = {
@@ -175,7 +232,7 @@ static const struct regmap_access_table veml6031x00_readable_table = {
 };
 
 static const struct regmap_range veml6031x00_writable_ranges[] = {
-	regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1),
+	regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_WL_H),
 };
 
 static const struct regmap_access_table veml6031x00_writable_table = {
@@ -185,6 +242,7 @@ static const struct regmap_access_table veml6031x00_writable_table = {
 
 static const struct regmap_range veml6031x00_volatile_ranges[] = {
 	regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_IR_H),
+	regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT),
 };
 
 static const struct regmap_access_table veml6031x00_volatile_table = {
@@ -192,6 +250,15 @@ static const struct regmap_access_table veml6031x00_volatile_table = {
 	.n_yes_ranges = ARRAY_SIZE(veml6031x00_volatile_ranges),
 };
 
+static const struct regmap_range veml6031x00_precious_ranges[] = {
+	regmap_reg_range(VEML6031X00_REG_INT, VEML6031X00_REG_INT),
+};
+
+static const struct regmap_access_table veml6031x00_precious_table = {
+	.yes_ranges = veml6031x00_precious_ranges,
+	.n_yes_ranges = ARRAY_SIZE(veml6031x00_precious_ranges),
+};
+
 static const struct regmap_config veml6031x00_regmap_config = {
 	.name = "veml6031x00_regmap",
 	.reg_bits = 8,
@@ -199,13 +266,20 @@ static const struct regmap_config veml6031x00_regmap_config = {
 	.rd_table = &veml6031x00_readable_table,
 	.wr_table = &veml6031x00_writable_table,
 	.volatile_table = &veml6031x00_volatile_table,
-	.max_register = VEML6031X00_REG_ID_H,
+	.precious_table = &veml6031x00_precious_table,
+	.max_register = VEML6031X00_REG_INT,
 	.cache_type = REGCACHE_MAPLE,
 };
 
+static const struct reg_field veml6031x00_rf_int_en =
+	REG_FIELD(VEML6031X00_REG_CONF0, 1, 1);
+
 static const struct reg_field veml6031x00_rf_it =
 	REG_FIELD(VEML6031X00_REG_CONF0, 4, 6);
 
+static const struct reg_field veml6031x00_rf_pers =
+	REG_FIELD(VEML6031X00_REG_CONF1, 1, 2);
+
 static const struct reg_field veml6031x00_rf_gain =
 	REG_FIELD(VEML6031X00_REG_CONF1, 3, 4);
 
@@ -224,6 +298,11 @@ static int veml6031x00_regfield_init(struct veml6031x00_data *data)
 		return PTR_ERR(rm_field);
 	rf->gain = rm_field;
 
+	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_int_en);
+	if (IS_ERR(rm_field))
+		return PTR_ERR(rm_field);
+	rf->int_en = rm_field;
+
 	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_it);
 	if (IS_ERR(rm_field))
 		return PTR_ERR(rm_field);
@@ -234,6 +313,11 @@ static int veml6031x00_regfield_init(struct veml6031x00_data *data)
 		return PTR_ERR(rm_field);
 	rf->pd_div4 = rm_field;
 
+	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_pers);
+	if (IS_ERR(rm_field))
+		return PTR_ERR(rm_field);
+	rf->pers = rm_field;
+
 	return 0;
 }
 
@@ -313,6 +397,30 @@ static int veml6031x00_set_it(struct iio_dev *iio, int val, int val2)
 	return regmap_field_write(data->rf.gain, gain_sel & 0x03);
 }
 
+static int veml6031x00_read_period(struct iio_dev *iio, int *val)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ret, reg;
+
+	ret = regmap_field_read(data->rf.pers, &reg);
+	if (ret)
+		return ret;
+
+	*val = 1 << reg;
+
+	return IIO_VAL_INT;
+}
+
+static int veml6031x00_write_period(struct iio_dev *iio, int val)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+
+	if (val < 0 || val > 8 || hweight8(val) != 1)
+		return -EINVAL;
+
+	return regmap_field_write(data->rf.pers, ffs(val) - 1);
+}
+
 static int veml6031x00_set_scale(struct iio_dev *iio, int val, int val2)
 {
 	struct veml6031x00_data *data = iio_priv(iio);
@@ -367,6 +475,51 @@ static int veml6031x00_get_scale(struct veml6031x00_data *data, int *val,
 	return IIO_VAL_INT_PLUS_NANO;
 }
 
+static int veml6031x00_read_th(struct iio_dev *iio, int *val, int *val2, int dir)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	__le16 reg;
+	int ret;
+
+	if (dir == IIO_EV_DIR_RISING)
+		ret = regmap_bulk_read(data->regmap, VEML6031X00_REG_WH_L,
+				       &reg, sizeof(reg));
+	else
+		ret = regmap_bulk_read(data->regmap, VEML6031X00_REG_WL_L,
+				       &reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	*val = le16_to_cpu(reg);
+
+	return IIO_VAL_INT;
+}
+
+static int veml6031x00_write_th(struct iio_dev *iio, int val, int val2, int dir)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	struct device *dev = data->dev;
+	__le16 reg = cpu_to_le16(val);
+	int ret;
+
+	if (val < 0 || val > U16_MAX || val2)
+		return -EINVAL;
+
+	if (dir == IIO_EV_DIR_RISING) {
+		ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WH_L,
+					&reg, sizeof(reg));
+		if (ret)
+			dev_dbg(dev, "Failed to set high threshold %d\n", ret);
+	} else {
+		ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WL_L,
+					&reg, sizeof(reg));
+		if (ret)
+			dev_dbg(dev, "Failed to set low threshold %d\n", ret);
+	}
+
+	return ret;
+}
+
 static int veml6031x00_single_read(struct iio_dev *iio, enum iio_chan_type type,
 				   int *val)
 {
@@ -477,13 +630,189 @@ static int veml6031x00_write_raw_get_fmt(struct iio_dev *indio_dev,
 	}
 }
 
+static int veml6031x00_set_interrupt(struct veml6031x00_data *data, bool state)
+	__must_hold(&data->lock)
+{
+	int ret;
+
+	if (state) {
+		data->int_users++;
+		if (data->int_users > 1)
+			return 0;
+	} else {
+		data->int_users--;
+		if (data->int_users > 0)
+			return 0;
+	}
+
+	ret = regmap_field_write(data->rf.int_en, state);
+	if (ret) {
+		if (state)
+			data->int_users--;
+		else
+			data->int_users++;
+	}
+
+	return ret;
+}
+
+static int veml6031x00_read_event_val(struct iio_dev *iio,
+				      const struct iio_chan_spec *chan,
+				      enum iio_event_type type,
+				      enum iio_event_direction dir,
+				      enum iio_event_info info,
+				      int *val, int *val2)
+{
+	switch (type) {
+	case IIO_EV_TYPE_THRESH:
+		if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+			return veml6031x00_read_period(iio, val);
+
+		return veml6031x00_read_th(iio, val, val2, dir);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int veml6031x00_write_event_val(struct iio_dev *iio,
+				       const struct iio_chan_spec *chan,
+				       enum iio_event_type type,
+				       enum iio_event_direction dir,
+				       enum iio_event_info info,
+				       int val, int val2)
+{
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		return veml6031x00_write_th(iio, val, val2, dir);
+	case IIO_EV_INFO_PERIOD:
+		return veml6031x00_write_period(iio, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int veml6031x00_read_event_config(struct iio_dev *iio,
+					 const struct iio_chan_spec *chan,
+					 enum iio_event_type type,
+					 enum iio_event_direction dir)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+
+	guard(mutex)(&data->lock);
+
+	return data->ev_en;
+}
+
+static int veml6031x00_write_event_config(struct iio_dev *iio,
+					  const struct iio_chan_spec *chan,
+					  enum iio_event_type type,
+					  enum iio_event_direction dir,
+					  bool state)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ret;
+
+	guard(mutex)(&data->lock);
+
+	/* avoid multiple increments/decrements from one source */
+	if (state == data->ev_en)
+		return 0;
+
+	if (state) {
+		ret = pm_runtime_resume_and_get(data->dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = veml6031x00_set_interrupt(data, state);
+	if (ret) {
+		if (state)
+			pm_runtime_put_autosuspend(data->dev);
+		return ret;
+	}
+
+	data->ev_en = state;
+
+	if (!state)
+		pm_runtime_put_autosuspend(data->dev);
+
+	return 0;
+}
+
 static const struct iio_info veml6031x00_info = {
 	.read_raw = veml6031x00_read_raw,
 	.read_avail = veml6031x00_read_avail,
 	.write_raw = veml6031x00_write_raw,
 	.write_raw_get_fmt = veml6031x00_write_raw_get_fmt,
+	.read_event_value = veml6031x00_read_event_val,
+	.write_event_value = veml6031x00_write_event_val,
+	.read_event_config = veml6031x00_read_event_config,
+	.write_event_config = veml6031x00_write_event_config,
+	.event_attrs = &veml6031x00_event_attr_group,
+};
+
+static const struct iio_info veml6031x00_info_no_irq = {
+	.read_raw = veml6031x00_read_raw,
+	.read_avail = veml6031x00_read_avail,
+	.write_raw = veml6031x00_write_raw,
+	.write_raw_get_fmt = veml6031x00_write_raw_get_fmt,
 };
 
+/* AF_TRIG is reset by hardware, but the rest of the fields are persistent */
+static int veml6031x00_set_af_trig(struct veml6031x00_data *data, bool state)
+{
+	regcache_drop_region(data->regmap, VEML6031X00_REG_CONF0,
+			     VEML6031X00_REG_CONF0);
+
+	return regmap_update_bits(data->regmap, VEML6031X00_REG_CONF0,
+				  VEML6031X00_CONF0_AF_TRIG,
+				  FIELD_PREP(VEML6031X00_CONF0_AF_TRIG, state));
+}
+
+static irqreturn_t veml6031x00_interrupt(int irq, void *private)
+{
+	struct iio_dev *iio = private;
+	struct veml6031x00_data *data = iio_priv(iio);
+	s64 timestamp;
+	int ret, reg;
+
+	ret = regmap_read(data->regmap, VEML6031X00_REG_INT, &reg);
+	if (ret) {
+		dev_err(data->dev,
+			"Failed to read interrupt register %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	if (!(reg & VEML6031X00_INT_MASK))
+		return IRQ_NONE;
+
+	guard(mutex)(&data->lock);
+
+	if ((reg & (VEML6031X00_INT_TH_H | VEML6031X00_INT_TH_L)) && data->ev_en) {
+		timestamp = iio_get_time_ns(iio);
+
+		if (reg & VEML6031X00_INT_TH_H)
+			iio_push_event(iio, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+								 IIO_EV_TYPE_THRESH,
+								 IIO_EV_DIR_RISING),
+				       timestamp);
+		if (reg & VEML6031X00_INT_TH_L)
+			iio_push_event(iio, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+								 IIO_EV_TYPE_THRESH,
+								 IIO_EV_DIR_FALLING),
+				       timestamp);
+	}
+
+	if ((reg & VEML6031X00_INT_DRDY) && data->trig_en) {
+		iio_trigger_poll_nested(data->trig);
+		ret = veml6031x00_set_af_trig(data, true);
+		if (ret)
+			dev_err(data->dev, "Failed to set trigger %d\n", ret);
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int veml6031x00_buffer_preenable(struct iio_dev *iio)
 {
 	struct veml6031x00_data *data = iio_priv(iio);
@@ -518,11 +847,54 @@ static int veml6031x00_buffer_postdisable(struct iio_dev *iio)
 	return 0;
 }
 
+static int veml6031x00_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *iio = iio_trigger_get_drvdata(trig);
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ret;
+
+	guard(mutex)(&data->lock);
+
+	if (state == data->trig_en)
+		return 0;
+
+	ret = veml6031x00_set_interrupt(data, state);
+	if (ret)
+		return ret;
+
+	/* The AF bit must be set before setting AF_TRIG */
+	ret = regmap_update_bits(data->regmap, VEML6031X00_REG_CONF0,
+				 VEML6031X00_CONF0_AF,
+				 FIELD_PREP(VEML6031X00_CONF0_AF, state));
+	if (ret)
+		goto err_disable_interrupt;
+
+	ret = veml6031x00_set_af_trig(data, state);
+	if (ret)
+		goto err_clear_af;
+
+	data->trig_en = state;
+
+	return 0;
+
+err_clear_af:
+	regmap_update_bits(data->regmap, VEML6031X00_REG_CONF0,
+			   VEML6031X00_CONF0_AF,
+			   FIELD_PREP(VEML6031X00_CONF0_AF, !state));
+err_disable_interrupt:
+	veml6031x00_set_interrupt(data, !state);
+	return ret;
+}
+
 static const struct iio_buffer_setup_ops veml6031x00_buffer_setup_ops = {
 	.preenable = veml6031x00_buffer_preenable,
 	.postdisable = veml6031x00_buffer_postdisable,
 };
 
+static const struct iio_trigger_ops veml6031x00_trigger_ops = {
+	.set_trigger_state = veml6031x00_set_trigger_state,
+};
+
 static irqreturn_t veml6031x00_trig_handler(int irq, void *p)
 {
 	struct iio_poll_func *pf = p;
@@ -581,7 +953,8 @@ static int veml6031x00_hw_init(struct iio_dev *iio)
 {
 	struct veml6031x00_data *data = iio_priv(iio);
 	struct device *dev = data->dev;
-	int ret;
+	int ret, val;
+	__le16 reg;
 
 	/* Max resolution = 6.9632 lx/cnt for gain = 0.125 and IT = 3.125ms */
 	ret = devm_iio_init_iio_gts(dev, 6, 963200000,
@@ -593,6 +966,54 @@ static int veml6031x00_hw_init(struct iio_dev *iio)
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to init iio gts\n");
 
+	reg = 0;
+	ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WL_L, &reg, sizeof(reg));
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to set low threshold\n");
+
+	reg = cpu_to_le16(U16_MAX);
+	ret = regmap_bulk_write(data->regmap, VEML6031X00_REG_WH_L, &reg, sizeof(reg));
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to set high threshold\n");
+
+	ret = regmap_field_write(data->rf.int_en, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(data->regmap, VEML6031X00_REG_INT, &val);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to clear interrupts\n");
+
+	return 0;
+}
+
+static int veml6031x00_setup_irq(struct i2c_client *i2c, struct iio_dev *iio)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	struct device *dev = data->dev;
+	int ret;
+
+	data->trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", iio->name,
+					    iio_device_id(iio));
+	if (!data->trig)
+		return -ENOMEM;
+
+	data->trig->ops = &veml6031x00_trigger_ops;
+	iio_trigger_set_drvdata(data->trig, iio);
+
+	ret = devm_iio_trigger_register(dev, data->trig);
+	if (ret)
+		return ret;
+
+	iio->trig = iio_trigger_get(data->trig);
+	ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
+					veml6031x00_interrupt,
+					IRQF_ONESHOT,
+					iio->name, iio);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request irq %d\n",
+				     i2c->irq);
+
 	return 0;
 }
 
@@ -618,6 +1039,10 @@ static int veml6031x00_probe(struct i2c_client *i2c)
 	data->dev = dev;
 	data->regmap = regmap;
 
+	ret = devm_mutex_init(dev, &data->lock);
+	if (ret)
+		return ret;
+
 	ret = veml6031x00_regfield_init(data);
 	if (ret)
 		return dev_err_probe(dev, ret, "Failed to init regfield\n");
@@ -657,12 +1082,21 @@ static int veml6031x00_probe(struct i2c_client *i2c)
 	iio->channels = veml6031x00_channels;
 	iio->num_channels = ARRAY_SIZE(veml6031x00_channels);
 	iio->modes = INDIO_DIRECT_MODE;
-	iio->info = &veml6031x00_info;
 
 	ret = veml6031x00_hw_init(iio);
 	if (ret)
 		return ret;
 
+	if (i2c->irq) {
+		ret = veml6031x00_setup_irq(i2c, iio);
+		if (ret)
+			return ret;
+
+		iio->info = &veml6031x00_info;
+	} else {
+		iio->info = &veml6031x00_info_no_irq;
+	}
+
 	ret = devm_iio_triggered_buffer_setup(dev, iio, NULL,
 					      veml6031x00_trig_handler,
 					      &veml6031x00_buffer_setup_ops);

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 3/4] iio: light: veml6031x00: add support for triggered buffers
From: Javier Carrasco @ 2026-05-24 21:53 UTC (permalink / raw)
  To: Jonathan Cameron, Lars-Peter Clausen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Rishi Gupta, David Lechner,
	Nuno Sá, Andy Shevchenko, Matti Vaittinen
  Cc: linux-iio, devicetree, linux-kernel, Javier Carrasco,
	Jonathan Cameron
In-Reply-To: <20260524-veml6031x00-v3-0-29165609b2b5@gmail.com>

Add triggered buffer functionality for the two channels the device
provides (ALS and IR).

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
 drivers/iio/light/Kconfig       |   2 +
 drivers/iio/light/veml6031x00.c | 106 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+)

diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 99a6ed80c7db..ff71de8454bd 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -717,6 +717,8 @@ config VEML6031X00
 	tristate "VEML6031X00 ambient light sensor series"
 	select REGMAP_I2C
 	select IIO_GTS_HELPER
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
 	depends on I2C
 	help
 	  Say Y here if you want to build a driver for the Vishay VEML6031X00
diff --git a/drivers/iio/light/veml6031x00.c b/drivers/iio/light/veml6031x00.c
index 50979d239230..9968d4414dc9 100644
--- a/drivers/iio/light/veml6031x00.c
+++ b/drivers/iio/light/veml6031x00.c
@@ -14,6 +14,8 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 #include <linux/iio/iio-gts-helper.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 /* Device registers */
 #define VEML6031X00_REG_CONF0       0x00
@@ -29,6 +31,12 @@
 #define VEML6031X00_CONF0_SD        BIT(0)
 #define VEML6031X00_CONF1_IR_SD     BIT(7)
 
+enum veml6031x00_scan {
+	VEML6031X00_SCAN_ALS,
+	VEML6031X00_SCAN_IR,
+	VEML6031X00_SCAN_TIMESTAMP,
+};
+
 struct veml6031x00_rf {
 	struct regmap_field *gain;
 	struct regmap_field *it;
@@ -128,6 +136,13 @@ static const struct iio_chan_spec veml6031x00_channels[] = {
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = VEML6031X00_SCAN_ALS,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
 	},
 	{
 		.type = IIO_INTENSITY,
@@ -138,7 +153,15 @@ static const struct iio_chan_spec veml6031x00_channels[] = {
 				      BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+		.scan_index = VEML6031X00_SCAN_IR,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
 	},
+	IIO_CHAN_SOFT_TIMESTAMP(VEML6031X00_SCAN_TIMESTAMP),
 };
 
 static const struct regmap_range veml6031x00_readable_ranges[] = {
@@ -461,6 +484,82 @@ static const struct iio_info veml6031x00_info = {
 	.write_raw_get_fmt = veml6031x00_write_raw_get_fmt,
 };
 
+static int veml6031x00_buffer_preenable(struct iio_dev *iio)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ret, it_usec;
+
+	ret = pm_runtime_resume_and_get(data->dev);
+	if (ret)
+		return ret;
+
+	ret = veml6031x00_get_it(data, &it_usec);
+	if (ret < 0) {
+		pm_runtime_put_autosuspend(data->dev);
+		return ret;
+	}
+
+	/*
+	 * Wait one integration period + 10% margin so the first triggered
+	 * read does not race with the sensor completing its first conversion
+	 * after power-on.
+	 */
+	fsleep(it_usec + (it_usec / 10));
+
+	return 0;
+}
+
+static int veml6031x00_buffer_postdisable(struct iio_dev *iio)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+
+	pm_runtime_put_autosuspend(data->dev);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops veml6031x00_buffer_setup_ops = {
+	.preenable = veml6031x00_buffer_preenable,
+	.postdisable = veml6031x00_buffer_postdisable,
+};
+
+static irqreturn_t veml6031x00_trig_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio = pf->indio_dev;
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ch, ret, i = 0;
+	struct {
+		__le16 chans[2];
+		aligned_s64 timestamp;
+	} scan = { };
+
+	if (*iio->active_scan_mask == (BIT(VEML6031X00_SCAN_ALS) |
+				       BIT(VEML6031X00_SCAN_IR))) {
+		ret = regmap_bulk_read(data->regmap,
+				       VEML6031X00_REG_ALS_L,
+				       &scan.chans, sizeof(scan.chans));
+		if (ret)
+			goto done;
+	} else {
+		iio_for_each_active_channel(iio, ch) {
+			ret = regmap_bulk_read(data->regmap,
+					       iio->channels[ch].address,
+					       &scan.chans[i++],
+					       sizeof(*scan.chans));
+			if (ret)
+				goto done;
+		}
+	}
+
+	iio_push_to_buffers_with_ts(iio, &scan, sizeof(scan), pf->timestamp);
+
+done:
+	iio_trigger_notify_done(iio->trig);
+
+	return IRQ_HANDLED;
+}
+
 static int veml6031x00_validate_part_id(struct veml6031x00_data *data)
 {
 	int part_id, ret;
@@ -564,6 +663,13 @@ static int veml6031x00_probe(struct i2c_client *i2c)
 	if (ret)
 		return ret;
 
+	ret = devm_iio_triggered_buffer_setup(dev, iio, NULL,
+					      veml6031x00_trig_handler,
+					      &veml6031x00_buffer_setup_ops);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to register triggered buffer\n");
+
 	pm_runtime_put_autosuspend(dev);
 
 	ret = devm_iio_device_register(dev, iio);

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 2/4] iio: light: add support for veml6031x00 ALS series
From: Javier Carrasco @ 2026-05-24 21:53 UTC (permalink / raw)
  To: Jonathan Cameron, Lars-Peter Clausen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Rishi Gupta, David Lechner,
	Nuno Sá, Andy Shevchenko, Matti Vaittinen
  Cc: linux-iio, devicetree, linux-kernel, Javier Carrasco,
	Jonathan Cameron
In-Reply-To: <20260524-veml6031x00-v3-0-29165609b2b5@gmail.com>

These sensors provide two light channels (ALS and IR), I2C communication
and a multiplexed interrupt line to signal data ready and configurable
threshold alarms.

This first implementation provides basic functionality (measurement
configuration, raw reads and ID validation) and defines the different
register regions in preparation for extended features in the subsequent
patches of the series.

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
 MAINTAINERS                     |   1 +
 drivers/iio/light/Kconfig       |  12 +
 drivers/iio/light/Makefile      |   1 +
 drivers/iio/light/veml6031x00.c | 657 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 671 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 94a23bac568d..47da46717c16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28385,6 +28385,7 @@ VISHAY VEML6031X00 AMBIENT LIGHT SENSOR DRIVER
 M:	Javier Carrasco <javier.carrasco.cruz@gmail.com>
 S:	Maintained
 F:	Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
+F:	drivers/iio/light/veml6031x00.c
 
 VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
 M:	Javier Carrasco <javier.carrasco.cruz@gmail.com>
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index eff33e456c70..99a6ed80c7db 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -713,6 +713,18 @@ config VEML6030
 	  To compile this driver as a module, choose M here: the
 	  module will be called veml6030.
 
+config VEML6031X00
+	tristate "VEML6031X00 ambient light sensor series"
+	select REGMAP_I2C
+	select IIO_GTS_HELPER
+	depends on I2C
+	help
+	  Say Y here if you want to build a driver for the Vishay VEML6031X00
+	  ambient light sensor series.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called veml6031x00.
+
 config VEML6040
 	tristate "VEML6040 RGBW light sensor"
 	select REGMAP_I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index c0048e0d5ca8..a8cc03cfb6c2 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_VCNL4000)		+= vcnl4000.o
 obj-$(CONFIG_VCNL4035)		+= vcnl4035.o
 obj-$(CONFIG_VEML3235)		+= veml3235.o
 obj-$(CONFIG_VEML6030)		+= veml6030.o
+obj-$(CONFIG_VEML6031X00)	+= veml6031x00.o
 obj-$(CONFIG_VEML6040)		+= veml6040.o
 obj-$(CONFIG_VEML6046X00)	+= veml6046x00.o
 obj-$(CONFIG_VEML6070)		+= veml6070.o
diff --git a/drivers/iio/light/veml6031x00.c b/drivers/iio/light/veml6031x00.c
new file mode 100644
index 000000000000..50979d239230
--- /dev/null
+++ b/drivers/iio/light/veml6031x00.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * VEML6031X00 Ambient Light Sensor
+ *
+ * Copyright (c) 2026, Javier Carrasco <javier.carrasco.cruz@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/iio-gts-helper.h>
+
+/* Device registers */
+#define VEML6031X00_REG_CONF0       0x00
+#define VEML6031X00_REG_CONF1       0x01
+#define VEML6031X00_REG_ALS_L       0x10
+#define VEML6031X00_REG_ALS_H       0x11
+#define VEML6031X00_REG_IR_L        0x12
+#define VEML6031X00_REG_IR_H        0x13
+#define VEML6031X00_REG_ID_L        0x14
+#define VEML6031X00_REG_ID_H        0x15
+
+/* Bit masks for specific functionality */
+#define VEML6031X00_CONF0_SD        BIT(0)
+#define VEML6031X00_CONF1_IR_SD     BIT(7)
+
+struct veml6031x00_rf {
+	struct regmap_field *gain;
+	struct regmap_field *it;
+	struct regmap_field *pd_div4;
+};
+
+struct veml6031x00_chip {
+	const char *name;
+	const int part_id;
+};
+
+struct veml6031x00_data {
+	struct device *dev;
+	struct iio_gts gts;
+	struct regmap *regmap;
+	struct veml6031x00_rf rf;
+	const struct veml6031x00_chip *chip;
+};
+
+static const struct iio_itime_sel_mul veml6031x00_it_sel[] = {
+	GAIN_SCALE_ITIME_US(3125, 0, 1),
+	GAIN_SCALE_ITIME_US(6250, 1, 2),
+	GAIN_SCALE_ITIME_US(12500, 2, 4),
+	GAIN_SCALE_ITIME_US(25000, 3, 8),
+	GAIN_SCALE_ITIME_US(50000, 4, 16),
+	GAIN_SCALE_ITIME_US(100000, 5, 32),
+	GAIN_SCALE_ITIME_US(200000, 6, 64),
+	GAIN_SCALE_ITIME_US(400000, 7, 128),
+};
+
+/*
+ * The gain selector encodes (PD_D4 << 2) | GAIN to identify each gain setting.
+ * Gains are multiplied by 8 to work with integers. The values in the iio-gts
+ * tables don't need corrections because the maximum value of the scale refers
+ * to GAIN = x1, and the rest of the values are obtained from the resulting
+ * linear function.
+ * TODO: add support for MILLI_GAIN_X165 and MILLI_GAIN_X660
+ */
+#define VEML6031X00_SEL_MILLI_GAIN_X125  0x07
+#define VEML6031X00_SEL_MILLI_GAIN_X250  0x04
+#define VEML6031X00_SEL_MILLI_GAIN_X500  0x03
+#define VEML6031X00_SEL_MILLI_GAIN_X1000 0x00
+#define VEML6031X00_SEL_MILLI_GAIN_X2000 0x01
+static const struct iio_gain_sel_pair veml6031x00_gain_sel[] = {
+	GAIN_SCALE_GAIN(1, VEML6031X00_SEL_MILLI_GAIN_X125),
+	GAIN_SCALE_GAIN(2, VEML6031X00_SEL_MILLI_GAIN_X250),
+	GAIN_SCALE_GAIN(4, VEML6031X00_SEL_MILLI_GAIN_X500),
+	GAIN_SCALE_GAIN(8, VEML6031X00_SEL_MILLI_GAIN_X1000),
+	GAIN_SCALE_GAIN(16, VEML6031X00_SEL_MILLI_GAIN_X2000),
+};
+
+/*
+ * Two shutdown bits (SD and ALS_IR_SD) must be cleared to power on
+ * the device.
+ */
+static int veml6031x00_als_power_on(struct veml6031x00_data *data)
+{
+	int ret;
+
+	ret = regmap_clear_bits(data->regmap, VEML6031X00_REG_CONF0,
+				VEML6031X00_CONF0_SD);
+	if (ret)
+		return ret;
+
+	return regmap_clear_bits(data->regmap, VEML6031X00_REG_CONF1,
+				 VEML6031X00_CONF1_IR_SD);
+}
+
+/*
+ * Two shutdown bits (SD and ALS_IR_SD) must be set to power off
+ * the device.
+ */
+static int veml6031x00_als_shutdown(struct veml6031x00_data *data)
+{
+	int ret;
+
+	ret = regmap_set_bits(data->regmap, VEML6031X00_REG_CONF0,
+			      VEML6031X00_CONF0_SD);
+	if (ret)
+		return ret;
+
+	return regmap_set_bits(data->regmap, VEML6031X00_REG_CONF1,
+			       VEML6031X00_CONF1_IR_SD);
+}
+
+static void veml6031x00_als_shutdown_action(void *data)
+{
+	veml6031x00_als_shutdown(data);
+}
+
+static const struct iio_chan_spec veml6031x00_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.address = VEML6031X00_REG_ALS_L,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+	},
+	{
+		.type = IIO_INTENSITY,
+		.address = VEML6031X00_REG_IR_L,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_IR,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+	},
+};
+
+static const struct regmap_range veml6031x00_readable_ranges[] = {
+	regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1),
+	regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_ID_H),
+};
+
+static const struct regmap_access_table veml6031x00_readable_table = {
+	.yes_ranges = veml6031x00_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(veml6031x00_readable_ranges),
+};
+
+static const struct regmap_range veml6031x00_writable_ranges[] = {
+	regmap_reg_range(VEML6031X00_REG_CONF0, VEML6031X00_REG_CONF1),
+};
+
+static const struct regmap_access_table veml6031x00_writable_table = {
+	.yes_ranges = veml6031x00_writable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(veml6031x00_writable_ranges),
+};
+
+static const struct regmap_range veml6031x00_volatile_ranges[] = {
+	regmap_reg_range(VEML6031X00_REG_ALS_L, VEML6031X00_REG_IR_H),
+};
+
+static const struct regmap_access_table veml6031x00_volatile_table = {
+	.yes_ranges = veml6031x00_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(veml6031x00_volatile_ranges),
+};
+
+static const struct regmap_config veml6031x00_regmap_config = {
+	.name = "veml6031x00_regmap",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.rd_table = &veml6031x00_readable_table,
+	.wr_table = &veml6031x00_writable_table,
+	.volatile_table = &veml6031x00_volatile_table,
+	.max_register = VEML6031X00_REG_ID_H,
+	.cache_type = REGCACHE_MAPLE,
+};
+
+static const struct reg_field veml6031x00_rf_it =
+	REG_FIELD(VEML6031X00_REG_CONF0, 4, 6);
+
+static const struct reg_field veml6031x00_rf_gain =
+	REG_FIELD(VEML6031X00_REG_CONF1, 3, 4);
+
+static const struct reg_field veml6031x00_rf_pd_div4 =
+	REG_FIELD(VEML6031X00_REG_CONF1, 6, 6);
+
+static int veml6031x00_regfield_init(struct veml6031x00_data *data)
+{
+	struct regmap *regmap = data->regmap;
+	struct device *dev = data->dev;
+	struct regmap_field *rm_field;
+	struct veml6031x00_rf *rf = &data->rf;
+
+	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_gain);
+	if (IS_ERR(rm_field))
+		return PTR_ERR(rm_field);
+	rf->gain = rm_field;
+
+	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_it);
+	if (IS_ERR(rm_field))
+		return PTR_ERR(rm_field);
+	rf->it = rm_field;
+
+	rm_field = devm_regmap_field_alloc(dev, regmap, veml6031x00_rf_pd_div4);
+	if (IS_ERR(rm_field))
+		return PTR_ERR(rm_field);
+	rf->pd_div4 = rm_field;
+
+	return 0;
+}
+
+static int veml6031x00_get_it(struct veml6031x00_data *data, int *val2)
+{
+	int ret, it_idx;
+
+	ret = regmap_field_read(data->rf.it, &it_idx);
+	if (ret)
+		return ret;
+
+	ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+	if (ret < 0)
+		return ret;
+
+	*val2 = ret;
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6031x00_set_it(struct iio_dev *iio, int val, int val2)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int ret, gain_sel, gain_reg, pd_div4, it_idx, new_gain, prev_gain, prev_it;
+	bool in_range;
+
+	if (val || !iio_gts_valid_time(&data->gts, val2))
+		return -EINVAL;
+
+	ret = regmap_field_read(data->rf.it, &it_idx);
+	if (ret)
+		return ret;
+
+	ret = regmap_field_read(data->rf.gain, &gain_reg);
+	if (ret)
+		return ret;
+
+	ret = regmap_field_read(data->rf.pd_div4, &pd_div4);
+	if (ret)
+		return ret;
+
+	prev_it = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+	if (prev_it < 0)
+		return prev_it;
+
+	if (prev_it == val2)
+		return 0;
+
+	prev_gain = iio_gts_find_gain_by_sel(&data->gts, (pd_div4 << 2) | gain_reg);
+	if (prev_gain < 0)
+		return prev_gain;
+
+	ret = iio_gts_find_new_gain_by_gain_time_min(&data->gts, prev_gain, prev_it,
+						     val2, &new_gain, &in_range);
+	if (ret)
+		return ret;
+
+	if (!in_range)
+		dev_dbg(data->dev, "Optimal gain out of range\n");
+
+	ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_field_write(data->rf.it, ret);
+	if (ret)
+		return ret;
+
+	gain_sel = iio_gts_find_sel_by_gain(&data->gts, new_gain);
+	if (gain_sel < 0)
+		return gain_sel;
+
+	ret = regmap_field_write(data->rf.pd_div4, gain_sel >> 2);
+	if (ret)
+		return ret;
+
+	return regmap_field_write(data->rf.gain, gain_sel & 0x03);
+}
+
+static int veml6031x00_set_scale(struct iio_dev *iio, int val, int val2)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int gain_sel, it_sel, ret;
+
+	ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2,
+						   &gain_sel, &it_sel);
+	if (ret)
+		return ret;
+
+	ret = regmap_field_write(data->rf.it, it_sel);
+	if (ret)
+		return ret;
+
+	ret = regmap_field_write(data->rf.pd_div4, gain_sel >> 2);
+	if (ret)
+		return ret;
+
+	return regmap_field_write(data->rf.gain, gain_sel & 0x03);
+}
+
+static int veml6031x00_get_scale(struct veml6031x00_data *data, int *val,
+				 int *val2)
+{
+	int gain, it, gain_reg, pd_div4, it_reg, ret, sel;
+
+	ret = regmap_field_read(data->rf.gain, &gain_reg);
+	if (ret)
+		return ret;
+
+	ret = regmap_field_read(data->rf.pd_div4, &pd_div4);
+	if (ret)
+		return ret;
+
+	sel = (pd_div4 << 2) | gain_reg;
+	gain = iio_gts_find_gain_by_sel(&data->gts, sel);
+	if (gain < 0)
+		return gain;
+
+	ret = regmap_field_read(data->rf.it, &it_reg);
+	if (ret)
+		return ret;
+
+	it = iio_gts_find_int_time_by_sel(&data->gts, it_reg);
+	if (it < 0)
+		return it;
+
+	ret = iio_gts_get_scale(&data->gts, gain, it, val, val2);
+	if (ret)
+		return ret;
+
+	return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int veml6031x00_single_read(struct iio_dev *iio, enum iio_chan_type type,
+				   int *val)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	int addr, it_usec, ret;
+	__le16 reg;
+
+	switch (type) {
+	case IIO_LIGHT:
+		addr = VEML6031X00_REG_ALS_L;
+		break;
+	case IIO_INTENSITY:
+		addr = VEML6031X00_REG_IR_L;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim);
+	if (IIO_DEV_ACQUIRE_FAILED(claim))
+		return -EBUSY;
+
+	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(data->dev, pm);
+	ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+	if (ret)
+		return ret;
+
+	ret = veml6031x00_get_it(data, &it_usec);
+	if (ret < 0)
+		return ret;
+
+	/* integration time + 10 % to ensure completion */
+	fsleep(it_usec + (it_usec / 10));
+
+	ret = regmap_bulk_read(data->regmap, addr, &reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	*val = le16_to_cpu(reg);
+	return IIO_VAL_INT;
+}
+
+static int veml6031x00_read_raw(struct iio_dev *iio,
+				struct iio_chan_spec const *chan, int *val,
+				int *val2, long mask)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return veml6031x00_single_read(iio, chan->type, val);
+	case IIO_CHAN_INFO_INT_TIME:
+		*val = 0;
+		return veml6031x00_get_it(data, val2);
+	case IIO_CHAN_INFO_SCALE:
+		return veml6031x00_get_scale(data, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int veml6031x00_read_avail(struct iio_dev *iio,
+				  struct iio_chan_spec const *chan,
+				  const int **vals, int *type, int *length,
+				  long mask)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_INT_TIME:
+		return iio_gts_avail_times(&data->gts, vals, type, length);
+	case IIO_CHAN_INFO_SCALE:
+		return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int veml6031x00_write_raw(struct iio_dev *iio,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim);
+	if (IIO_DEV_ACQUIRE_FAILED(claim))
+		return -EBUSY;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_INT_TIME:
+		return veml6031x00_set_it(iio, val, val2);
+	case IIO_CHAN_INFO_SCALE:
+		return veml6031x00_set_scale(iio, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int veml6031x00_write_raw_get_fmt(struct iio_dev *indio_dev,
+					 struct iio_chan_spec const *chan,
+					 long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_CHAN_INFO_INT_TIME:
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info veml6031x00_info = {
+	.read_raw = veml6031x00_read_raw,
+	.read_avail = veml6031x00_read_avail,
+	.write_raw = veml6031x00_write_raw,
+	.write_raw_get_fmt = veml6031x00_write_raw_get_fmt,
+};
+
+static int veml6031x00_validate_part_id(struct veml6031x00_data *data)
+{
+	int part_id, ret;
+	__le16 reg;
+
+	ret = regmap_bulk_read(data->regmap, VEML6031X00_REG_ID_L, &reg,
+			       sizeof(reg));
+	if (ret)
+		return dev_err_probe(data->dev, ret, "Failed to read ID\n");
+
+	part_id = le16_to_cpu(reg);
+	if (part_id != data->chip->part_id)
+		dev_warn(data->dev, "Unknown ID %04x\n", part_id);
+
+	return 0;
+}
+
+static int veml6031x00_hw_init(struct iio_dev *iio)
+{
+	struct veml6031x00_data *data = iio_priv(iio);
+	struct device *dev = data->dev;
+	int ret;
+
+	/* Max resolution = 6.9632 lx/cnt for gain = 0.125 and IT = 3.125ms */
+	ret = devm_iio_init_iio_gts(dev, 6, 963200000,
+				    veml6031x00_gain_sel,
+				    ARRAY_SIZE(veml6031x00_gain_sel),
+				    veml6031x00_it_sel,
+				    ARRAY_SIZE(veml6031x00_it_sel),
+				    &data->gts);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to init iio gts\n");
+
+	return 0;
+}
+
+static int veml6031x00_probe(struct i2c_client *i2c)
+{
+	struct device *dev = &i2c->dev;
+	struct veml6031x00_data *data;
+	struct iio_dev *iio;
+	struct regmap *regmap;
+	int ret;
+
+	regmap = devm_regmap_init_i2c(i2c, &veml6031x00_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap),
+				     "Failed to set regmap\n");
+
+	iio = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!iio)
+		return -ENOMEM;
+
+	data = iio_priv(iio);
+	i2c_set_clientdata(i2c, iio);
+	data->dev = dev;
+	data->regmap = regmap;
+
+	ret = veml6031x00_regfield_init(data);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to init regfield\n");
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+	data->chip = i2c_get_match_data(i2c);
+	if (!data->chip)
+		return dev_err_probe(dev, -EINVAL, "Failed to get chip data\n");
+
+	ret = devm_add_action_or_reset(dev, veml6031x00_als_shutdown_action, data);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add shutdown action\n");
+
+	/* The device starts in power down mode by default */
+	ret = veml6031x00_als_power_on(data);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to power on the device\n");
+
+	pm_runtime_set_autosuspend_delay(dev, 2000);
+	pm_runtime_use_autosuspend(dev);
+	ret = devm_pm_runtime_set_active_enabled(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
+
+	ret = devm_pm_runtime_get_noresume(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get runtime PM\n");
+
+	ret = veml6031x00_validate_part_id(data);
+	if (ret)
+		return ret;
+
+	iio->name = data->chip->name;
+	iio->channels = veml6031x00_channels;
+	iio->num_channels = ARRAY_SIZE(veml6031x00_channels);
+	iio->modes = INDIO_DIRECT_MODE;
+	iio->info = &veml6031x00_info;
+
+	ret = veml6031x00_hw_init(iio);
+	if (ret)
+		return ret;
+
+	pm_runtime_put_autosuspend(dev);
+
+	ret = devm_iio_device_register(dev, iio);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register iio device\n");
+
+	return 0;
+}
+
+static int veml6031x00_runtime_suspend(struct device *dev)
+{
+	struct veml6031x00_data *data = iio_priv(dev_get_drvdata(dev));
+
+	return veml6031x00_als_shutdown(data);
+}
+
+static int veml6031x00_runtime_resume(struct device *dev)
+{
+	struct veml6031x00_data *data = iio_priv(dev_get_drvdata(dev));
+
+	return veml6031x00_als_power_on(data);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(veml6031x00_pm_ops, veml6031x00_runtime_suspend,
+				 veml6031x00_runtime_resume, NULL);
+
+static const struct veml6031x00_chip veml6031x00_chip = {
+	.name = "veml6031x00",
+	.part_id = 0x0001,
+};
+
+static const struct veml6031x00_chip veml6031x01_chip = {
+	.name = "veml6031x01",
+	.part_id = 0x0001,
+};
+
+static const struct veml6031x00_chip veml60311x00_chip = {
+	.name = "veml60311x00",
+	.part_id = 0x1001,
+};
+
+static const struct veml6031x00_chip veml60311x01_chip = {
+	.name = "veml60311x01",
+	.part_id = 0x1001,
+};
+
+static const struct of_device_id veml6031x00_of_match[] = {
+	{
+		.compatible = "vishay,veml6031x00",
+		.data = &veml6031x00_chip,
+	},
+	{
+		.compatible = "vishay,veml6031x01",
+		.data = &veml6031x01_chip,
+	},
+	{
+		.compatible = "vishay,veml60311x00",
+		.data = &veml60311x00_chip,
+	},
+	{
+		.compatible = "vishay,veml60311x01",
+		.data = &veml60311x01_chip,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, veml6031x00_of_match);
+
+static const struct i2c_device_id veml6031x00_id[] = {
+	{ .name = "veml6031x00", .driver_data = (kernel_ulong_t)&veml6031x00_chip },
+	{ .name = "veml6031x01", .driver_data = (kernel_ulong_t)&veml6031x01_chip },
+	{ .name = "veml60311x00", .driver_data = (kernel_ulong_t)&veml60311x00_chip },
+	{ .name = "veml60311x01", .driver_data = (kernel_ulong_t)&veml60311x01_chip },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, veml6031x00_id);
+
+static struct i2c_driver veml6031x00_driver = {
+	.driver = {
+		.name = "veml6031x00",
+		.of_match_table = veml6031x00_of_match,
+		.pm = pm_ptr(&veml6031x00_pm_ops),
+	},
+	.probe = veml6031x00_probe,
+	.id_table = veml6031x00_id,
+};
+module_i2c_driver(veml6031x00_driver);
+
+MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
+MODULE_DESCRIPTION("VEML6031X00 Ambient Light Sensor");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_GTS_HELPER");

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 1/4] dt-bindings: iio: light: veml6030: add veml6031x00 ALS series
From: Javier Carrasco @ 2026-05-24 21:53 UTC (permalink / raw)
  To: Jonathan Cameron, Lars-Peter Clausen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Rishi Gupta, David Lechner,
	Nuno Sá, Andy Shevchenko, Matti Vaittinen
  Cc: linux-iio, devicetree, linux-kernel, Javier Carrasco,
	Jonathan Cameron, Krzysztof Kozlowski
In-Reply-To: <20260524-veml6031x00-v3-0-29165609b2b5@gmail.com>

These ambient light sensors share their properties with the ones
from the same manufacturer that are supported by this bindings.

Note that only two datasheets are provided as every one of them covers
two devices (veml6031x00/veml60311x00 and veml6031x01/veml60311x01).

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
 .../bindings/iio/light/vishay,veml6030.yaml        | 23 +++++++++++++++++++++-
 MAINTAINERS                                        |  5 +++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
index 4ea69f1fdd63..e01e8747e47c 100644
--- a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
+++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
@@ -4,7 +4,9 @@
 $id: http://devicetree.org/schemas/iio/light/vishay,veml6030.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: VEML3235, VEML6030, VEML6035 and VEML7700 Ambient Light Sensors (ALS)
+title:
+  VEML3235, VEML6030, VEML6031x00 series, VEML6035 and VEML7700 Ambient
+  Light Sensors (ALS)
 
 maintainers:
   - Rishi Gupta <gupt21@gmail.com>
@@ -22,12 +24,18 @@ description: |
   Specifications about the sensors can be found at:
     https://www.vishay.com/docs/80131/veml3235.pdf
     https://www.vishay.com/docs/84366/veml6030.pdf
+    https://www.vishay.com/docs/80007/veml6031x00.pdf
+    https://www.vishay.com/docs/80008/veml6031x01.pdf
     https://www.vishay.com/docs/84889/veml6035.pdf
     https://www.vishay.com/docs/84286/veml7700.pdf
 
 properties:
   compatible:
     enum:
+      - vishay,veml6031x00
+      - vishay,veml6031x01
+      - vishay,veml60311x00
+      - vishay,veml60311x01
       - vishay,veml3235
       - vishay,veml6030
       - vishay,veml6035
@@ -67,6 +75,8 @@ allOf:
       properties:
         compatible:
           enum:
+            - vishay,veml6031x00
+            - vishay,veml6031x01
             - vishay,veml6035
     then:
       properties:
@@ -79,12 +89,23 @@ allOf:
         compatible:
           enum:
             - vishay,veml3235
+            - vishay,veml60311x00
+            - vishay,veml60311x01
             - vishay,veml7700
     then:
       properties:
         reg:
           enum:
             - 0x10
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - vishay,veml3235
+            - vishay,veml7700
+    then:
+      properties:
         interrupts: false
 
 additionalProperties: false
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..94a23bac568d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28381,6 +28381,11 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
 F:	drivers/iio/light/veml6046x00.c
 
+VISHAY VEML6031X00 AMBIENT LIGHT SENSOR DRIVER
+M:	Javier Carrasco <javier.carrasco.cruz@gmail.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
+
 VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
 M:	Javier Carrasco <javier.carrasco.cruz@gmail.com>
 S:	Maintained

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 0/4] iio: light: add support for veml6031x00 ALS series
From: Javier Carrasco @ 2026-05-24 21:53 UTC (permalink / raw)
  To: Jonathan Cameron, Lars-Peter Clausen, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Rishi Gupta, David Lechner,
	Nuno Sá, Andy Shevchenko, Matti Vaittinen
  Cc: linux-iio, devicetree, linux-kernel, Javier Carrasco,
	Jonathan Cameron, Krzysztof Kozlowski

These ambient light sensors with I2C interface provide two light
channels (ALS and IR), high/low threshold alarms with configurable
persistence, and a data ready signal.

The devices covered by this driver have the same resolution, and they
share most of their functionality. These are the differences between
them (note that the x belongs to their names, and it is not a wildcard):

 - Device ID: accessible via two 8-byte registers, different values for
   veml6031x00/veml6031x01 and veml60311x00/veml60311x01.
 - I2C address: same grouping, 0x29 and 0x10 I2C addresses.
 - AEC qualification: AEC-Q100 for veml6031x00/veml60311x00 and
   AEC-Q101 for veml6031x01/veml60311x01.

The alarms and the data ready signals share the interrupt pin, and an
interrupt status register must be accessed to identify the source. Such
multiplexing is not new in IIO, and I have followed existing examples
for it. The persistence setting (own attribute) to trigger the alarms
uses the pattern that has already been used for the veml6030.

The device configuration is in general documented in the datasheet and
the application note. There is an exception, though: the activation of
the "active force" mode that is required for the data ready signal must
be carried out in two steps even though the affected bits are located in
the same register: first ALS_AF (active force mode enable) must be set,
and then ALS_TRIG (active force trigger setting) must be enabled. I have
added a brief commentary in the code to explain this behavior, which has
been confirmed by the manufacturer.

The only functionality that has not been implemented yet is the x0.66
gain (and its x0.165 counterpart when PD_DIV=1), which makes the gts
helpers less usable due to the conversions required. It is indeed an
uncommon gain to use (there are x0.5 and x0.125 gains) with no known
use-case at the moment that justifies making adjustments to the gts
helpers or adding artificial conversions to make it work.

This driver has been tested with the four supported devices separately
as well as in pairs where the I2C addresses don't overlap.

To: Jonathan Cameron <jic23@kernel.org>
To: Lars-Peter Clausen <lars@metafoo.de>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Rishi Gupta <gupt21@gmail.com>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
To: Matti Vaittinen <mazziesaccount@gmail.com>
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>

Changes in v3:
- Move veml6030 fixes to a separate patch stack.
- Use C99 initializers for i2c_device_id.
- Split driver code into multiple patches to ease its review.
- Rework locking to get rid of atomic increment/decrement ops.
- Fix error paths in pm_runtime operations.
- Use IIO_DEV_ACQUIRE_DIRECT_MODE for single read/write ops.
- Link to v2: https://lore.kernel.org/r/20260513-veml6031x00-v2-0-4703ca661a1d@gmail.com

Changes in v2:
- Add commit to fix bug in veml6030.c (channel type when pushing
  events) and remove dead code.
- Use gts helpers to simplify operations.
- Drop unused gain_idx.
- Build INT_MASK as an OR operation of the involved bits.
- Format arrays to follow the desired standard for IIO.
- Directly return function result as the last operation within another
  function instead of 'ret = x; if (ret) return ret; return 0;'.
- Fix some spacing (double space, tab for alignemnt in info struct).
- Use sizeof() for __le16 reg instead of 2.
- Return an error if the part ID could not be read.
- Spell out sd -> shutdown.
- Use devm_mutex_init() instead of mutex_init().
- Avoid using conditional guard, use claim/release instead.
- Access integration times from the global array to get and set the
  integration time instead of using a switch.
- Simplify read of available periods (persistence).
- Drop IRQF_TRIGGER_FALLING in the threaded irq request.
- Add regmap ranges.
- Link to v1: https://lore.kernel.org/r/20241126-veml6031x00-v1-0-4affa62bfefd@gmail.com

---
Javier Carrasco (4):
      dt-bindings: iio: light: veml6030: add veml6031x00 ALS series
      iio: light: add support for veml6031x00 ALS series
      iio: light: veml6031x00: add support for triggered buffers
      iio: light: veml6031x00: add support for events and trigger

 .../bindings/iio/light/vishay,veml6030.yaml        |   23 +-
 MAINTAINERS                                        |    6 +
 drivers/iio/light/Kconfig                          |   14 +
 drivers/iio/light/Makefile                         |    1 +
 drivers/iio/light/veml6031x00.c                    | 1197 ++++++++++++++++++++
 5 files changed, 1240 insertions(+), 1 deletion(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20241109-veml6031x00-aa9463da064a

Best regards,
-- 
Javier Carrasco <javier.carrasco.cruz@gmail.com>


^ permalink raw reply

* Re: [RFC PATCH v1 3/4] iio: flow: add Sensirion SLF3x liquid flow sensor driver
From: Guenter Roeck @ 2026-05-24 21:40 UTC (permalink / raw)
  To: Wadim Mueller, jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, jdelvare,
	ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-4-wafgo01@gmail.com>

On 5/24/26 13:49, Wadim Mueller wrote:
> From: Wadim Mueller <wadim.mueller@cmblu.de>
> 
> Add an IIO driver for the Sensirion SLF3S family of digital
> liquid-flow sensors.  The supported sub-types (SLF3S-0600F,
> SLF3S-4000B) share the same register map and command set and are
> distinguished only by the flow scale; the variant is detected at
> probe time from the product-information register.
> 
Sice you are at it, it might make sense to also support SLF3S-1300F.


> The driver exposes two IIO channels:
>    - in_volumeflow_raw / in_volumeflow_scale (litres per second)
>    - in_temp_raw       / in_temp_scale       (milli-degC)
> 
> Continuous measurement mode is started in probe and stopped via
> devm-action; read_raw() fetches the most recent sample on demand.
> 
> Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
> ---
>   drivers/iio/Kconfig       |   1 +
>   drivers/iio/Makefile      |   1 +
>   drivers/iio/flow/Kconfig  |  22 ++++
>   drivers/iio/flow/Makefile |   7 +
>   drivers/iio/flow/slf3x.c  | 264 ++++++++++++++++++++++++++++++++++++++

What does the "X" refer to ? Why not "S" ?

>   5 files changed, 295 insertions(+)
>   create mode 100644 drivers/iio/flow/Kconfig
>   create mode 100644 drivers/iio/flow/Makefile
>   create mode 100644 drivers/iio/flow/slf3x.c
> 
> 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..355857a6b
> --- /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_SLF3X
> +	tristate "Sensirion SLF3x 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-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 slf3x.
> +
> +endmenu
> diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile
> new file mode 100644
> index 000000000..9eb9bdde0
> --- /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_SLF3X) += slf3x.o
> diff --git a/drivers/iio/flow/slf3x.c b/drivers/iio/flow/slf3x.c
> new file mode 100644
> index 000000000..e4ee1a04a
> --- /dev/null
> +++ b/drivers/iio/flow/slf3x.c
> @@ -0,0 +1,264 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sensirion SLF3x liquid flow sensor driver.
> + *
> + * Copyright (C) 2026 CMBlu Energy
> + * Author: Wadim Mueller <wadim.mueller@cmblu.de>
> + */
> +
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <linux/unaligned.h>
> +
> +#define SLF3X_CRC8_POLY		0x31
> +#define SLF3X_CRC8_INIT		0xff
> +
> +#define SLF3X_PRODUCT_ID_LEN	18
> +#define SLF3X_FAMILY_BYTE	1
> +#define SLF3X_SUBTYPE_BYTE	3
> +
> +#define SLF3X_FAMILY_ID		0x03
> +
> +#define SLF3X_MEAS_LEN		9
> +#define SLF3X_MEAS_DELAY_US	12000
> +
> +/* Temperature LSB is 1/200 °C; IIO_TEMP scale is in mC/LSB => 5. */
> +#define SLF3X_TEMP_SCALE	5
> +
> +static const u8 slf3x_cmd_prep_pid[]	= { 0x36, 0x7c };
> +static const u8 slf3x_cmd_read_pid[]	= { 0xe1, 0x02 };
> +static const u8 slf3x_cmd_start_water[]	= { 0x36, 0x08 };

I looked at LQ_DS_SLF3S-1300F, LQ_DS_SLF3S-0600F, and LQ_DS_SLF3S-4000B.
They all also support Isopropyl alcohol (IPA) measurements.

Would it make sense to also provide support for other liquid types
besides water ? That could be a sysfs attribute and/or a devicetree
property.

> +static const u8 slf3x_cmd_stop[]	= { 0x3f, 0xf9 };
> +
> +DECLARE_CRC8_TABLE(slf3x_crc_table);
> +
> +struct slf3x_variant {
> +	u8 sub_type;
> +	const char *name;
> +	/*
> +	 * Flow scale exposed via IIO_CHAN_INFO_SCALE in litres per second
> +	 * per LSB, encoded as IIO_VAL_FRACTIONAL (num / den).  The encoding
> +	 * comes from the Sensirion datasheet's "scale factor" (ticks per
> +	 * ml/min) combined with the 1 ml/min = 1/60000 l/s conversion.
> +	 */

Not my call to make, but at least the Sensirion sensors all talk about
flow rate per minute, not per second. A Google search suggests that
flow rate is normally measured per minute or even per hour, and that
per-second measurements are typically only used for large-scale engineering,
rivers, dams, and rapid industrial chemical dosing. Taking SLF3S-0600F
as example, it measures up to ±2000 µl/min (!). Even Sensirion's gas
sensors use per-minute flow rates.

Any special reason to use a per-second rate ?

> +	int scale_num;
> +	int scale_den;
> +};
> +
> +static const struct slf3x_variant slf3x_variants[] = {
> +	{ .sub_type = 0x03, .name = "slf3s-0600f",
> +	  .scale_num = 1, .scale_den = 6000000 },
> +	{ .sub_type = 0x05, .name = "slf3s-4000b",
> +	  .scale_num = 1, .scale_den = 1666680000 },
> +};
> +
> +struct slf3x_data {
> +	struct i2c_client *client;
> +	const struct slf3x_variant *variant;
> +};
> +
> +static int slf3x_verify_crc(const u8 *block)
> +{
> +	return crc8(slf3x_crc_table, block, 2, SLF3X_CRC8_INIT) == block[2] ?
> +		       0 :
> +		       -EIO;

This returns -EIO on error ...

> +}
> +
> +static int slf3x_write_cmd(struct i2c_client *client, const u8 *cmd)
> +{
> +	int ret = i2c_master_send(client, cmd, 2);
> +
> +	if (ret == 2)
> +		return 0;
> +	return ret < 0 ? ret : -EIO;
> +}
> +
> +static int slf3x_read_product_info(struct slf3x_data *sf)
> +{
> +	struct i2c_client *client = sf->client;
> +	u8 buf[SLF3X_PRODUCT_ID_LEN];
> +	int ret, i;
> +
> +	ret = slf3x_write_cmd(client, slf3x_cmd_prep_pid);
> +	if (ret)
> +		return ret;
> +
> +	ret = slf3x_write_cmd(client, slf3x_cmd_read_pid);
> +	if (ret)
> +		return ret;
> +
> +	ret = i2c_master_recv(client, buf, sizeof(buf));
> +	if (ret != sizeof(buf))
> +		return ret < 0 ? ret : -EIO;
> +
> +	for (i = 0; i < SLF3X_PRODUCT_ID_LEN; i += 3) {
> +		if (slf3x_verify_crc(&buf[i])) {

... which is then ignored here and replaced by -EIO.

Why not just have it return a boolean ?

> +			dev_err(&client->dev,
> +				"product-info CRC mismatch at byte %d\n", i);
> +			return -EIO;
> +		}
> +	}
> +
> +	if (buf[SLF3X_FAMILY_BYTE] != SLF3X_FAMILY_ID) {
> +		dev_err(&client->dev,
> +			"unexpected device family 0x%02x\n",
> +			buf[SLF3X_FAMILY_BYTE]);
> +		return -ENODEV;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(slf3x_variants); i++) {
> +		if (buf[SLF3X_SUBTYPE_BYTE] == slf3x_variants[i].sub_type) {
> +			sf->variant = &slf3x_variants[i];
> +			return 0;
> +		}
> +	}
> +
> +	dev_err(&client->dev, "unsupported SLF3x sub-type 0x%02x\n",
> +		buf[SLF3X_SUBTYPE_BYTE]);

Not my call to make, but the driver is way too noisy for my liking.

> +	return -ENODEV;
> +}
> +
> +static int slf3x_read_sample(struct slf3x_data *sf, s16 *flow, s16 *temp)
> +{
> +	u8 buf[SLF3X_MEAS_LEN];
> +	int ret, i;
> +
> +	ret = i2c_master_recv(sf->client, buf, sizeof(buf));
> +	if (ret != sizeof(buf))
> +		return ret < 0 ? ret : -EIO;
> +
> +	for (i = 0; i < SLF3X_MEAS_LEN; i += 3) {
> +		if (slf3x_verify_crc(&buf[i]))
> +			return -EIO;
> +	}
> +
> +	*flow = (s16)get_unaligned_be16(&buf[0]);
> +	*temp = (s16)get_unaligned_be16(&buf[3]);
> +	return 0;
> +}
> +
> +static const struct iio_chan_spec slf3x_channels[] = {
> +	{
> +		.type = IIO_VOLUMEFLOW,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +	},
> +	{
> +		.type = IIO_TEMP,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +	},
> +};
> +
> +static int slf3x_read_raw(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan, int *val,
> +			  int *val2, long mask)
> +{
> +	struct slf3x_data *sf = iio_priv(indio_dev);
> +	s16 flow, temp;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = slf3x_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) {
> +			*val = sf->variant->scale_num;
> +			*val2 = sf->variant->scale_den;
> +			return IIO_VAL_FRACTIONAL;
> +		}
> +		*val = SLF3X_TEMP_SCALE;
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info slf3x_info = {
> +	.read_raw = slf3x_read_raw,
> +};
> +
> +static void slf3x_stop_meas(void *data)
> +{
> +	struct slf3x_data *sf = data;
> +
> +	slf3x_write_cmd(sf->client, slf3x_cmd_stop);
> +}
> +
> +static int slf3x_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct iio_dev *indio_dev;
> +	struct slf3x_data *sf;
> +	int ret;
> +
> +	ret = devm_regulator_get_enable_optional(dev, "vdd");
> +	if (ret < 0 && ret != -ENODEV)
> +		return dev_err_probe(dev, ret, "failed to enable vdd\n");
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*sf));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	sf = iio_priv(indio_dev);
> +	sf->client = client;
> +	crc8_populate_msb(slf3x_crc_table, SLF3X_CRC8_POLY);
> +
> +	ret = slf3x_read_product_info(sf);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "product info read failed\n");
> +
> +	ret = slf3x_write_cmd(client, slf3x_cmd_start_water);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "start measurement failed\n");
> +
> +	usleep_range(SLF3X_MEAS_DELAY_US, SLF3X_MEAS_DELAY_US + 1000);
> +
> +	ret = devm_add_action_or_reset(dev, slf3x_stop_meas, sf);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->name = sf->variant->name;
> +	indio_dev->channels = slf3x_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(slf3x_channels);
> +	indio_dev->info = &slf3x_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	return devm_iio_device_register(dev, indio_dev);
> +}
> +
> +static const struct i2c_device_id slf3x_id[] = {
> +	{ "slf3s" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, slf3x_id);
> +
> +static const struct of_device_id slf3x_of_match[] = {
> +	{ .compatible = "sensirion,slf3s" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, slf3x_of_match);
> +
> +static struct i2c_driver slf3x_driver = {
> +	.driver = {
> +		.name = "slf3x",
> +		.of_match_table = slf3x_of_match,
> +	},
> +	.probe = slf3x_probe,
> +	.id_table = slf3x_id,
> +};
> +module_i2c_driver(slf3x_driver);
> +
> +MODULE_AUTHOR("Wadim Mueller <wadim.mueller@cmblu.de>");
> +MODULE_DESCRIPTION("Sensirion SLF3x liquid flow sensor driver");
> +MODULE_LICENSE("GPL");


^ permalink raw reply

* Re: [RFC PATCH v1 1/4] iio: types: add IIO_VOLUMEFLOW channel type
From: Guenter Roeck @ 2026-05-24 21:39 UTC (permalink / raw)
  To: Wadim Mueller, jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, jdelvare,
	ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-2-wafgo01@gmail.com>

On 5/24/26 13:49, Wadim Mueller wrote:
> From: Wadim Mueller <wadim.mueller@cmblu.de>
> 
> Add a new IIO channel type for liquid volumetric flow sensors.  The
> unit exposed via the standard _scale attribute is litres per second
> (l/s), so drivers reporting smaller native units (e.g. ml/min) only
> need to set a fractional scale.
> 

Two questions: Why restricting to liquid (there are also gas flow sensors),
and why litres/second and not per minute which seems to be a more common
unit ?

More on the latter in patch 3.

Thanks,
Guenter

> Update iio-core's name table, the iio_event_monitor whitelist and
> the sysfs-bus-iio ABI document to match.
> 
> Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
> ---
>   Documentation/ABI/testing/sysfs-bus-iio | 17 +++++++++++++++++
>   drivers/iio/industrialio-core.c         |  1 +
>   include/uapi/linux/iio/types.h          |  1 +
>   tools/iio/iio_event_monitor.c           |  2 ++
>   4 files changed, 21 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 5f87dcee7..68eb703c1 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -2458,3 +2458,20 @@ 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:6.19
> +Contact:linux-iio@vger.kernel.org
> +Description:
> +Raw (unscaled) volumetric flow rate reading from the channel.
> +To convert to standard units (litres / second) apply the
> +channel's _scale (and _offset, when present).
> +
> +What:/sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
> +What:/sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
> +KernelVersion:6.19
> +Contact:linux-iio@vger.kernel.org
> +Description:
> +Scale factor applied to raw volumetric flow readings to obtain
> +the value in litres per second (l/s).
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 22eefd048..aa34fcd8e 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;


^ permalink raw reply

* Re: [RFC PATCH v1 3/4] iio: flow: add Sensirion SLF3x liquid flow sensor driver
From: sashiko-bot @ 2026-05-24 21:37 UTC (permalink / raw)
  To: Wadim Mueller; +Cc: conor+dt, linux-hwmon, devicetree, robh
In-Reply-To: <20260524205112.26638-4-wafgo01@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The fractional scale for the SLF3S-4000B variant evaluates to exactly 0.0 in sysfs due to IIO formatting logic, permanently zeroing out flow readings for userspace.
- [Medium] Concurrent initialization of the shared global `slf3x_crc_table` in `probe()` causes a data race.
- [Medium] Continuous measurement mode is not restarted after a system suspend/resume cycle.
--

commit 2a3dbf69e1c64c32d26f6f97455487233c63c7c0
Author: Wadim Mueller <wadim.mueller@cmblu.de>

iio: flow: add Sensirion SLF3x liquid flow sensor driver

Add an IIO driver for the Sensirion SLF3S family of digital
liquid-flow sensors. The supported sub-types (SLF3S-0600F,
SLF3S-4000B) share the same register map and command set and are
distinguished only by the flow scale.

> diff --git a/drivers/iio/flow/slf3x.c b/drivers/iio/flow/slf3x.c
> new file mode 100644
> index 0000000000000..e4ee1a04a46d7
> --- /dev/null
> +++ b/drivers/iio/flow/slf3x.c

[ ... ]

> +static const struct slf3x_variant slf3x_variants[] = {
> +	{ .sub_type = 0x03, .name = "slf3s-0600f",
> +	  .scale_num = 1, .scale_den = 6000000 },
> +	{ .sub_type = 0x05, .name = "slf3s-4000b",
> +	  .scale_num = 1, .scale_den = 1666680000 },

[Severity: High]
Will the fractional scale for the SLF3S-4000B variant permanently zero out
flow readings for userspace?

The driver returns IIO_VAL_FRACTIONAL with val = 1 and val2 = 1666680000.
The IIO core formats fractional values for sysfs up to nanounit precision
(9 decimal places) using the calculation (val * 1000000000LL) / val2.

Because the denominator (1,666,680,000) is strictly larger than the 10^9
multiplier, the integer division computes to exactly 0. As a result, the
string emitted to sysfs will be 0.000000000.

Userspace applications rely on this sysfs attribute to convert raw readings
into physical units, meaning any flow rate reading will be multiplied by 0.0,
rendering the sensor's flow channel completely non-functional.

> +};

[ ... ]

> +static int slf3x_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct iio_dev *indio_dev;
> +	struct slf3x_data *sf;
> +	int ret;
> +
> +	ret = devm_regulator_get_enable_optional(dev, "vdd");
> +	if (ret < 0 && ret != -ENODEV)
> +		return dev_err_probe(dev, ret, "failed to enable vdd\n");
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*sf));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	sf = iio_priv(indio_dev);
> +	sf->client = client;
> +	crc8_populate_msb(slf3x_crc_table, SLF3X_CRC8_POLY);

[Severity: Medium]
Can this concurrent initialization of the shared global slf3x_crc_table cause
a data race?

The driver defines a global static array slf3x_crc_table and dynamically
populates it by calling crc8_populate_msb() unconditionally during
slf3x_probe().

If multiple SLF3x sensors are present and probed concurrently on different I2C
buses, or if a newly plugged sensor is probed while an already-registered
sensor is handling an active read, multiple threads will concurrently modify
or read the global table without synchronization.

This could result in transient -EIO errors or Kernel Concurrency Sanitizer
warnings.

> +
> +	ret = slf3x_read_product_info(sf);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "product info read failed\n");

[ ... ]

> +static struct i2c_driver slf3x_driver = {
> +	.driver = {
> +		.name = "slf3x",
> +		.of_match_table = slf3x_of_match,
> +	},
> +	.probe = slf3x_probe,
> +	.id_table = slf3x_id,
> +};
> +module_i2c_driver(slf3x_driver);

[Severity: Medium]
Will continuous measurement mode fail to restart after a system suspend/resume
cycle?

The driver initiates the sensor's continuous measurement mode (0x36 0x08)
exclusively in probe() and assumes the device stays in this mode indefinitely
to fetch samples on demand in read_raw().

However, the driver does not implement power management suspend and resume
callbacks. If the system suspends and the sensor loses power (e.g., via
regulator cutoff), it will wake up in its default idle state.

Without a resume callback to re-issue the continuous measurement command,
subsequent read operations in read_raw() will return invalid data, silently
breaking the driver after a suspend/resume cycle.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260524205112.26638-1-wafgo01@gmail.com?part=3

^ permalink raw reply

* Re: [PATCH v2 7/8] dt-bindings: display: allwinner: Split H616 DE33 layer reg space
From: Chen-Yu Tsai @ 2026-05-24 21:33 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Jernej Skrabec, samuel, mripard, maarten.lankhorst, tzimmermann,
	airlied, simona, robh, krzk+dt, conor+dt, mturquette, sboyd,
	dri-devel, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-clk
In-Reply-To: <20260514-valiant-ape-of-discourse-a4f5a9@quoll>

Hi,

(resent from new email)

On Thu, May 14, 2026 at 2:04 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Sat, May 09, 2026 at 09:00:14PM +0200, Jernej Skrabec wrote:
> > From: Jernej Skrabec <jernej.skrabec@gmail.com>
> >
> > As it turns out, current H616 DE33 binding was written based on
> > incomplete understanding of DE33 design. Namely, planes are shared
> > resource and not tied to specific mixer, which was the case for previous
> > generations of Display Engine (DE3 and earlier).
> >
> > This means that current DE33 binding doesn't properly reflect HW and
> > using it would mean that second mixer (used for second display output)
> > can't be supported.
> >
> > Remove layer register space, which will be represented with additional
> > node, and replace it with phandle, which will point to that new, shared
> > node. That way, all mixers can share same layers.
> >
> > There is no user of this binding yet, so changes can be made safely,
> > without breaking any backward compatibility.
>
> There is user. git grep gives me:
> drivers/gpu/drm/sun4i/sun8i_mixer.c
>
> which means this is a released ABI. As I understood, the old code was

We held off on merging the DT changes so that we could rework this.
I can't find the actual request though. It was probably over IRC.

> working fine but just did not support all use cases. Why this cannot be
> kept backwards compatible?

AFAIK the "planes" block is shared between two display mixers. As the
commit message explains, this prevents using the second mixer, since
only one of them can claim and map the register space. And on the H700
(which is the same die as the H616 discussed here but with more exposed
interfaces), there could actually be a use case for the second mixer.

Hope that explains things.


ChenYu

^ permalink raw reply

* Re: [PATCH v2 7/8] dt-bindings: display: allwinner: Split H616 DE33 layer reg space
From: Chen-Yu Tsai @ 2026-05-24 21:20 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Jernej Skrabec, samuel, mripard, maarten.lankhorst, tzimmermann,
	airlied, simona, robh, krzk+dt, conor+dt, mturquette, sboyd,
	dri-devel, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-clk
In-Reply-To: <20260514-valiant-ape-of-discourse-a4f5a9@quoll>

Hi,

On Thu, May 14, 2026 at 2:04 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Sat, May 09, 2026 at 09:00:14PM +0200, Jernej Skrabec wrote:
> > From: Jernej Skrabec <jernej.skrabec@gmail.com>
> >
> > As it turns out, current H616 DE33 binding was written based on
> > incomplete understanding of DE33 design. Namely, planes are shared
> > resource and not tied to specific mixer, which was the case for previous
> > generations of Display Engine (DE3 and earlier).
> >
> > This means that current DE33 binding doesn't properly reflect HW and
> > using it would mean that second mixer (used for second display output)
> > can't be supported.
> >
> > Remove layer register space, which will be represented with additional
> > node, and replace it with phandle, which will point to that new, shared
> > node. That way, all mixers can share same layers.
> >
> > There is no user of this binding yet, so changes can be made safely,
> > without breaking any backward compatibility.
>
> There is user. git grep gives me:
> drivers/gpu/drm/sun4i/sun8i_mixer.c
>
> which means this is a released ABI. As I understood, the old code was

We held off on merging the DT changes so that we could rework this.
I can't find the actual request though. It was probably over IRC.

> working fine but just did not support all use cases. Why this cannot be
> kept backwards compatible?

AFAIK the "planes" block is shared between two display mixers. As the
commit message explains, this prevents using the second mixer, since
only one of them can claim and map the register space. And on the H700
(which is the same die as the H616 discussed here but with more exposed
interfaces), there could actually be a use case for the second mixer.

Hope that explains things.


ChenYu

^ permalink raw reply

* Re: [RFC PATCH v1 2/4] dt-bindings: iio: flow: add Sensirion SLF3x liquid flow sensor
From: sashiko-bot @ 2026-05-24 21:10 UTC (permalink / raw)
  To: Wadim Mueller; +Cc: robh, linux-hwmon, devicetree, conor+dt
In-Reply-To: <20260524205112.26638-3-wafgo01@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The binding defines a generic compatible string `sensirion,slf3s` instead of specific hardware part numbers.
--

commit 5a08fd855b770d7594ec38ac7c89f4642153097b
Author: Wadim Mueller <wadim.mueller@cmblu.de>

dt-bindings: iio: flow: add Sensirion SLF3x liquid flow sensor

This commit adds a device tree binding for the Sensirion SLF3x family of I2C
liquid-flow sensors. It exposes a single generic compatible string and relies
on runtime detection to differentiate between sub-types.

> diff --git a/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml

[ ... ]

> +properties:
> +  compatible:
> +    enum:
> +      - sensirion,slf3s

[Severity: Low]
Should this define specific hardware part numbers like "sensirion,slf3s-0600f"
and "sensirion,slf3s-4000b" instead of just a generic string?

Standard device tree practice usually requires describing the exact hardware
present on the board rather than relying solely on driver auto-detection
logic. Including specific compatibles, potentially with this generic string as
a fallback, could ensure forward compatibility if future hardware revisions
modify or break the auto-detection mechanism.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260524205112.26638-1-wafgo01@gmail.com?part=2

^ permalink raw reply

* Re: [RFC PATCH v1 1/4] iio: types: add IIO_VOLUMEFLOW channel type
From: sashiko-bot @ 2026-05-24 21:08 UTC (permalink / raw)
  To: Wadim Mueller; +Cc: robh, conor+dt, devicetree, linux-hwmon
In-Reply-To: <20260524205112.26638-2-wafgo01@gmail.com>

> From: Wadim Mueller <wadim.mueller@cmblu.de>
> 
> Add a new IIO channel type for liquid volumetric flow sensors.  The
> unit exposed via the standard _scale attribute is litres per second
> (l/s), so drivers reporting smaller native units (e.g. ml/min) only
> need to set a fractional scale.
> 
> Update iio-core's name table, the iio_event_monitor whitelist and
> the sysfs-bus-iio ABI document to match.
> 
> Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260524205112.26638-1-wafgo01@gmail.com?part=1


^ permalink raw reply

* Re: (subset) [PATCH v3 0/3] Add GPADC support for A523
From: Chen-Yu Tsai @ 2026-05-24 21:04 UTC (permalink / raw)
  To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jernej Skrabec,
	Samuel Holland, Maksim Kiselev, Michal Piekos
  Cc: linux-iio, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Conor Dooley
In-Reply-To: <20260516-sunxi-a523-gpadc-v3-0-a3a04cff2620@mmpsystems.pl>

On Sat, 16 May 2026 07:34:13 +0200, Michal Piekos wrote:
> Add support for Allwinner A523 GPADC in sun20i gpadc driver and describe
> corresponding node in dts for A523 SoC.
> 
> A523 uses same model as existing driver except it has two clocks.
> 
> Added support to enable more than one clock in the driver, extended the
> binding with new compatible and wired up dts node for A523 as its own
> fallback compatible.
> 
> [...]

Applied to sunxi/dt-for-7.2 in sunxi, thanks!

[3/3] arm64: dts: allwinner: a523: add gpadc node
      https://git.kernel.org/sunxi/linux/c/44cf19e41c76

Best regards,
-- 
Chen-Yu Tsai <wens@kernel.org>


^ permalink raw reply

* [RFC PATCH v1 4/4] MAINTAINERS: add entry for Sensirion SLF3x flow sensor driver
From: Wadim Mueller @ 2026-05-24 20:49 UTC (permalink / raw)
  To: jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux,
	jdelvare, ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-1-wafgo01@gmail.com>

From: Wadim Mueller <wadim.mueller@cmblu.de>

Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 06a8c7457..5cafa8c8f 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 SLF3X LIQUID FLOW SENSOR DRIVER
+M:	Wadim Mueller <wadim.mueller@cmblu.de>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
+F:	drivers/iio/flow/slf3x.c
+
 SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
 M:	Tomasz Duszynski <tduszyns@gmail.com>
 S:	Maintained
-- 
2.52.0


^ permalink raw reply related

* [RFC PATCH v1 3/4] iio: flow: add Sensirion SLF3x liquid flow sensor driver
From: Wadim Mueller @ 2026-05-24 20:49 UTC (permalink / raw)
  To: jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux,
	jdelvare, ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-1-wafgo01@gmail.com>

From: Wadim Mueller <wadim.mueller@cmblu.de>

Add an IIO driver for the Sensirion SLF3S family of digital
liquid-flow sensors.  The supported sub-types (SLF3S-0600F,
SLF3S-4000B) share the same register map and command set and are
distinguished only by the flow scale; the variant is detected at
probe time from the product-information register.

The driver exposes two IIO channels:
  - in_volumeflow_raw / in_volumeflow_scale (litres per second)
  - in_temp_raw       / in_temp_scale       (milli-degC)

Continuous measurement mode is started in probe and stopped via
devm-action; read_raw() fetches the most recent sample on demand.

Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
---
 drivers/iio/Kconfig       |   1 +
 drivers/iio/Makefile      |   1 +
 drivers/iio/flow/Kconfig  |  22 ++++
 drivers/iio/flow/Makefile |   7 +
 drivers/iio/flow/slf3x.c  | 264 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 295 insertions(+)
 create mode 100644 drivers/iio/flow/Kconfig
 create mode 100644 drivers/iio/flow/Makefile
 create mode 100644 drivers/iio/flow/slf3x.c

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..355857a6b
--- /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_SLF3X
+	tristate "Sensirion SLF3x 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-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 slf3x.
+
+endmenu
diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile
new file mode 100644
index 000000000..9eb9bdde0
--- /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_SLF3X) += slf3x.o
diff --git a/drivers/iio/flow/slf3x.c b/drivers/iio/flow/slf3x.c
new file mode 100644
index 000000000..e4ee1a04a
--- /dev/null
+++ b/drivers/iio/flow/slf3x.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SLF3x liquid flow sensor driver.
+ *
+ * Copyright (C) 2026 CMBlu Energy
+ * Author: Wadim Mueller <wadim.mueller@cmblu.de>
+ */
+
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/unaligned.h>
+
+#define SLF3X_CRC8_POLY		0x31
+#define SLF3X_CRC8_INIT		0xff
+
+#define SLF3X_PRODUCT_ID_LEN	18
+#define SLF3X_FAMILY_BYTE	1
+#define SLF3X_SUBTYPE_BYTE	3
+
+#define SLF3X_FAMILY_ID		0x03
+
+#define SLF3X_MEAS_LEN		9
+#define SLF3X_MEAS_DELAY_US	12000
+
+/* Temperature LSB is 1/200 °C; IIO_TEMP scale is in mC/LSB => 5. */
+#define SLF3X_TEMP_SCALE	5
+
+static const u8 slf3x_cmd_prep_pid[]	= { 0x36, 0x7c };
+static const u8 slf3x_cmd_read_pid[]	= { 0xe1, 0x02 };
+static const u8 slf3x_cmd_start_water[]	= { 0x36, 0x08 };
+static const u8 slf3x_cmd_stop[]	= { 0x3f, 0xf9 };
+
+DECLARE_CRC8_TABLE(slf3x_crc_table);
+
+struct slf3x_variant {
+	u8 sub_type;
+	const char *name;
+	/*
+	 * Flow scale exposed via IIO_CHAN_INFO_SCALE in litres per second
+	 * per LSB, encoded as IIO_VAL_FRACTIONAL (num / den).  The encoding
+	 * comes from the Sensirion datasheet's "scale factor" (ticks per
+	 * ml/min) combined with the 1 ml/min = 1/60000 l/s conversion.
+	 */
+	int scale_num;
+	int scale_den;
+};
+
+static const struct slf3x_variant slf3x_variants[] = {
+	{ .sub_type = 0x03, .name = "slf3s-0600f",
+	  .scale_num = 1, .scale_den = 6000000 },
+	{ .sub_type = 0x05, .name = "slf3s-4000b",
+	  .scale_num = 1, .scale_den = 1666680000 },
+};
+
+struct slf3x_data {
+	struct i2c_client *client;
+	const struct slf3x_variant *variant;
+};
+
+static int slf3x_verify_crc(const u8 *block)
+{
+	return crc8(slf3x_crc_table, block, 2, SLF3X_CRC8_INIT) == block[2] ?
+		       0 :
+		       -EIO;
+}
+
+static int slf3x_write_cmd(struct i2c_client *client, const u8 *cmd)
+{
+	int ret = i2c_master_send(client, cmd, 2);
+
+	if (ret == 2)
+		return 0;
+	return ret < 0 ? ret : -EIO;
+}
+
+static int slf3x_read_product_info(struct slf3x_data *sf)
+{
+	struct i2c_client *client = sf->client;
+	u8 buf[SLF3X_PRODUCT_ID_LEN];
+	int ret, i;
+
+	ret = slf3x_write_cmd(client, slf3x_cmd_prep_pid);
+	if (ret)
+		return ret;
+
+	ret = slf3x_write_cmd(client, slf3x_cmd_read_pid);
+	if (ret)
+		return ret;
+
+	ret = i2c_master_recv(client, buf, sizeof(buf));
+	if (ret != sizeof(buf))
+		return ret < 0 ? ret : -EIO;
+
+	for (i = 0; i < SLF3X_PRODUCT_ID_LEN; i += 3) {
+		if (slf3x_verify_crc(&buf[i])) {
+			dev_err(&client->dev,
+				"product-info CRC mismatch at byte %d\n", i);
+			return -EIO;
+		}
+	}
+
+	if (buf[SLF3X_FAMILY_BYTE] != SLF3X_FAMILY_ID) {
+		dev_err(&client->dev,
+			"unexpected device family 0x%02x\n",
+			buf[SLF3X_FAMILY_BYTE]);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(slf3x_variants); i++) {
+		if (buf[SLF3X_SUBTYPE_BYTE] == slf3x_variants[i].sub_type) {
+			sf->variant = &slf3x_variants[i];
+			return 0;
+		}
+	}
+
+	dev_err(&client->dev, "unsupported SLF3x sub-type 0x%02x\n",
+		buf[SLF3X_SUBTYPE_BYTE]);
+	return -ENODEV;
+}
+
+static int slf3x_read_sample(struct slf3x_data *sf, s16 *flow, s16 *temp)
+{
+	u8 buf[SLF3X_MEAS_LEN];
+	int ret, i;
+
+	ret = i2c_master_recv(sf->client, buf, sizeof(buf));
+	if (ret != sizeof(buf))
+		return ret < 0 ? ret : -EIO;
+
+	for (i = 0; i < SLF3X_MEAS_LEN; i += 3) {
+		if (slf3x_verify_crc(&buf[i]))
+			return -EIO;
+	}
+
+	*flow = (s16)get_unaligned_be16(&buf[0]);
+	*temp = (s16)get_unaligned_be16(&buf[3]);
+	return 0;
+}
+
+static const struct iio_chan_spec slf3x_channels[] = {
+	{
+		.type = IIO_VOLUMEFLOW,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+	},
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+	},
+};
+
+static int slf3x_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan, int *val,
+			  int *val2, long mask)
+{
+	struct slf3x_data *sf = iio_priv(indio_dev);
+	s16 flow, temp;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = slf3x_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) {
+			*val = sf->variant->scale_num;
+			*val2 = sf->variant->scale_den;
+			return IIO_VAL_FRACTIONAL;
+		}
+		*val = SLF3X_TEMP_SCALE;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info slf3x_info = {
+	.read_raw = slf3x_read_raw,
+};
+
+static void slf3x_stop_meas(void *data)
+{
+	struct slf3x_data *sf = data;
+
+	slf3x_write_cmd(sf->client, slf3x_cmd_stop);
+}
+
+static int slf3x_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct iio_dev *indio_dev;
+	struct slf3x_data *sf;
+	int ret;
+
+	ret = devm_regulator_get_enable_optional(dev, "vdd");
+	if (ret < 0 && ret != -ENODEV)
+		return dev_err_probe(dev, ret, "failed to enable vdd\n");
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*sf));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	sf = iio_priv(indio_dev);
+	sf->client = client;
+	crc8_populate_msb(slf3x_crc_table, SLF3X_CRC8_POLY);
+
+	ret = slf3x_read_product_info(sf);
+	if (ret)
+		return dev_err_probe(dev, ret, "product info read failed\n");
+
+	ret = slf3x_write_cmd(client, slf3x_cmd_start_water);
+	if (ret)
+		return dev_err_probe(dev, ret, "start measurement failed\n");
+
+	usleep_range(SLF3X_MEAS_DELAY_US, SLF3X_MEAS_DELAY_US + 1000);
+
+	ret = devm_add_action_or_reset(dev, slf3x_stop_meas, sf);
+	if (ret)
+		return ret;
+
+	indio_dev->name = sf->variant->name;
+	indio_dev->channels = slf3x_channels;
+	indio_dev->num_channels = ARRAY_SIZE(slf3x_channels);
+	indio_dev->info = &slf3x_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id slf3x_id[] = {
+	{ "slf3s" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, slf3x_id);
+
+static const struct of_device_id slf3x_of_match[] = {
+	{ .compatible = "sensirion,slf3s" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, slf3x_of_match);
+
+static struct i2c_driver slf3x_driver = {
+	.driver = {
+		.name = "slf3x",
+		.of_match_table = slf3x_of_match,
+	},
+	.probe = slf3x_probe,
+	.id_table = slf3x_id,
+};
+module_i2c_driver(slf3x_driver);
+
+MODULE_AUTHOR("Wadim Mueller <wadim.mueller@cmblu.de>");
+MODULE_DESCRIPTION("Sensirion SLF3x liquid flow sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.52.0


^ permalink raw reply related

* [RFC PATCH v1 2/4] dt-bindings: iio: flow: add Sensirion SLF3x liquid flow sensor
From: Wadim Mueller @ 2026-05-24 20:49 UTC (permalink / raw)
  To: jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux,
	jdelvare, ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-1-wafgo01@gmail.com>

From: Wadim Mueller <wadim.mueller@cmblu.de>

Add a DT binding for the Sensirion SLF3x family of I2C liquid-flow
sensors.  A single 'sensirion,slf3s' compatible is exposed because
the sub-types share the same register map and are detected at
runtime via the product-information register.

Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
---
 .../bindings/iio/flow/sensirion,slf3s.yaml    | 49 +++++++++++++++++++
 1 file changed, 49 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..5ef62435f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
@@ -0,0 +1,49 @@
+# 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 SLF3x liquid flow sensor
+
+maintainers:
+  - Wadim Mueller <wadim.mueller@cmblu.de>
+
+description: |
+  Family of digital liquid-flow sensors from Sensirion with I2C interface.
+  The same register map is shared by all family members; sub-types differ
+  only in the flow scale factor exposed by the device and are detected at
+  probe time via the product-information register.
+
+  Supported sub-types:
+    - SLF3S-0600F (calibrated range ±2 ml/min)
+    - SLF3S-4000B (calibrated range ±80 ml/min)
+
+properties:
+  compatible:
+    enum:
+      - sensirion,slf3s
+
+  reg:
+    maxItems: 1
+
+  vdd-supply: true
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      flow-sensor@8 {
+        compatible = "sensirion,slf3s";
+        reg = <0x08>;
+        vdd-supply = <&reg_3v3>;
+      };
+    };
-- 
2.52.0


^ permalink raw reply related

* [RFC PATCH v1 1/4] iio: types: add IIO_VOLUMEFLOW channel type
From: Wadim Mueller @ 2026-05-24 20:49 UTC (permalink / raw)
  To: jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux,
	jdelvare, ak, linux-iio, linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260524205112.26638-1-wafgo01@gmail.com>

From: Wadim Mueller <wadim.mueller@cmblu.de>

Add a new IIO channel type for liquid volumetric flow sensors.  The
unit exposed via the standard _scale attribute is litres per second
(l/s), so drivers reporting smaller native units (e.g. ml/min) only
need to set a fractional scale.

Update iio-core's name table, the iio_event_monitor whitelist and
the sysfs-bus-iio ABI document to match.

Signed-off-by: Wadim Mueller <wadim.mueller@cmblu.de>
---
 Documentation/ABI/testing/sysfs-bus-iio | 17 +++++++++++++++++
 drivers/iio/industrialio-core.c         |  1 +
 include/uapi/linux/iio/types.h          |  1 +
 tools/iio/iio_event_monitor.c           |  2 ++
 4 files changed, 21 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 5f87dcee7..68eb703c1 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2458,3 +2458,20 @@ 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:6.19
+Contact:linux-iio@vger.kernel.org
+Description:
+Raw (unscaled) volumetric flow rate reading from the channel.
+To convert to standard units (litres / second) apply the
+channel's _scale (and _offset, when present).
+
+What:/sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
+What:/sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
+KernelVersion:6.19
+Contact:linux-iio@vger.kernel.org
+Description:
+Scale factor applied to raw volumetric flow readings to obtain
+the value in litres per second (l/s).
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 22eefd048..aa34fcd8e 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

* [RFC PATCH v1 0/4] iio: add Sensirion SLF3x liquid flow sensor support
From: Wadim Mueller @ 2026-05-24 20:49 UTC (permalink / raw)
  To: jic23
  Cc: lars, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux,
	jdelvare, ak, linux-iio, linux-hwmon, devicetree, linux-kernel

From: Wadim Mueller <wadim.mueller@cmblu.de>

Hi all,

this RFC adds support for the Sensirion SLF3x family of liquid-flow
sensors (SLF3S-0600F and SLF3S-4000B).  Before I send it as a normal
patch I'd like to ask three things:

1. Subsystem.  Liquid-flow sensors don't seem to have a home in
   mainline yet.  iio/ feels like the natural place for me, but
   please correct me if hwmon (or somewhere else) is prefered.

2. Channel type.  IIO has no flow channel type so far.  The series
   adds IIO_VOLUMEFLOW with _scale in litres per second per LSB,
   so drivers reporting smaller units only need a fractional scale.
   IIO_MASSFLOW (for gas-flow sensors) was left out on purpose --
   happy to add it in the same series if that's more usefull.

3. Subdirectory.  I put the driver in a new drivers/iio/flow/
   since there is no flow subsytem in iio yet.  If colocating with
   drivers/iio/pressure/ (next to sdp500) is preferred I'll respin.

Patches:

  1/4  iio: types: add IIO_VOLUMEFLOW channel type
  2/4  dt-bindings: iio: flow: add sensirion,slf3s binding
  3/4  iio: flow: add Sensirion SLF3x driver
  4/4  MAINTAINERS: add entry

Tested with a SLF3S-0600F on a TI AM64x platform.

Thanks,
Wadim

Wadim Mueller (4):
  iio: types: add IIO_VOLUMEFLOW channel type
  dt-bindings: iio: flow: add Sensirion SLF3x liquid flow sensor
  iio: flow: add Sensirion SLF3x liquid flow sensor driver
  MAINTAINERS: add entry for Sensirion SLF3x flow sensor driver

 Documentation/ABI/testing/sysfs-bus-iio       |  17 ++
 .../bindings/iio/flow/sensirion,slf3s.yaml    |  49 ++++
 MAINTAINERS                                   |   7 +
 drivers/iio/Kconfig                           |   1 +
 drivers/iio/Makefile                          |   1 +
 drivers/iio/flow/Kconfig                      |  22 ++
 drivers/iio/flow/Makefile                     |   7 +
 drivers/iio/flow/slf3x.c                      | 264 ++++++++++++++++++
 drivers/iio/industrialio-core.c               |   1 +
 include/uapi/linux/iio/types.h                |   1 +
 tools/iio/iio_event_monitor.c                 |   2 +
 11 files changed, 372 insertions(+)
 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/slf3x.c


base-commit: 3cd8b194bf3428dfa53120fee47e827a7c495815
-- 
2.52.0


^ permalink raw reply

* Re: [PATCH 5/7] arm64: dts: allwinner: sun50i-h6: Add missing SRAM region for video engine
From: Chen-Yu Tsai @ 2026-05-24 20:46 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: Maxime Ripard, Mauro Carvalho Chehab, Jernej Skrabec,
	Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Greg Kroah-Hartman, linux-media, linux-staging, devicetree,
	linux-sunxi, linux-arm-kernel, linux-kernel
In-Reply-To: <afpUiupOgPhLksM8@shepard>

On Tue, May 5, 2026 at 10:35 PM Paul Kocialkowski <paulk@sys-base.io> wrote:
>
> Hi Chen-Yu,
>
> On Tue 05 May 26, 21:48, Chen-Yu Tsai wrote:
> > The SRAM C region contains a partial alias to the VE SRAM already
> > referenced by the video engine. To avoid access through this alias
> > window, the region should also be claimed by the video engine.
> >
> > Add a reference to the SRAM C region to the video engine node.
>
> It feels very weird to have a reference to the DE2 SRAM region in the
> VE node. It seems unlikely that the same region would be used by both DE2 and
> VE and I am pretty sure can have both running at the same time without
> overstepping.
>
> From what I can see we have so far assumed that the SRAM C and SRAM C1
> are two different physical SRAM areas, but this is most likely not the case.
> My guess would be that SRAM C1 is actually a part of SRAM C and the DE2 is

No. It's the opposite. 0x28000 maps to the same SRAM block at 0x1a00000.
If you write to one, you see the results in both. However the alias at
028000 can be disabled while retaining access to 0x1a00000. I don't
remember how much of the alias points to VE SRAM (0x1a00000). Maybe it
was the whole alias on the H6.

Also, I can't remember if this was tested on the H6 or H616, but the
VE indeed writes to VE SRAM (0x1a00000), so that part is indeed used
by the video engine.

> using another part of it. The syscon block probably allows switching access
> to these different parts of SRAM C.

From what I've seen in the vendor BSP, both drivers toggle the switch.
I believe the goal is to prevent the CPU from having access, rather than
either peripheral claiming it as its own. And that is also what our SRAM
driver does. It claims the SRAM from the CPU. That's all. The toggle bit
pattern used by both drivers is the same.

> Also the sram_c1 node implies it's 2 MiB, which sounds quite unlikely.

That is what the memory map says. Calling it SRAM C1 is probably wrong.


ChenYu

> All the best,
>
> Paul
>
> >
> > Fixes: b542570e5605 ("arm64: dts: allwinner: h6: Add Video Engine node")
> > Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
> > ---
> >  arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
> > index 72ce1a75647b..88c6e3e105c0 100644
> > --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
> > +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
> > @@ -205,7 +205,7 @@ video-codec@1c0e000 {
> >                       clock-names = "ahb", "mod", "ram";
> >                       resets = <&ccu RST_BUS_VE>;
> >                       interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
> > -                     allwinner,sram = <&ve_sram 1>;
> > +                     allwinner,sram = <&ve_sram 1>, <&de2_sram 1>;
> >                       iommus = <&iommu 3>;
> >               };
> >
> > --
> > 2.47.3
> >
>
> --
> Paul Kocialkowski,
>
> Independent contractor - sys-base - https://www.sys-base.io/
> Free software developer - https://www.paulk.fr/
>
> Expert in multimedia, graphics and embedded hardware support with Linux.

^ permalink raw reply

* Re: [PATCH 1/2] crypto: Delete Qualcomm crypto engine driver
From: Eric Biggers @ 2026-05-24 20:45 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Demi Marie Obenour, Dmitry Baryshkov, Herbert Xu, David S. Miller,
	Thara Gopinath, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Russell King, linux-kernel,
	linux-crypto, linux-arm-msm, Ard Biesheuvel, devicetree,
	linux-arm-kernel
In-Reply-To: <d97382a6-6c5d-4a3f-89cc-3ae9b432de3f@kernel.org>

On Sun, May 24, 2026 at 10:29:28PM +0200, Krzysztof Kozlowski wrote:
> On 24/05/2026 22:12, Demi Marie Obenour wrote:
> > On 5/24/26 12:42, Dmitry Baryshkov wrote:
> >> On Sat, May 23, 2026 at 03:03:56PM -0400, Demi Marie Obenour via B4 Relay wrote:
> >>> From: Demi Marie Obenour <demiobenour@gmail.com>
> >>>
> >>> It's slower than the generic C code and causes problems.
> >>
> >> Which problems?
> > 
> > See https://lore.kernel.org/all/20260522024912.GC5937@quark/.
> 
> Your commit is still incomplete and other people's opinion is poor
> reason. If you do not know what to write, ask that person to make
> necessary changes.
> 
> Not mentioning that removing driver is not even necessary to achieve the
> goal Eric was mentioning and if I understood correctly: you are removing
> even the pieces Eric found useful.

This driver is more than an order of magnitude slower than the CPU for
both encryption and hashing.  See:

    https://lore.kernel.org/r/20250704070322.20692-1-ebiggers@kernel.org/
    https://lore.kernel.org/r/20250615031807.GA81869@sol/

There are many examples of it having bugs as well, for example see the
second link above.

That's why it had to be disabled via the cra_priority system.  This
driver was actively making Linux worse.

This isn't particularly unique to drivers/crypto/, of course.  This one
we just have data on, so it's a bit clearer.

I've yet to see any real reason to keep this driver.

Crypto drivers need to be held to a higher standard than other device
drivers, as well.  The onus is on those who want to keep a particular
crypto driver to prove that it's worth keeping.

- Eric

^ permalink raw reply

* Re: [PATCH 1/2] crypto: Delete Qualcomm crypto engine driver
From: Demi Marie Obenour @ 2026-05-24 20:31 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Dmitry Baryshkov
  Cc: Herbert Xu, David S. Miller, Thara Gopinath, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Russell King, linux-kernel, linux-crypto, linux-arm-msm,
	Eric Biggers, Ard Biesheuvel, devicetree, linux-arm-kernel
In-Reply-To: <d97382a6-6c5d-4a3f-89cc-3ae9b432de3f@kernel.org>


[-- Attachment #1.1.1: Type: text/plain, Size: 1919 bytes --]

On 5/24/26 16:29, Krzysztof Kozlowski wrote:
> On 24/05/2026 22:12, Demi Marie Obenour wrote:
>> On 5/24/26 12:42, Dmitry Baryshkov wrote:
>>> On Sat, May 23, 2026 at 03:03:56PM -0400, Demi Marie Obenour via B4 Relay wrote:
>>>> From: Demi Marie Obenour <demiobenour@gmail.com>
>>>>
>>>> It's slower than the generic C code and causes problems.
>>>
>>> Which problems?
>>
>> See https://lore.kernel.org/all/20260522024912.GC5937@quark/.
> 
> Your commit is still incomplete and other people's opinion is poor
> reason. If you do not know what to write, ask that person to make
> necessary changes.
> 
> Not mentioning that removing driver is not even necessary to achieve the
> goal Eric was mentioning and if I understood correctly: you are removing
> even the pieces Eric found useful.
> 
>>
>> Also, if there are no systems in which the QCE driver is actually
>> the highest priority, then unless someone adjusts priorities manually
>> it's unused code.
> 
> That's not a reason to remove a driver.
> 
> 
>>
>>> Also in the security world faster and safer are two orthogonal axis with
>>> very limited correlation.
>>
>> If by "safer" you mean protection against physical side-channel
>> attacks, then my understanding is that all operations on secret keys
>> need to be masked.  This includes copying and storage.
>>
>> Linux only supports this for protected keys, and even then sometimes
>> uses the kernel's own RNG for key generation.  There is no support
>> for using the QCE for protected keys.
>>
>> Linux does support using hardware-wrapped keys with inline crypto
>> engines, which are what are actually used on Android.
> 
> Patches are discussed for some time, did you miss that?
> 
> Best regards,
> Krzysztof

Thanks for the useful explanation.  I'll remove this patch from my
tree and won't resend it.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: (subset) [PATCH v3 00/17] arm64: Use EL2 virtual timer when running VHE
From: Chen-Yu Tsai @ 2026-05-24 20:30 UTC (permalink / raw)
  To: linux-arm-kernel, linux-acpi, linux-kernel, devicetree,
	Marc Zyngier
  Cc: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
	Will Deacon, Rafael J. Wysocki, Mark Rutland, Daniel Lezcano,
	Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jernej Skrabec, Samuel Holland, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Ge Gordon,
	BST Linux Kernel Upstream Group, Jesper Nilsson, Lars Persson,
	Alim Akhtar, Ivaylo Ivanov, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Dinh Nguyen,
	Matthias Brugger, AngeloGioacchino Del Regno, Thierry Reding,
	Jonathan Hunter, Bjorn Andersson, Konrad Dybcio,
	Andreas Färber, Yu-Chun Lin [林祐君],
	Heiko Stuebner, Shawn Lin, Orson Zhai, Baolin Wang, Michal Simek
In-Reply-To: <20260523140242.586031-1-maz@kernel.org>

On Sat, 23 May 2026 15:02:25 +0100, Marc Zyngier wrote:
> This is the third version of the series initially posted at [1],
> which
> 
> - updates the ACPI GTDT parsing to deal the v3 layout and the EL2
>   virtual timer,
> - moves the architected timer driver to use it when running VHE,
> - fixes a number of DTs to reflect the reality of the HW.
> 
> [...]

Applied to sunxi/dt-for-7.2 in sunxi, thanks!

[05/17] arm64: dts: allwinner: Add EL2 virtual timer interrupt
        https://git.kernel.org/sunxi/linux/c/86eeca347091

Best regards,
-- 
Chen-Yu Tsai <wens@kernel.org>


^ permalink raw reply

* Re: [PATCH 1/2] crypto: Delete Qualcomm crypto engine driver
From: Krzysztof Kozlowski @ 2026-05-24 20:29 UTC (permalink / raw)
  To: Demi Marie Obenour, Dmitry Baryshkov
  Cc: Herbert Xu, David S. Miller, Thara Gopinath, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Russell King, linux-kernel, linux-crypto, linux-arm-msm,
	Eric Biggers, Ard Biesheuvel, devicetree, linux-arm-kernel
In-Reply-To: <66317f6a-645e-432b-ae11-8f40569d4117@gmail.com>

On 24/05/2026 22:12, Demi Marie Obenour wrote:
> On 5/24/26 12:42, Dmitry Baryshkov wrote:
>> On Sat, May 23, 2026 at 03:03:56PM -0400, Demi Marie Obenour via B4 Relay wrote:
>>> From: Demi Marie Obenour <demiobenour@gmail.com>
>>>
>>> It's slower than the generic C code and causes problems.
>>
>> Which problems?
> 
> See https://lore.kernel.org/all/20260522024912.GC5937@quark/.

Your commit is still incomplete and other people's opinion is poor
reason. If you do not know what to write, ask that person to make
necessary changes.

Not mentioning that removing driver is not even necessary to achieve the
goal Eric was mentioning and if I understood correctly: you are removing
even the pieces Eric found useful.

> 
> Also, if there are no systems in which the QCE driver is actually
> the highest priority, then unless someone adjusts priorities manually
> it's unused code.

That's not a reason to remove a driver.


> 
>> Also in the security world faster and safer are two orthogonal axis with
>> very limited correlation.
> 
> If by "safer" you mean protection against physical side-channel
> attacks, then my understanding is that all operations on secret keys
> need to be masked.  This includes copying and storage.
> 
> Linux only supports this for protected keys, and even then sometimes
> uses the kernel's own RNG for key generation.  There is no support
> for using the QCE for protected keys.
> 
> Linux does support using hardware-wrapped keys with inline crypto
> engines, which are what are actually used on Android.

Patches are discussed for some time, did you miss that?

Best regards,
Krzysztof

^ permalink raw reply

* Re: (subset) [PATCH v9 0/9] Allwinner A31/A83T MIPI CSI-2 and A31 ISP / Platform Support
From: Chen-Yu Tsai @ 2026-05-24 20:26 UTC (permalink / raw)
  To: linux-media, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Paul Kocialkowski
  Cc: Yong Deng, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jernej Skrabec, Samuel Holland,
	Michael Turquette, Stephen Boyd, Brian Masney, Maxime Ripard
In-Reply-To: <20260518153339.619947-1-paulk@sys-base.io>

On Mon, 18 May 2026 17:33:29 +0200, Paul Kocialkowski wrote:
> This series adds platform support for the V3s/V3/S3 MIPI CSI-2 and ISP units
> as well the as A83T MIPI CSI-2 unit in the respective device-trees.
> Overlays for the BananaPi M3 cameras are also provided as actual users of the
> camera pipeline on A83T.
> 
> The corresponding drivers and dt bindings were merged a long time ago but this
> series was never actually picked up. It seems more than ready to be merged!
> 
> [...]

Applied to sunxi/dt-for-7.2 in sunxi, thanks!

[1/9] dt-bindings: media: sun6i-a31-csi: Add optional interconnect properties
      https://git.kernel.org/sunxi/linux/c/4543300b2d55
[2/9] dt-bindings: media: sun6i-a31-isp: Add optional interconnect properties
      https://git.kernel.org/sunxi/linux/c/bdf22efc6cb2
[4/9] ARM: dts: sun8i: v3s: Add mbus node to represent the interconnect
      https://git.kernel.org/sunxi/linux/c/3c6867908ecb
[7/9] ARM: dts: sun8i: a83t: Add MIPI CSI-2 controller node
      https://git.kernel.org/sunxi/linux/c/4fc5086a3d99

Best regards,
-- 
Chen-Yu Tsai <wens@kernel.org>


^ permalink raw reply

* Re: [PATCH 1/2] crypto: Delete Qualcomm crypto engine driver
From: Krzysztof Kozlowski @ 2026-05-24 20:24 UTC (permalink / raw)
  To: demiobenour, Herbert Xu, David S. Miller, Thara Gopinath,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Russell King
  Cc: linux-kernel, linux-crypto, linux-arm-msm, Eric Biggers,
	Ard Biesheuvel, devicetree, linux-arm-kernel
In-Reply-To: <20260523-delete-qce-v1-1-86105cd7f406@gmail.com>

On 23/05/2026 21:03, Demi Marie Obenour via B4 Relay wrote:
> From: Demi Marie Obenour <demiobenour@gmail.com>
> 
> It's slower than the generic C code and causes problems.

That's really vague and incomplete. You need to make your case, provide
arguments, numbers, data. Otherwise it is just trolling.

Best regards,
Krzysztof

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox