1 // SPDX-License-Identifier: GPL-2.0+
3 * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
5 * Copyright (C) 2015, 2018
6 * Author: Matt Ranostay <matt.ranostay@konsulko.com>
9 #include <linux/module.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/mutex.h>
12 #include <linux/init.h>
13 #include <linux/i2c.h>
14 #include <linux/iio/iio.h>
16 #define AMS_IAQCORE_DATA_SIZE 9
18 #define AMS_IAQCORE_VOC_CO2_IDX 0
19 #define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
20 #define AMS_IAQCORE_VOC_TVOC_IDX 2
22 struct ams_iaqcore_reading
{
27 } __attribute__((__packed__
));
29 struct ams_iaqcore_data
{
30 struct i2c_client
*client
;
32 unsigned long last_update
;
34 struct ams_iaqcore_reading buffer
;
37 static const struct iio_chan_spec ams_iaqcore_channels
[] = {
39 .type
= IIO_CONCENTRATION
,
40 .channel2
= IIO_MOD_CO2
,
42 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
43 .address
= AMS_IAQCORE_VOC_CO2_IDX
,
46 .type
= IIO_RESISTANCE
,
47 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
48 .address
= AMS_IAQCORE_VOC_RESISTANCE_IDX
,
51 .type
= IIO_CONCENTRATION
,
52 .channel2
= IIO_MOD_VOC
,
54 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
55 .address
= AMS_IAQCORE_VOC_TVOC_IDX
,
59 static int ams_iaqcore_read_measurement(struct ams_iaqcore_data
*data
)
61 struct i2c_client
*client
= data
->client
;
64 struct i2c_msg msg
= {
66 .flags
= client
->flags
| I2C_M_RD
,
67 .len
= AMS_IAQCORE_DATA_SIZE
,
68 .buf
= (char *) &data
->buffer
,
71 ret
= i2c_transfer(client
->adapter
, &msg
, 1);
73 return (ret
== AMS_IAQCORE_DATA_SIZE
) ? 0 : ret
;
76 static int ams_iaqcore_get_measurement(struct ams_iaqcore_data
*data
)
80 /* sensor can only be polled once a second max per datasheet */
81 if (!time_after(jiffies
, data
->last_update
+ HZ
))
84 ret
= ams_iaqcore_read_measurement(data
);
88 data
->last_update
= jiffies
;
93 static int ams_iaqcore_read_raw(struct iio_dev
*indio_dev
,
94 struct iio_chan_spec
const *chan
, int *val
,
97 struct ams_iaqcore_data
*data
= iio_priv(indio_dev
);
100 if (mask
!= IIO_CHAN_INFO_PROCESSED
)
103 mutex_lock(&data
->lock
);
104 ret
= ams_iaqcore_get_measurement(data
);
109 switch (chan
->address
) {
110 case AMS_IAQCORE_VOC_CO2_IDX
:
112 *val2
= be16_to_cpu(data
->buffer
.co2_ppm
);
113 ret
= IIO_VAL_INT_PLUS_MICRO
;
115 case AMS_IAQCORE_VOC_RESISTANCE_IDX
:
116 *val
= be32_to_cpu(data
->buffer
.resistance
);
119 case AMS_IAQCORE_VOC_TVOC_IDX
:
121 *val2
= be16_to_cpu(data
->buffer
.voc_ppb
);
122 ret
= IIO_VAL_INT_PLUS_NANO
;
129 mutex_unlock(&data
->lock
);
134 static const struct iio_info ams_iaqcore_info
= {
135 .read_raw
= ams_iaqcore_read_raw
,
138 static int ams_iaqcore_probe(struct i2c_client
*client
,
139 const struct i2c_device_id
*id
)
141 struct iio_dev
*indio_dev
;
142 struct ams_iaqcore_data
*data
;
144 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*data
));
148 data
= iio_priv(indio_dev
);
149 i2c_set_clientdata(client
, indio_dev
);
150 data
->client
= client
;
152 /* so initial reading will complete */
153 data
->last_update
= jiffies
- HZ
;
154 mutex_init(&data
->lock
);
156 indio_dev
->info
= &ams_iaqcore_info
;
157 indio_dev
->name
= dev_name(&client
->dev
);
158 indio_dev
->modes
= INDIO_DIRECT_MODE
;
160 indio_dev
->channels
= ams_iaqcore_channels
;
161 indio_dev
->num_channels
= ARRAY_SIZE(ams_iaqcore_channels
);
163 return devm_iio_device_register(&client
->dev
, indio_dev
);
166 static const struct i2c_device_id ams_iaqcore_id
[] = {
167 { "ams-iaq-core", 0 },
170 MODULE_DEVICE_TABLE(i2c
, ams_iaqcore_id
);
172 static const struct of_device_id ams_iaqcore_dt_ids
[] = {
173 { .compatible
= "ams,iaq-core" },
176 MODULE_DEVICE_TABLE(of
, ams_iaqcore_dt_ids
);
178 static struct i2c_driver ams_iaqcore_driver
= {
180 .name
= "ams-iaq-core",
181 .of_match_table
= ams_iaqcore_dt_ids
,
183 .probe
= ams_iaqcore_probe
,
184 .id_table
= ams_iaqcore_id
,
186 module_i2c_driver(ams_iaqcore_driver
);
188 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
189 MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
190 MODULE_LICENSE("GPL v2");