aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Ranostay <mranostay@gmail.com>2015-12-06 01:58:22 -0500
committerJonathan Cameron <jic23@kernel.org>2015-12-12 10:07:38 -0500
commit466df4d0c1a5edee243698bdcad1ec4f3a1799b1 (patch)
tree9897fcab66262e652748165414dbc754a0173300
parent4d33615df58bf308626489cbfb8acbc8bbd45658 (diff)
iio: chemical: add AMS iAQ-core support
Add support for AMS iAQ-core continuous and pulsed VOC sensors. Signed-off-by: Matt Ranostay <mranostay@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--drivers/iio/chemical/Kconfig8
-rw-r--r--drivers/iio/chemical/Makefile1
-rw-r--r--drivers/iio/chemical/ams-iaq-core.c200
4 files changed, 210 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index c50cf13c852e..f6fec952d683 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -20,6 +20,7 @@ adi,adt7476 +/-1C TDM Extended Temp Range I.C
20adi,adt7490 +/-1C TDM Extended Temp Range I.C 20adi,adt7490 +/-1C TDM Extended Temp Range I.C
21adi,adxl345 Three-Axis Digital Accelerometer 21adi,adxl345 Three-Axis Digital Accelerometer
22adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too) 22adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
23ams,iaq-core AMS iAQ-Core VOC Sensor
23at,24c08 i2c serial eeprom (24cxx) 24at,24c08 i2c serial eeprom (24cxx)
24atmel,24c00 i2c serial eeprom (24cxx) 25atmel,24c00 i2c serial eeprom (24cxx)
25atmel,24c01 i2c serial eeprom (24cxx) 26atmel,24c01 i2c serial eeprom (24cxx)
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 3061b7299f0f..f16de61be46d 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -4,6 +4,14 @@
4 4
5menu "Chemical Sensors" 5menu "Chemical Sensors"
6 6
7config IAQCORE
8 tristate "AMS iAQ-Core VOC sensors"
9 depends on I2C
10 help
11 Say Y here to build I2C interface support for the AMS
12 iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
13 sensors
14
7config VZ89X 15config VZ89X
8 tristate "SGX Sensortech MiCS VZ89X VOC sensor" 16 tristate "SGX Sensortech MiCS VZ89X VOC sensor"
9 depends on I2C 17 depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 7292f2ded587..167861fadfab 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -3,4 +3,5 @@
3# 3#
4 4
5# When adding new entries keep the list in alphabetical order 5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
6obj-$(CONFIG_VZ89X) += vz89x.o 7obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c
new file mode 100644
index 000000000000..41a8e6f2e31d
--- /dev/null
+++ b/drivers/iio/chemical/ams-iaq-core.c
@@ -0,0 +1,200 @@
1/*
2 * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
3 *
4 * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
5 *
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.
10 *
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.
15 *
16 */
17
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>
23
24#define AMS_IAQCORE_DATA_SIZE 9
25
26#define AMS_IAQCORE_VOC_CO2_IDX 0
27#define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
28#define AMS_IAQCORE_VOC_TVOC_IDX 2
29
30struct ams_iaqcore_reading {
31 __be16 co2_ppm;
32 u8 status;
33 __be32 resistance;
34 __be16 voc_ppb;
35} __attribute__((__packed__));
36
37struct ams_iaqcore_data {
38 struct i2c_client *client;
39 struct mutex lock;
40 unsigned long last_update;
41
42 struct ams_iaqcore_reading buffer;
43};
44
45static const struct iio_chan_spec ams_iaqcore_channels[] = {
46 {
47 .type = IIO_CONCENTRATION,
48 .channel2 = IIO_MOD_CO2,
49 .modified = 1,
50 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
51 .address = AMS_IAQCORE_VOC_CO2_IDX,
52 },
53 {
54 .type = IIO_RESISTANCE,
55 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
56 .address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
57 },
58 {
59 .type = IIO_CONCENTRATION,
60 .channel2 = IIO_MOD_VOC,
61 .modified = 1,
62 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
63 .address = AMS_IAQCORE_VOC_TVOC_IDX,
64 },
65};
66
67static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
68{
69 struct i2c_client *client = data->client;
70 int ret;
71
72 struct i2c_msg msg = {
73 .addr = client->addr,
74 .flags = client->flags | I2C_M_RD,
75 .len = AMS_IAQCORE_DATA_SIZE,
76 .buf = (char *) &data->buffer,
77 };
78
79 ret = i2c_transfer(client->adapter, &msg, 1);
80
81 return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
82}
83
84static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
85{
86 int ret;
87
88 /* sensor can only be polled once a second max per datasheet */
89 if (!time_after(jiffies, data->last_update + HZ))
90 return 0;
91
92 ret = ams_iaqcore_read_measurement(data);
93 if (ret < 0)
94 return ret;
95
96 data->last_update = jiffies;
97
98 return 0;
99}
100
101static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
102 struct iio_chan_spec const *chan, int *val,
103 int *val2, long mask)
104{
105 struct ams_iaqcore_data *data = iio_priv(indio_dev);
106 int ret;
107
108 if (mask != IIO_CHAN_INFO_PROCESSED)
109 return -EINVAL;
110
111 mutex_lock(&data->lock);
112 ret = ams_iaqcore_get_measurement(data);
113
114 if (ret)
115 goto err_out;
116
117 switch (chan->address) {
118 case AMS_IAQCORE_VOC_CO2_IDX:
119 *val = 0;
120 *val2 = be16_to_cpu(data->buffer.co2_ppm);
121 ret = IIO_VAL_INT_PLUS_MICRO;
122 break;
123 case AMS_IAQCORE_VOC_RESISTANCE_IDX:
124 *val = be32_to_cpu(data->buffer.resistance);
125 ret = IIO_VAL_INT;
126 break;
127 case AMS_IAQCORE_VOC_TVOC_IDX:
128 *val = 0;
129 *val2 = be16_to_cpu(data->buffer.voc_ppb);
130 ret = IIO_VAL_INT_PLUS_NANO;
131 break;
132 default:
133 ret = -EINVAL;
134 }
135
136err_out:
137 mutex_unlock(&data->lock);
138
139 return ret;
140}
141
142static const struct iio_info ams_iaqcore_info = {
143 .read_raw = ams_iaqcore_read_raw,
144 .driver_module = THIS_MODULE,
145};
146
147static int ams_iaqcore_probe(struct i2c_client *client,
148 const struct i2c_device_id *id)
149{
150 struct iio_dev *indio_dev;
151 struct ams_iaqcore_data *data;
152
153 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
154 if (!indio_dev)
155 return -ENOMEM;
156
157 data = iio_priv(indio_dev);
158 i2c_set_clientdata(client, indio_dev);
159 data->client = client;
160
161 /* so initial reading will complete */
162 data->last_update = jiffies - HZ;
163 mutex_init(&data->lock);
164
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;
169
170 indio_dev->channels = ams_iaqcore_channels;
171 indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
172
173 return devm_iio_device_register(&client->dev, indio_dev);
174}
175
176static const struct i2c_device_id ams_iaqcore_id[] = {
177 { "ams-iaq-core", 0 },
178 { }
179};
180MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
181
182static const struct of_device_id ams_iaqcore_dt_ids[] = {
183 { .compatible = "ams,iaq-core" },
184 { }
185};
186MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
187
188static struct i2c_driver ams_iaqcore_driver = {
189 .driver = {
190 .name = "ams-iaq-core",
191 .of_match_table = of_match_ptr(ams_iaqcore_dt_ids),
192 },
193 .probe = ams_iaqcore_probe,
194 .id_table = ams_iaqcore_id,
195};
196module_i2c_driver(ams_iaqcore_driver);
197
198MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
199MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
200MODULE_LICENSE("GPL v2");