* [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
@ 2026-11-04 15:11 Nikhil Gautam
2026-11-04 21:51 ` Andy Shevchenko
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Nikhil Gautam @ 2026-11-04 15:11 UTC (permalink / raw)
To: linux-iio; +Cc: linux-kernel, jic23, dlechner, nuno.sa, andy, Nikhil Gautam
Allow userspace to configure the CCS811 IAQ measurement rate through
the IIO sampling_frequency attribute.
Support the three IAQ measurement modes with sampling frequencies of
1 Hz, 0.1 Hz, and approximately 0.016667 Hz, corresponding to
measurement intervals of 1, 10, and 60 seconds.
Preserve unrelated bits in the MEAS_MODE register when updating the
drive mode, and cache the selected mode after a successful register
write. Expose the supported frequencies through
sampling_frequency_available.
Signed-off-by: Nikhil Gautam <nikhilgtr@gmail.com>
---
Changes in v5:
- Add an index enum shared by the sampling frequency and drive mode tables.
- Make loop counters unsigned and use reverse xmas tree declaration order.
- Use a local i2c_client pointer in ccs811_update_sampling_freq().
- Drop the redundant ret variable in ccs811_write_raw().
Changes in v4:
- Move the changelog below the cutter line.
- Use a two-dimensional sampling frequency table with a matching
drive mode table, and look entries up with simple loops.
- Use IIO_DEV_ACQUIRE_DIRECT_MODE() in write_raw().
Changes in v3:
- Reuse the sampling frequency array instead of duplicating frequency values.
- Separate register updates from locking and wakeup handling.
- Use a separate variable for the updated MEAS_MODE register value.
- Simplify the sampling frequency getter.
- Mark the completed drive-mode configuration TODO as done.
Changes in v2:
- Fix the Clang build error caused by guard() in the switch statement.
drivers/iio/chemical/ccs811.c | 154 ++++++++++++++++++++++++++++++++--
1 file changed, 148 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c
index d126ee12ffe5..6d675534c5b6 100644
--- a/drivers/iio/chemical/ccs811.c
+++ b/drivers/iio/chemical/ccs811.c
@@ -9,7 +9,7 @@
* IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High)
*
* TODO:
- * 1. Make the drive mode selectable form userspace
+ * 1. DONE. Was: "Make the drive mode selectable form userspace"
* 2. Add support for interrupts
* 3. Adjust time to wait for data to be ready based on selected operation mode
* 4. Read error register and put the information in logs
@@ -54,6 +54,7 @@
#define CCS811_STATUS_FW_MODE_APPLICATION BIT(7)
/* Measurement modes */
+#define CCS811_MODE_MASK GENMASK(6, 4)
#define CCS811_MODE_IDLE 0x00
#define CCS811_MODE_IAQ_1SEC 0x10
#define CCS811_MODE_IAQ_10SEC 0x20
@@ -64,6 +65,24 @@
#define CCS811_VOLTAGE_MASK 0x3FF
+enum {
+ CCS811_MODE_IAQ_1SEC_IDX,
+ CCS811_MODE_IAQ_10SEC_IDX,
+ CCS811_MODE_IAQ_60SEC_IDX,
+};
+
+static const int ccs811_samp_freq[][2] = {
+ [CCS811_MODE_IAQ_1SEC_IDX] = { 1, 0 },
+ [CCS811_MODE_IAQ_10SEC_IDX] = { 0, 100000 },
+ [CCS811_MODE_IAQ_60SEC_IDX] = { 0, 16667 },
+};
+
+static const u8 ccs811_drive_mode[] = {
+ [CCS811_MODE_IAQ_1SEC_IDX] = CCS811_MODE_IAQ_1SEC,
+ [CCS811_MODE_IAQ_10SEC_IDX] = CCS811_MODE_IAQ_10SEC,
+ [CCS811_MODE_IAQ_60SEC_IDX] = CCS811_MODE_IAQ_60SEC,
+};
+
struct ccs811_reading {
__be16 co2;
__be16 voc;
@@ -79,6 +98,7 @@ struct ccs811_data {
struct iio_trigger *drdy_trig;
struct gpio_desc *wakeup_gpio;
bool drdy_trig_on;
+ u8 drive_mode;
/* Ensures correct alignment of timestamp if present */
struct {
s16 channels[2];
@@ -103,6 +123,8 @@ static const struct iio_chan_spec ccs811_channels[] = {
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 0,
.scan_type = {
.sign = 'u',
@@ -163,16 +185,21 @@ static int ccs811_start_sensor_application(struct i2c_client *client)
return 0;
}
-static int ccs811_setup(struct i2c_client *client)
+static int ccs811_setup(struct ccs811_data *data)
{
int ret;
- ret = ccs811_start_sensor_application(client);
+ ret = ccs811_start_sensor_application(data->client);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, CCS811_MEAS_MODE,
+ CCS811_MODE_IAQ_1SEC);
if (ret < 0)
return ret;
- return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,
- CCS811_MODE_IAQ_1SEC);
+ data->drive_mode = CCS811_MODE_IAQ_1SEC;
+ return 0;
}
static void ccs811_set_wakeup(struct ccs811_data *data, bool enable)
@@ -188,6 +215,55 @@ static void ccs811_set_wakeup(struct ccs811_data *data, bool enable)
usleep_range(20, 30);
}
+static int ccs811_update_sampling_freq(struct ccs811_data *data,
+ int val, int val2)
+{
+ struct i2c_client *client = data->client;
+ unsigned int i;
+ u8 drive_mode;
+ u8 mode;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(ccs811_samp_freq); i++) {
+ if (val == ccs811_samp_freq[i][0] &&
+ val2 == ccs811_samp_freq[i][1])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ccs811_samp_freq))
+ return -EINVAL;
+
+ drive_mode = ccs811_drive_mode[i];
+
+ ret = i2c_smbus_read_byte_data(client, CCS811_MEAS_MODE);
+ if (ret < 0)
+ return ret;
+
+ mode = (ret & ~CCS811_MODE_MASK) | drive_mode;
+
+ ret = i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, mode);
+ if (ret < 0)
+ return ret;
+
+ data->drive_mode = drive_mode;
+
+ return 0;
+}
+
+static int ccs811_set_sampling_freq(struct ccs811_data *data,
+ int val, int val2)
+{
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ccs811_set_wakeup(data, true);
+ ret = ccs811_update_sampling_freq(data, val, val2);
+ ccs811_set_wakeup(data, false);
+
+ return ret;
+}
+
static int ccs811_get_measurement(struct ccs811_data *data)
{
int ret, tries = 11;
@@ -215,6 +291,24 @@ static int ccs811_get_measurement(struct ccs811_data *data)
return ret;
}
+static int ccs811_get_sampling_freq(struct ccs811_data *data,
+ int *val, int *val2)
+{
+ guard(mutex)(&data->lock);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(ccs811_drive_mode); i++) {
+ if (ccs811_drive_mode[i] != data->drive_mode)
+ continue;
+
+ *val = ccs811_samp_freq[i][0];
+ *val2 = ccs811_samp_freq[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
static int ccs811_read_info_raw(struct ccs811_data *data,
struct iio_chan_spec const *chan,
int *val, int mask)
@@ -249,6 +343,24 @@ static int ccs811_read_info_raw(struct ccs811_data *data,
}
}
+static int ccs811_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ccs811_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ return ccs811_set_sampling_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
static int ccs811_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -293,13 +405,43 @@ static int ccs811_read_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ccs811_get_sampling_freq(data, val, val2);
default:
return -EINVAL;
}
}
+static int ccs811_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ *vals = (const int *)ccs811_samp_freq;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(ccs811_samp_freq) * 2;
+
+ return IIO_AVAIL_LIST;
+}
+
+static int ccs811_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (mask == IIO_CHAN_INFO_SAMP_FREQ)
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ return -EINVAL;
+}
+
static const struct iio_info ccs811_info = {
.read_raw = ccs811_read_raw,
+ .write_raw = ccs811_write_raw,
+ .read_avail = ccs811_read_avail,
+ .write_raw_get_fmt = ccs811_write_raw_get_fmt,
};
static int ccs811_set_trigger_state(struct iio_trigger *trig,
@@ -460,7 +602,7 @@ static int ccs811_probe(struct i2c_client *client)
return -ENODEV;
}
- ret = ccs811_setup(client);
+ ret = ccs811_setup(data);
if (ret < 0) {
ccs811_set_wakeup(data, false);
return ret;
--
2.39.5
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
2026-11-04 15:11 [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable Nikhil Gautam
@ 2026-11-04 21:51 ` Andy Shevchenko
2026-11-05 18:23 ` kernel test robot
2026-11-05 21:08 ` kernel test robot
2 siblings, 0 replies; 4+ messages in thread
From: Andy Shevchenko @ 2026-11-04 21:51 UTC (permalink / raw)
To: Nikhil Gautam; +Cc: linux-iio, linux-kernel, jic23, dlechner, nuno.sa, andy
On Wed, Nov 04, 2026 at 08:41:38PM +0530, Nikhil Gautam wrote:
> Allow userspace to configure the CCS811 IAQ measurement rate through
> the IIO sampling_frequency attribute.
>
> Support the three IAQ measurement modes with sampling frequencies of
> 1 Hz, 0.1 Hz, and approximately 0.016667 Hz, corresponding to
> measurement intervals of 1, 10, and 60 seconds.
>
> Preserve unrelated bits in the MEAS_MODE register when updating the
> drive mode, and cache the selected mode after a successful register
> write. Expose the supported frequencies through
> sampling_frequency_available.
LGTM now,
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
2026-11-04 15:11 [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable Nikhil Gautam
2026-11-04 21:51 ` Andy Shevchenko
@ 2026-11-05 18:23 ` kernel test robot
2026-11-05 21:08 ` kernel test robot
2 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2026-11-05 18:23 UTC (permalink / raw)
To: Nikhil Gautam, linux-iio
Cc: oe-kbuild-all, linux-kernel, jic23, dlechner, nuno.sa, andy,
Nikhil Gautam
Hi Nikhil,
kernel test robot noticed the following build errors:
[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on linus/master v7.3 next-20261105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Nikhil-Gautam/iio-chemical-ccs811-Make-sampling-frequency-configurable/20261104-204138
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/20261104151138.9968-1-nikhilgtr%40gmail.com
patch subject: [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
config: arc-randconfig-1000-20261105 (https://download.01.org/0day-ci/archive/20261106/202611060115.RlOXqnls-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261106/202611060115.RlOXqnls-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611060115.RlOXqnls-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from drivers/iio/chemical/ccs811.c:18:
drivers/iio/chemical/ccs811.c: In function 'ccs811_write_raw':
>> include/linux/cleanup.h:298:2: error: a label can only be part of a statement and a declaration is not a statement
class_##_name##_t var __cleanup(class_##_name##_destructor) = \
^~~~~~
include/linux/cleanup.h:422:34: note: in expansion of macro 'CLASS'
#define ACQUIRE(_name, _var) CLASS(_name, _var)
^~~~~
include/linux/iio/iio.h:817:2: note: in expansion of macro 'ACQUIRE'
ACQUIRE(__priv__iio_dev_mode_lock_try_direct, claim)(dev)
^~~~~~~
drivers/iio/chemical/ccs811.c:354:3: note: in expansion of macro 'IIO_DEV_ACQUIRE_DIRECT_MODE'
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
vim +298 include/linux/cleanup.h
54da6a0924311c7 Peter Zijlstra 2023-05-26 293
2deccd5c862a033 Peter Zijlstra 2026-03-09 294 #define EXTEND_CLASS(_name, ext, _init, _init_args...) \
2deccd5c862a033 Peter Zijlstra 2026-03-09 295 EXTEND_CLASS_COND(_name, ext, 0, _init, _init_args)
2deccd5c862a033 Peter Zijlstra 2026-03-09 296
54da6a0924311c7 Peter Zijlstra 2023-05-26 297 #define CLASS(_name, var) \
54da6a0924311c7 Peter Zijlstra 2023-05-26 @298 class_##_name##_t var __cleanup(class_##_name##_destructor) = \
54da6a0924311c7 Peter Zijlstra 2023-05-26 299 class_##_name##_constructor
54da6a0924311c7 Peter Zijlstra 2023-05-26 300
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
2026-11-04 15:11 [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable Nikhil Gautam
2026-11-04 21:51 ` Andy Shevchenko
2026-11-05 18:23 ` kernel test robot
@ 2026-11-05 21:08 ` kernel test robot
2 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2026-11-05 21:08 UTC (permalink / raw)
To: Nikhil Gautam, linux-iio
Cc: llvm, oe-kbuild-all, linux-kernel, jic23, dlechner, nuno.sa,
andy, Nikhil Gautam
Hi Nikhil,
kernel test robot noticed the following build errors:
[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on linus/master v7.4-rc1 next-20261105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Nikhil-Gautam/iio-chemical-ccs811-Make-sampling-frequency-configurable/20261104-204138
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/20261104151138.9968-1-nikhilgtr%40gmail.com
patch subject: [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable
config: x86_64-randconfig-1004-20261105 (https://download.01.org/0day-ci/archive/20261106/202611060402.20veo4W8-lkp@intel.com/config)
compiler: clang version 22.1.8 (https://github.com/llvm/llvm-project ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261106/202611060402.20veo4W8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611060402.20veo4W8-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/iio/chemical/ccs811.c:359:2: error: cannot jump from switch statement to this case label
359 | default:
| ^
drivers/iio/chemical/ccs811.c:354:42: note: jump bypasses initialization of variable with __attribute__((cleanup))
354 | IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
| ^
1 error generated.
vim +359 drivers/iio/chemical/ccs811.c
345
346 static int ccs811_write_raw(struct iio_dev *indio_dev,
347 struct iio_chan_spec const *chan,
348 int val, int val2, long mask)
349 {
350 struct ccs811_data *data = iio_priv(indio_dev);
351
352 switch (mask) {
353 case IIO_CHAN_INFO_SAMP_FREQ:
354 IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
355 if (IIO_DEV_ACQUIRE_FAILED(claim))
356 return -EBUSY;
357
358 return ccs811_set_sampling_freq(data, val, val2);
> 359 default:
360 return -EINVAL;
361 }
362 }
363
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-05 21:09 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-04 15:11 [PATCH v5] iio: chemical: ccs811: Make sampling frequency configurable Nikhil Gautam
2026-11-04 21:51 ` Andy Shevchenko
2026-11-05 18:23 ` kernel test robot
2026-11-05 21:08 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®