* [PATCH v2 0/2] iio: chemical: add support for SGP41 sensor
@ 2026-11-15 5:59 Akshat Chandra
2026-11-15 5:59 ` [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding Akshat Chandra
2026-11-15 5:59 ` [PATCH v2 2/2] iio: chemical: Add support for Sensirion SGP41 Akshat Chandra
0 siblings, 2 replies; 4+ messages in thread
From: Akshat Chandra @ 2026-11-15 5:59 UTC (permalink / raw)
To: linux-iio; +Cc: jic23, andy, Lukasz Kulga
The SGP41 is a gas sensor used for air quality monitoring by sensing
VOC and NOx.
This driver follows the approach of exposing raw resistance values to userspace, where the gas-index algorithm can
be applied.
Temperature and relative humidity compensation are supported and can
be configured through the driver's IIO attributes.
The SGP41 is similar to the existing SGP40, but requires its own driver
for the following reasons:
- It has a dedicated conditioning command that must be performed before
raw measurements.
- A measurement returns a 6-byte response containing two raw tick
values, each with its own CRC, rather than the SGP40's single value.
- It supports both VOC and NOx measurements and requires the heater to
be powered off after use.
The series consists of two patches:
1/2: dt-bindings: iio: chemical: Add Sensirion SGP41 binding
Add a dedicated Device Tree binding, including the VDD supply.
2/2: iio: chemical: Add support for Sensirion SGP41
Add the IIO driver for the sensor.
Changes in v2:
- Split the binding and driver into separate patches.
- Add a dedicated Device Tree binding documenting the VDD supply.
- Update the driver and patch descriptions following review feedback.
- Fixed some issues in driver.
v1: https://patchew.org/linux/20260831151235.64076-1-notmissinge%40gmail.com/
Signed-off-by: Akshat Chandra <notmissinge@gmail.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding
2026-11-15 5:59 [PATCH v2 0/2] iio: chemical: add support for SGP41 sensor Akshat Chandra
@ 2026-11-15 5:59 ` Akshat Chandra
2026-11-15 6:14 ` sashiko-bot
2026-11-15 5:59 ` [PATCH v2 2/2] iio: chemical: Add support for Sensirion SGP41 Akshat Chandra
1 sibling, 1 reply; 4+ messages in thread
From: Akshat Chandra @ 2026-11-15 5:59 UTC (permalink / raw)
To: linux-iio
Cc: jic23, andy, Lukasz Kulga, David Lechner, Nuno Sá,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-kernel
Add a Device Tree binding for the Sensirion SGP41 VOC and NOx gas
sensor, including its VDD supply.
Signed-off-by: Akshat Chandra <notmissinge@gmail.com>
---
.../iio/chemical/sensirion,sgp41.yaml | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml
diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml
new file mode 100644
index 000000000000..28528a695cf3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/chemical/sensirion,sgp41.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sensirion SGP41 VOC and NOx gas sensor
+
+maintainers:
+ - Akshat Chandra <notmissinge@gmail.com>
+
+description: |
+ Sensirion SGP41 indoor air quality sensor measuring raw VOC and NOx
+ signals over I2C.
+
+properties:
+ compatible:
+ const: sensirion,sgp41
+
+ reg:
+ const: 0x59
+
+ vdd-supply: true
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ gas-sensor@59 {
+ compatible = "sensirion,sgp41";
+ reg = <0x59>;
+ vdd-supply = <&vdd>;
+ };
+ };
+...
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] iio: chemical: Add support for Sensirion SGP41
2026-11-15 5:59 [PATCH v2 0/2] iio: chemical: add support for SGP41 sensor Akshat Chandra
2026-11-15 5:59 ` [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding Akshat Chandra
@ 2026-11-15 5:59 ` Akshat Chandra
1 sibling, 0 replies; 4+ messages in thread
From: Akshat Chandra @ 2026-11-15 5:59 UTC (permalink / raw)
To: linux-iio
Cc: jic23, andy, Lukasz Kulga, David Lechner, Nuno Sá,
linux-kernel
Add a driver for the Sensirion SGP41 VOC and NOx gas sensor,
connected over I2C.
Expose raw VOC and NOx signals as resistance channels and
allow userspace to write temperature and relative humidity
compensation values.
Run sensor conditioning lazily before the first raw measurement.
Calculate an estimated VOC index and leave NOx-index calculation
to userspace.
Signed-off-by: Akshat Chandra <notmissinge@gmail.com>
---
drivers/iio/chemical/Kconfig | 11 +
drivers/iio/chemical/Makefile | 1 +
drivers/iio/chemical/sgp41.c | 542 ++++++++++++++++++++++++++++++++++
3 files changed, 554 insertions(+)
create mode 100644 drivers/iio/chemical/sgp41.c
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index b22afa1f6d59..cbb6a51b1cfd 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -210,6 +210,17 @@ config SENSIRION_SGP40
To compile this driver as module, choose M here: the
module will be called sgp40.
+config SENSIRION_SGP41
+ tristate "Sensirion SGP41 gas sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say Y here to build I2C interface to support Sensirion SGP41 gas
+ sensor
+
+ To compile this driver as module, choose M here: the
+ module will be called sgp41.
+
config SPS30
tristate
select IIO_BUFFER
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 2287a00a6b75..90041aaa41fe 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SEN0322) += sen0322.o
obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o
+obj-$(CONFIG_SENSIRION_SGP41) += sgp41.o
obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_SPS30_I2C) += sps30_i2c.o
obj-$(CONFIG_SPS30_SERIAL) += sps30_serial.o
diff --git a/drivers/iio/chemical/sgp41.c b/drivers/iio/chemical/sgp41.c
new file mode 100644
index 000000000000..f094ba22b7aa
--- /dev/null
+++ b/drivers/iio/chemical/sgp41.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sgp41 - Support for Sensirion SGP41 Gas Sensor
+ *
+ * Copyright (C) 2026 Akshat Chandra <notmissinge@gmail.com>
+ *
+ * I2C slave address: 0x59
+ *
+ * Datasheet can be found here:
+ * https://www.sensirion.com/file/datasheet_sgp41
+ *
+ * There are two functionalities supported:
+ *
+ * 1) read raw logarithmic resistance value from sensor
+ * --> useful to pass it to the algorithm of the sensor vendor for
+ * measuring deteriorations and improvements of air quality.
+ * The values can be read from the in_resistance_voc_raw and
+ * in_resistance_nox_raw.
+ *
+ * 2) calculate an estimated VOC index (0 - 500) and expose it through
+ * the in_concentration_voc_input attribute.
+ *
+ * The VOC index is calculated using the raw VOC resistance and a
+ * configurable calibration bias, exposed through
+ * in_resistance_voc_calibbias (default 30000).
+ *
+ * The driver does not calculate a NOx index.
+ *
+ * Conditioning runs before the first raw measurement, once per second
+ * for a total of 10 seconds.
+ *
+ * Compensation values for relative humidity and temperature can be
+ * configured through the corresponding output attributes.
+ *
+ * The sensor heater is turned off when the device is removed.
+ */
+
+#include<linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#define SGP41_CALC_POWER 14
+#define SGP41_CRC8_POLYNOMIAL 0x31
+#define SGP41_CRC8_INIT 0xff
+
+#define SGP41_COND_ITERATIONS 10
+#define SRAW_VOC 1
+#define SRAW_NOX 2
+
+DECLARE_CRC8_TABLE(sgp41_crc8_table);
+
+struct sgp41_data {
+ struct device *dev;
+ struct i2c_client *client;
+
+ int rht;
+ int temp;
+
+ int voc_calibbias;
+
+ struct mutex lock; /* Protects sensor state and I2C transaction.*/
+ bool conditioned;
+ u8 condition_count; /*Counter for conditioning_cmd sent.*/
+};
+
+struct sgp41_tg_measure {
+ u8 command[2];
+ __be16 rht_ticks;
+ u8 rht_crc;
+ __be16 temp_ticks;
+ u8 temp_crc;
+} __packed;
+
+struct sgp41_tg_result {
+ __be16 voc_res_ticks;
+ u8 voc_res_crc;
+ __be16 nox_res_ticks;
+ u8 nox_res_crc;
+} __packed;
+
+static const struct iio_chan_spec sgp41_channels[] = {
+ {
+ .type = IIO_RESISTANCE,/*VOC*/
+ .channel = SRAW_VOC,
+ .extend_name = "voc",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ },
+ {
+ .type = IIO_RESISTANCE,/*NOX*/
+ .channel = SRAW_NOX,
+ .extend_name = "nox",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ {
+ .type = IIO_TEMP,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+};
+
+/*
+ * taylor approximation of e^x:
+ * y = 1 + x + x^2 / 2 + x^3 / 6 + x^4 / 24 + ... + x^n / n!
+ *
+ * Because we are calculating x real value multiplied by 2^power we get
+ * an additional 2^power^n to divide for every element. For a reasonable
+ * precision this would overflow after a few iterations. Therefore we
+ * divide the x^n part whenever its about to overflow (xmax).
+ */
+
+static u32 sgp41_exp(int exp, u32 power, u32 rounds)
+{
+ u32 x, y, xp;
+ u32 factorial, divider, xmax;
+ int sign = 1;
+ u32 i;
+
+ if (exp == 0) {
+ return 1 << power;
+ } else if (exp < 0) {
+ sign = -1;
+ exp *= -1;
+ }
+
+ xmax = 0x7FFFFFFF / exp;
+ x = exp;
+ xp = 1;
+ factorial = 1;
+ y = 1 << power;
+ divider = 0;
+
+ for (i = 1; i <= rounds; i++) {
+ xp *= x;
+ factorial *= i;
+ y += (xp >> divider) / factorial;
+ divider += power;
+ /* divide when next multiplication would overflow */
+ if (xp >= xmax) {
+ xp >>= power;
+ divider -= power;
+ }
+ }
+
+ if (sign == -1)
+ return (1 << (power * 2)) / y;
+ else
+ return y;
+}
+
+static void temp_rht_payload(struct sgp41_data *data,
+ struct sgp41_tg_measure *tg)
+{
+ s64 ticks;
+ u16 ticks16;
+
+ ticks = div_s64((s64)data->rht * 65535, 100000);
+ ticks16 = (u16)clamp_t(s64, ticks, 0, 65535);
+ tg->rht_ticks = cpu_to_be16(ticks16);
+ tg->rht_crc = crc8(sgp41_crc8_table, (u8 *)&tg->rht_ticks, 2,
+ SGP41_CRC8_INIT);
+
+ ticks = div_s64((s64)(data->temp + 45000) * 65535, 175000);
+ ticks16 = (u16)clamp_t(s64, ticks, 0, 65535);
+ tg->temp_ticks = cpu_to_be16(ticks16);
+ tg->temp_crc = crc8(sgp41_crc8_table, (u8 *)&tg->temp_ticks, 2,
+ SGP41_CRC8_INIT);
+}
+
+static int sgp41_calc_voc(struct sgp41_data *data, u16 voc_raw, int *voc)
+{
+ int x;
+ u32 exp = 0;
+ int voc_calibbias;
+
+ /* we calculate as a multiple of 16384 (2^14) */
+ scoped_guard(mutex, &data->lock)
+ voc_calibbias = data->voc_calibbias;
+
+ x = ((int)voc_raw - voc_calibbias) * 106;
+ /* voc = 500 / (1 + e^x) */
+ exp = sgp41_exp(x, SGP41_CALC_POWER, 18);
+ *voc = 500 * ((1 << (SGP41_CALC_POWER * 2)) / ((1 << SGP41_CALC_POWER) + exp));
+
+ dev_dbg(data->dev, "voc_raw: %d voc_calibbias: %d x: %d exp: %u voc: %d\n",
+ voc_raw, data->voc_calibbias, x, exp, *voc);
+
+ return 0;
+}
+
+static int sgp41_run_conditioning(struct sgp41_data *data)
+{
+ struct sgp41_tg_measure tg = {
+ .command = { 0x26, 0x12 },
+ };
+ int i;
+ int ret;
+
+ for (i = 0; i < SGP41_COND_ITERATIONS; i++) {
+ if (data->condition_count!= i)
+ i = data->condition_count;
+
+ temp_rht_payload(data, &tg);
+ ret = i2c_master_send(data->client, (const char *)&tg,
+ sizeof(tg));
+ if (ret != sizeof(tg)) {
+ dev_warn(data->dev,
+ "i2c_master_send ret: %d sizeof: %zu\n",
+ ret, sizeof(tg));
+ return ret < 0 ? ret : -EIO;
+ }
+ data->condition_count++;
+ msleep(50);
+
+ if (i != SGP41_COND_ITERATIONS - 1)
+ msleep(950);
+ }
+
+ data->conditioned = true;
+
+ return 0;
+}
+
+static int sgp41_heater_off(struct sgp41_data *data)
+{
+ u8 heater_off_cmd[2] = { 0x36, 0x15 };
+ int ret;
+
+ ret = i2c_master_send(data->client, heater_off_cmd,
+ sizeof(heater_off_cmd));
+ if (ret != sizeof(heater_off_cmd)) {
+ dev_warn(data->dev,
+ "i2c_master_send ret: %d sizeof: %zu\n",
+ ret, sizeof(heater_off_cmd));
+ return ret < 0 ? ret : -EIO;
+ }
+ data-> conditioned = false;
+ data->condition_count = 0;
+ dev_dbg(data->dev, "heater turned off\n");
+
+ return 0;
+}
+
+static int sgp41_measure_raw(struct sgp41_data *data,
+ u16 *voc_raw, u16 *nox_raw)
+{
+ struct sgp41_tg_measure tg = {
+ .command = { 0x26, 0x19 },
+ };
+ struct sgp41_tg_result tgres;
+ u8 crc;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ if (!data->conditioned) {
+ ret = sgp41_run_conditioning(data);
+ if (ret) {
+ sgp41_heater_off(data);
+ return ret;
+ }
+ }
+
+ temp_rht_payload(data, &tg);
+
+ ret = i2c_master_send(data->client, (const char *)&tg, sizeof(tg));
+ if (ret != sizeof(tg)) {
+ dev_warn(data->dev,
+ "i2c_master_send ret: %d sizeof: %zu\n",
+ ret, sizeof(tg));
+ return ret < 0 ? ret : -EIO;
+ }
+
+ msleep(50);
+
+ ret = i2c_master_recv(data->client, (u8 *)&tgres, sizeof(tgres));
+ if (ret < 0)
+ return ret;
+
+ if (ret != sizeof(tgres)) {
+ dev_warn(data->dev,
+ "i2c_master_recv ret: %d sizeof: %zu\n",
+ ret, sizeof(tgres));
+ return -EIO;
+ }
+
+ crc = crc8(sgp41_crc8_table, (u8 *)&tgres.voc_res_ticks, 2,
+ SGP41_CRC8_INIT);
+ if (crc != tgres.voc_res_crc) {
+ dev_err(data->dev, "CRC error while measure-voc_raw\n");
+ return -EIO;
+ }
+
+ crc = crc8(sgp41_crc8_table, (u8 *)&tgres.nox_res_ticks, 2,
+ SGP41_CRC8_INIT);
+ if (crc != tgres.nox_res_crc) {
+ dev_err(data->dev, "CRC error while measure-nox_raw\n");
+ return -EIO;
+ }
+
+ *voc_raw = be16_to_cpu(tgres.voc_res_ticks);
+ *nox_raw = be16_to_cpu(tgres.nox_res_ticks);
+
+ return 0;
+}
+
+static int sgp41_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct sgp41_data *data = iio_priv(indio_dev);
+ u16 voc_raw;
+ u16 nox_raw;
+ int ret;
+ int voc;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_RESISTANCE: {
+ ret = sgp41_measure_raw(data, &voc_raw, &nox_raw);
+ if (ret)
+ return ret;
+
+ switch (chan->channel) {
+ case SRAW_VOC:
+ *val = voc_raw;
+ return IIO_VAL_INT;
+ case SRAW_NOX:
+ *val = nox_raw;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ }
+ case IIO_TEMP: {
+ guard(mutex)(&data->lock);
+ *val = data->temp;
+ return IIO_VAL_INT;
+ }
+ case IIO_HUMIDITYRELATIVE: {
+ guard(mutex)(&data->lock);
+ *val = data->rht;
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->channel2) {
+ case IIO_MOD_VOC:
+ ret = sgp41_measure_raw(data, &voc_raw, &nox_raw);
+ if (ret)
+ return ret;
+
+ ret = sgp41_calc_voc(data, voc_raw, &voc);
+ if (ret)
+ return ret;
+
+ *val = voc / (1 << SGP41_CALC_POWER);
+ /*
+ * calculation should fit into integer, where:
+ * voc <= (500 * 2^SGP41_CALC_POWER) = 8192000
+ * (with SGP41_CALC_POWER = 14)
+ */
+ *val2 = ((voc % (1 << SGP41_CALC_POWER)) * 244) /
+ (1 << (SGP41_CALC_POWER - 12));
+ dev_dbg(data->dev, "voc: %d val: %d.%06d\n",
+ voc, *val, *val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->channel) {
+ /*For VOC Calibration,*/
+ case SRAW_VOC: {
+ guard(mutex)(&data->lock);
+ *val = data->voc_calibbias;
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sgp41_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct sgp41_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_TEMP: {
+ if (val < -45000 || val > 130000)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+ data->temp = val;
+ return 0;
+ }
+ case IIO_HUMIDITYRELATIVE: {
+ if (val < 0 || val > 100000)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+ data->rht = val;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->channel) {
+ /*For VOC Calibration,*/
+ case SRAW_VOC: {
+ if (val < 20000 || val > 52768)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+ data->voc_calibbias = val;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static void sgp41_remove(struct i2c_client *client)
+{
+ int ret;
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct sgp41_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ guard(mutex)(&data->lock);
+ ret = sgp41_heater_off(data);
+ if (ret)
+ dev_warn(data->dev, "Failed to turn off heater: %d\n", ret);
+}
+
+static const struct iio_info sgp41_info = {
+ .read_raw = sgp41_read_raw,
+ .write_raw = sgp41_write_raw,
+};
+
+static int sgp41_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct sgp41_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ data->dev = dev;
+ data->conditioned = false;
+ data->condition_count = 0;
+
+ crc8_populate_msb(sgp41_crc8_table, SGP41_CRC8_POLYNOMIAL);
+
+ mutex_init(&data->lock);
+
+ /*default values.*/
+ data->rht = 50000;
+ data->temp = 25000;
+ data->voc_calibbias = 30000; /*resistance raw value for voc index of 250*/
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get/enable vdd supply\n");
+
+ indio_dev->info = &sgp41_info;
+
+ if (id)
+ indio_dev->name = id->name;
+ else
+ indio_dev->name = "sgp41";
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = sgp41_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sgp41_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ dev_err(dev, "failed to register iio device\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id sgp41_id[] = {
+ { .name = "sgp41" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, sgp41_id);
+
+static const struct of_device_id sgp41_dt_ids[] = {
+ { .compatible = "sensirion,sgp41" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, sgp41_dt_ids);
+
+static struct i2c_driver sgp41_driver = {
+ .driver = {
+ .name = "sgp41",
+ .of_match_table = sgp41_dt_ids,
+ },
+ .probe = sgp41_probe,
+ .id_table = sgp41_id,
+ .remove = sgp41_remove,
+};
+
+module_i2c_driver(sgp41_driver);
+
+MODULE_AUTHOR("Akshat Chandra <notmissinge@gmail.com>");
+MODULE_DESCRIPTION("Sensirion SGP41 gas sensor");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding
2026-11-15 5:59 ` [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding Akshat Chandra
@ 2026-11-15 6:14 ` sashiko-bot
0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-15 6:14 UTC (permalink / raw)
To: Akshat Chandra; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] dt-bindings: iio: chemical: sgp41: missing vddh-supply for
heater pin
- [Low] dt-bindings: iio: chemical: sgp41: 2-space example indent and
const reg
- [Low] dt-bindings: iio: chemical: sgp41: deprecated GPL-2.0 SPDX
license tag
--
commit 364f23f4f9b681d905ad97232d6cdcac6d5b17e4
Author: Akshat Chandra <notmissinge@gmail.com>
dt-bindings: iio: chemical: Add Sensirion SGP41 binding
This commit adds a Device Tree binding schema for the Sensirion SGP41 VOC
and NOx I2C gas sensor.
> diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml
> new file mode 100644
> index 0000000000000..c2bb6818f4390
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp41.yaml
> @@ -0,0 +1,43 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
[Severity: Low]
Should this use (GPL-2.0-only OR BSD-2-Clause) instead of the deprecated
GPL-2.0 identifier, per Documentation/devicetree/bindings/submitting-patches.rst?
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/chemical/sensirion,sgp41.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sensirion SGP41 VOC and NOx gas sensor
> +
> +maintainers:
> + - Akshat Chandra <notmissinge@gmail.com>
> +
> +description: |
> + Sensirion SGP41 indoor air quality sensor measuring raw VOC and NOx
> + signals over I2C.
> +
> +properties:
> + compatible:
> + const: sensirion,sgp41
> +
> + reg:
> + const: 0x59
> +
> + vdd-supply: true
> +
> +required:
> + - compatible
> + - reg
> + - vdd-supply
> +additionalProperties: false
[Severity: Medium]
Should the schema also define vddh-supply?
The SGP41 has a dedicated VDDH pin for the hotplate heater supply in
addition to the logic VDD pin. Boards that power VDD (for example at
1.8 V for I2C) and VDDH from separate regulators cannot describe
vddh-supply in Device Tree when additionalProperties is false without
failing dtbs_check.
> +
> +examples:
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + gas-sensor@59 {
> + compatible = "sensirion,sgp41";
> + reg = <0x59>;
> + vdd-supply = <&vdd>;
> + };
> + };
[Severity: Low]
The example uses 2-space indentation instead of 4 spaces per level, which
triggers an indent-unit-strict warning from scripts/dtc/dt-check-style.
Also, should reg in properties use maxItems: 1 rather than const: 0x59 to
follow standard IIO binding conventions and allow I2C address translation?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261115055941.362185-1-notmissinge@gmail.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-15 6:14 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-15 5:59 [PATCH v2 0/2] iio: chemical: add support for SGP41 sensor Akshat Chandra
2026-11-15 5:59 ` [PATCH v2 1/2] dt-bindings: iio: chemical: Add Sensirion SGP41 binding Akshat Chandra
2026-11-15 6:14 ` sashiko-bot
2026-11-15 5:59 ` [PATCH v2 2/2] iio: chemical: Add support for Sensirion SGP41 Akshat Chandra
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.