Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: chemical: Add driver for SEN0322

Add support for the DFRobot SEN0322 oxygen sensor.

To instantiate (assuming device is connected to I2C-2):
echo 'sen0322 0x73' > /sys/class/i2c-dev/i2c-2/device/new_device

To get the oxygen concentration (assuming device is iio:device0) multiply
the values read from:
/sys/bus/iio/devices/iio:device0/in_concentration_raw
/sys/bus/iio/devices/iio:device0/in_concentration_scale

Datasheet: https://wiki.dfrobot.com/Gravity_I2C_Oxygen_Sensor_SKU_SEN0322
Signed-off-by: Tóth János <gomba007@gmail.com>
Link: https://patch.msgid.link/20250506-iio-chemical-sen0322-v4-2-1465ac8dc190@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Tóth János and committed by
Jonathan Cameron
d524b3e0 844ca960

+178
+6
MAINTAINERS
··· 6863 6863 S: Maintained 6864 6864 F: drivers/rtc/rtc-sd2405al.c 6865 6865 6866 + DFROBOT SEN0322 DRIVER 6867 + M: Tóth János <gomba007@gmail.com> 6868 + L: linux-iio@vger.kernel.org 6869 + S: Maintained 6870 + F: drivers/iio/chemical/sen0322.c 6871 + 6866 6872 DH ELECTRONICS DHSOM SOM AND BOARD SUPPORT 6867 6873 M: Christoph Niedermaier <cniedermaier@dh-electronics.com> 6868 6874 M: Marek Vasut <marex@denx.de>
+10
drivers/iio/chemical/Kconfig
··· 176 176 To compile this driver as a module, choose M here: the module will 177 177 be called scd4x. 178 178 179 + config SEN0322 180 + tristate "SEN0322 oxygen sensor" 181 + depends on I2C 182 + select REGMAP_I2C 183 + help 184 + Say Y here to build support for the DFRobot SEN0322 oxygen sensor. 185 + 186 + To compile this driver as a module, choose M here: the module will 187 + be called sen0322. 188 + 179 189 config SENSIRION_SGP30 180 190 tristate "Sensirion SGPxx gas sensors" 181 191 depends on I2C
+1
drivers/iio/chemical/Makefile
··· 21 21 obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o 22 22 obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o 23 23 obj-$(CONFIG_SCD4X) += scd4x.o 24 + obj-$(CONFIG_SEN0322) += sen0322.o 24 25 obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o 25 26 obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o 26 27 obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o
+161
drivers/iio/chemical/sen0322.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Driver for the DFRobot SEN0322 oxygen sensor. 4 + * 5 + * Datasheet: 6 + * https://wiki.dfrobot.com/Gravity_I2C_Oxygen_Sensor_SKU_SEN0322 7 + * 8 + * Possible I2C slave addresses: 9 + * 0x70 10 + * 0x71 11 + * 0x72 12 + * 0x73 13 + * 14 + * Copyright (C) 2025 Tóth János <gomba007@gmail.com> 15 + */ 16 + 17 + #include <linux/i2c.h> 18 + #include <linux/regmap.h> 19 + 20 + #include <linux/iio/iio.h> 21 + 22 + #define SEN0322_REG_DATA 0x03 23 + #define SEN0322_REG_COEFF 0x0A 24 + 25 + struct sen0322 { 26 + struct regmap *regmap; 27 + }; 28 + 29 + static int sen0322_read_data(struct sen0322 *sen0322) 30 + { 31 + u8 data[3] = { }; 32 + int ret; 33 + 34 + ret = regmap_bulk_read(sen0322->regmap, SEN0322_REG_DATA, data, 35 + sizeof(data)); 36 + if (ret < 0) 37 + return ret; 38 + 39 + /* 40 + * The actual value in the registers is: 41 + * val = data[0] + data[1] / 10 + data[2] / 100 42 + * but it is multiplied by 100 here to avoid floating-point math 43 + * and the scale is divided by 100 to compensate this. 44 + */ 45 + return data[0] * 100 + data[1] * 10 + data[2]; 46 + } 47 + 48 + static int sen0322_read_scale(struct sen0322 *sen0322, int *num, int *den) 49 + { 50 + u32 val; 51 + int ret; 52 + 53 + ret = regmap_read(sen0322->regmap, SEN0322_REG_COEFF, &val); 54 + if (ret < 0) 55 + return ret; 56 + 57 + if (val) { 58 + *num = val; 59 + *den = 100000; /* Coeff is scaled by 1000 at calibration. */ 60 + } else { /* The device is not calibrated, using the factory-defaults. */ 61 + *num = 209; /* Oxygen content in the atmosphere is 20.9%. */ 62 + *den = 120000; /* Output of the sensor at 20.9% is 120 uA. */ 63 + } 64 + 65 + dev_dbg(regmap_get_device(sen0322->regmap), "scale: %d/%d\n", 66 + *num, *den); 67 + 68 + return 0; 69 + } 70 + 71 + static int sen0322_read_raw(struct iio_dev *iio_dev, 72 + const struct iio_chan_spec *chan, 73 + int *val, int *val2, long mask) 74 + { 75 + struct sen0322 *sen0322 = iio_priv(iio_dev); 76 + int ret; 77 + 78 + if (chan->type != IIO_CONCENTRATION) 79 + return -EINVAL; 80 + 81 + switch (mask) { 82 + case IIO_CHAN_INFO_RAW: 83 + ret = sen0322_read_data(sen0322); 84 + if (ret < 0) 85 + return ret; 86 + 87 + *val = ret; 88 + return IIO_VAL_INT; 89 + 90 + case IIO_CHAN_INFO_SCALE: 91 + ret = sen0322_read_scale(sen0322, val, val2); 92 + if (ret < 0) 93 + return ret; 94 + 95 + return IIO_VAL_FRACTIONAL; 96 + 97 + default: 98 + return -EINVAL; 99 + } 100 + } 101 + 102 + static const struct iio_info sen0322_info = { 103 + .read_raw = sen0322_read_raw, 104 + }; 105 + 106 + static const struct regmap_config sen0322_regmap_conf = { 107 + .reg_bits = 8, 108 + .val_bits = 8, 109 + }; 110 + 111 + static const struct iio_chan_spec sen0322_channel = { 112 + .type = IIO_CONCENTRATION, 113 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 114 + BIT(IIO_CHAN_INFO_SCALE), 115 + }; 116 + 117 + static int sen0322_probe(struct i2c_client *client) 118 + { 119 + struct sen0322 *sen0322; 120 + struct iio_dev *iio_dev; 121 + 122 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 123 + return -ENODEV; 124 + 125 + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sen0322)); 126 + if (!iio_dev) 127 + return -ENOMEM; 128 + 129 + sen0322 = iio_priv(iio_dev); 130 + 131 + sen0322->regmap = devm_regmap_init_i2c(client, &sen0322_regmap_conf); 132 + if (IS_ERR(sen0322->regmap)) 133 + return PTR_ERR(sen0322->regmap); 134 + 135 + iio_dev->info = &sen0322_info; 136 + iio_dev->name = "sen0322"; 137 + iio_dev->channels = &sen0322_channel; 138 + iio_dev->num_channels = 1; 139 + iio_dev->modes = INDIO_DIRECT_MODE; 140 + 141 + return devm_iio_device_register(&client->dev, iio_dev); 142 + } 143 + 144 + static const struct of_device_id sen0322_of_match[] = { 145 + { .compatible = "dfrobot,sen0322" }, 146 + { } 147 + }; 148 + MODULE_DEVICE_TABLE(of, sen0322_of_match); 149 + 150 + static struct i2c_driver sen0322_driver = { 151 + .driver = { 152 + .name = "sen0322", 153 + .of_match_table = sen0322_of_match, 154 + }, 155 + .probe = sen0322_probe, 156 + }; 157 + module_i2c_driver(sen0322_driver); 158 + 159 + MODULE_AUTHOR("Tóth János <gomba007@gmail.com>"); 160 + MODULE_LICENSE("GPL"); 161 + MODULE_DESCRIPTION("SEN0322 oxygen sensor driver");