2 * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
4 * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 #include <linux/module.h>
19 #include <linux/mutex.h>
20 #include <linux/init.h>
21 #include <linux/i2c.h>
22 #include <linux/iio/iio.h>
24 #define AMS_IAQCORE_DATA_SIZE 9
26 #define AMS_IAQCORE_VOC_CO2_IDX 0
27 #define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
28 #define AMS_IAQCORE_VOC_TVOC_IDX 2
30 struct ams_iaqcore_reading
{
35 } __attribute__((__packed__
));
37 struct ams_iaqcore_data
{
38 struct i2c_client
*client
;
40 unsigned long last_update
;
42 struct ams_iaqcore_reading buffer
;
45 static const struct iio_chan_spec ams_iaqcore_channels
[] = {
47 .type
= IIO_CONCENTRATION
,
48 .channel2
= IIO_MOD_CO2
,
50 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
51 .address
= AMS_IAQCORE_VOC_CO2_IDX
,
54 .type
= IIO_RESISTANCE
,
55 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
56 .address
= AMS_IAQCORE_VOC_RESISTANCE_IDX
,
59 .type
= IIO_CONCENTRATION
,
60 .channel2
= IIO_MOD_VOC
,
62 .info_mask_separate
= BIT(IIO_CHAN_INFO_PROCESSED
),
63 .address
= AMS_IAQCORE_VOC_TVOC_IDX
,
67 static int ams_iaqcore_read_measurement(struct ams_iaqcore_data
*data
)
69 struct i2c_client
*client
= data
->client
;
72 struct i2c_msg msg
= {
74 .flags
= client
->flags
| I2C_M_RD
,
75 .len
= AMS_IAQCORE_DATA_SIZE
,
76 .buf
= (char *) &data
->buffer
,
79 ret
= i2c_transfer(client
->adapter
, &msg
, 1);
81 return (ret
== AMS_IAQCORE_DATA_SIZE
) ? 0 : ret
;
84 static int ams_iaqcore_get_measurement(struct ams_iaqcore_data
*data
)
88 /* sensor can only be polled once a second max per datasheet */
89 if (!time_after(jiffies
, data
->last_update
+ HZ
))
92 ret
= ams_iaqcore_read_measurement(data
);
96 data
->last_update
= jiffies
;
101 static int ams_iaqcore_read_raw(struct iio_dev
*indio_dev
,
102 struct iio_chan_spec
const *chan
, int *val
,
103 int *val2
, long mask
)
105 struct ams_iaqcore_data
*data
= iio_priv(indio_dev
);
108 if (mask
!= IIO_CHAN_INFO_PROCESSED
)
111 mutex_lock(&data
->lock
);
112 ret
= ams_iaqcore_get_measurement(data
);
117 switch (chan
->address
) {
118 case AMS_IAQCORE_VOC_CO2_IDX
:
120 *val2
= be16_to_cpu(data
->buffer
.co2_ppm
);
121 ret
= IIO_VAL_INT_PLUS_MICRO
;
123 case AMS_IAQCORE_VOC_RESISTANCE_IDX
:
124 *val
= be32_to_cpu(data
->buffer
.resistance
);
127 case AMS_IAQCORE_VOC_TVOC_IDX
:
129 *val2
= be16_to_cpu(data
->buffer
.voc_ppb
);
130 ret
= IIO_VAL_INT_PLUS_NANO
;
137 mutex_unlock(&data
->lock
);
142 static const struct iio_info ams_iaqcore_info
= {
143 .read_raw
= ams_iaqcore_read_raw
,
144 .driver_module
= THIS_MODULE
,
147 static int ams_iaqcore_probe(struct i2c_client
*client
,
148 const struct i2c_device_id
*id
)
150 struct iio_dev
*indio_dev
;
151 struct ams_iaqcore_data
*data
;
153 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*data
));
157 data
= iio_priv(indio_dev
);
158 i2c_set_clientdata(client
, indio_dev
);
159 data
->client
= client
;
161 /* so initial reading will complete */
162 data
->last_update
= jiffies
- HZ
;
163 mutex_init(&data
->lock
);
165 indio_dev
->dev
.parent
= &client
->dev
;
166 indio_dev
->info
= &ams_iaqcore_info
,
167 indio_dev
->name
= dev_name(&client
->dev
);
168 indio_dev
->modes
= INDIO_DIRECT_MODE
;
170 indio_dev
->channels
= ams_iaqcore_channels
;
171 indio_dev
->num_channels
= ARRAY_SIZE(ams_iaqcore_channels
);
173 return devm_iio_device_register(&client
->dev
, indio_dev
);
176 static const struct i2c_device_id ams_iaqcore_id
[] = {
177 { "ams-iaq-core", 0 },
180 MODULE_DEVICE_TABLE(i2c
, ams_iaqcore_id
);
182 static const struct of_device_id ams_iaqcore_dt_ids
[] = {
183 { .compatible
= "ams,iaq-core" },
186 MODULE_DEVICE_TABLE(of
, ams_iaqcore_dt_ids
);
188 static struct i2c_driver ams_iaqcore_driver
= {
190 .name
= "ams-iaq-core",
191 .of_match_table
= of_match_ptr(ams_iaqcore_dt_ids
),
193 .probe
= ams_iaqcore_probe
,
194 .id_table
= ams_iaqcore_id
,
196 module_i2c_driver(ams_iaqcore_driver
);
198 MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
199 MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
200 MODULE_LICENSE("GPL v2");