aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Ranostay <mranostay@gmail.com>2015-09-13 23:26:14 -0400
committerJonathan Cameron <jic23@kernel.org>2015-09-23 15:23:26 -0400
commitcd8d97774f23c601d0ed66b5035bcc77dd665b10 (patch)
tree697447f174078f89052dc1ff7840ddf6cf33468b
parent9bff3131cfb30ef761adfad08c4a0a1b7faf1e20 (diff)
iio: chemical: add SGX VZ89x VOC sensor support
Add support for VZ89X sensors VOC and CO2 reporting channels in percentage which can be converted to part per million. Signed-off-by: Matt Ranostay <mranostay@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x7
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/chemical/Kconfig15
-rw-r--r--drivers/iio/chemical/Makefile6
-rw-r--r--drivers/iio/chemical/vz89x.c237
7 files changed, 268 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x b/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x
new file mode 100644
index 000000000000..c0c1ea924535
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x
@@ -0,0 +1,7 @@
1What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
2Date: September 2015
3KernelVersion: 4.3
4Contact: Matt Ranostay <mranostay@gmail.com>
5Description:
6 Get the raw calibration VOC value from the sensor.
7 This value has little application outside of calibration.
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index e1c07dd8792d..ed7ef4684aa3 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -88,6 +88,7 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
88ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC 88ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
89ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC 89ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
90samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power) 90samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
91sgx,vz89x SGX Sensortech VZ89X Sensors
91sii,s35390a 2-wire CMOS real-time clock 92sii,s35390a 2-wire CMOS real-time clock
92skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply 93skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
93st-micro,24c256 i2c serial eeprom (24cxx) 94st-micro,24c256 i2c serial eeprom (24cxx)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6fe0d6524c9c..119c94df2b9e 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -47,6 +47,7 @@ config IIO_TRIGGERED_EVENT
47source "drivers/iio/accel/Kconfig" 47source "drivers/iio/accel/Kconfig"
48source "drivers/iio/adc/Kconfig" 48source "drivers/iio/adc/Kconfig"
49source "drivers/iio/amplifiers/Kconfig" 49source "drivers/iio/amplifiers/Kconfig"
50source "drivers/iio/chemical/Kconfig"
50source "drivers/iio/common/Kconfig" 51source "drivers/iio/common/Kconfig"
51source "drivers/iio/dac/Kconfig" 52source "drivers/iio/dac/Kconfig"
52source "drivers/iio/frequency/Kconfig" 53source "drivers/iio/frequency/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 40995f366843..e2100554e3b4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -13,6 +13,7 @@ obj-y += accel/
13obj-y += adc/ 13obj-y += adc/
14obj-y += amplifiers/ 14obj-y += amplifiers/
15obj-y += buffer/ 15obj-y += buffer/
16obj-y += chemical/
16obj-y += common/ 17obj-y += common/
17obj-y += dac/ 18obj-y += dac/
18obj-y += gyro/ 19obj-y += gyro/
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
new file mode 100644
index 000000000000..3061b7299f0f
--- /dev/null
+++ b/drivers/iio/chemical/Kconfig
@@ -0,0 +1,15 @@
1#
2# Chemical sensors
3#
4
5menu "Chemical Sensors"
6
7config VZ89X
8 tristate "SGX Sensortech MiCS VZ89X VOC sensor"
9 depends on I2C
10 help
11 Say Y here to build I2C interface support for the SGX
12 Sensortech MiCS VZ89X VOC (Volatile Organic Compounds)
13 sensors
14
15endmenu
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
new file mode 100644
index 000000000000..7292f2ded587
--- /dev/null
+++ b/drivers/iio/chemical/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for IIO chemical sensors
3#
4
5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
new file mode 100644
index 000000000000..b45420039ca2
--- /dev/null
+++ b/drivers/iio/chemical/vz89x.c
@@ -0,0 +1,237 @@
1/*
2 * vz89x.c - Support for SGX Sensortech MiCS VZ89X 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
23#include <linux/iio/iio.h>
24#include <linux/iio/sysfs.h>
25
26#define VZ89X_REG_MEASUREMENT 0x09
27#define VZ89X_REG_MEASUREMENT_SIZE 6
28
29#define VZ89X_VOC_CO2_IDX 0
30#define VZ89X_VOC_SHORT_IDX 1
31#define VZ89X_VOC_TVOC_IDX 2
32#define VZ89X_VOC_RESISTANCE_IDX 3
33
34struct vz89x_data {
35 struct i2c_client *client;
36 struct mutex lock;
37 unsigned long last_update;
38
39 u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
40};
41
42static const struct iio_chan_spec vz89x_channels[] = {
43 {
44 .type = IIO_CONCENTRATION,
45 .channel2 = IIO_MOD_CO2,
46 .modified = 1,
47 .info_mask_separate =
48 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
49 .address = VZ89X_VOC_CO2_IDX,
50 },
51 {
52 .type = IIO_CONCENTRATION,
53 .channel2 = IIO_MOD_VOC,
54 .modified = 1,
55 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
56 .address = VZ89X_VOC_SHORT_IDX,
57 .extend_name = "short",
58 },
59 {
60 .type = IIO_CONCENTRATION,
61 .channel2 = IIO_MOD_VOC,
62 .modified = 1,
63 .info_mask_separate =
64 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
65 .address = VZ89X_VOC_TVOC_IDX,
66 },
67 {
68 .type = IIO_RESISTANCE,
69 .info_mask_separate =
70 BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
71 .address = VZ89X_VOC_RESISTANCE_IDX,
72 },
73};
74
75static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
76static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
77
78static struct attribute *vz89x_attributes[] = {
79 &iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
80 &iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
81 NULL,
82};
83
84static const struct attribute_group vz89x_attrs_group = {
85 .attrs = vz89x_attributes,
86};
87
88static int vz89x_get_measurement(struct vz89x_data *data)
89{
90 int ret;
91 int i;
92
93 /* sensor can only be polled once a second max per datasheet */
94 if (!time_after(jiffies, data->last_update + HZ))
95 return 0;
96
97 ret = i2c_smbus_write_word_data(data->client,
98 VZ89X_REG_MEASUREMENT, 0);
99 if (ret < 0)
100 return ret;
101
102 for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
103 ret = i2c_smbus_read_byte(data->client);
104 if (ret < 0)
105 return ret;
106 data->buffer[i] = ret;
107 }
108
109 data->last_update = jiffies;
110
111 return 0;
112}
113
114static int vz89x_get_resistance_reading(struct vz89x_data *data)
115{
116 u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX];
117
118 return buf[0] | (buf[1] << 8) | (buf[2] << 16);
119}
120
121static int vz89x_read_raw(struct iio_dev *indio_dev,
122 struct iio_chan_spec const *chan, int *val,
123 int *val2, long mask)
124{
125 struct vz89x_data *data = iio_priv(indio_dev);
126 int ret = -EINVAL;
127
128 switch (mask) {
129 case IIO_CHAN_INFO_RAW:
130 mutex_lock(&data->lock);
131 ret = vz89x_get_measurement(data);
132 mutex_unlock(&data->lock);
133
134 if (ret)
135 return ret;
136
137 switch (chan->address) {
138 case VZ89X_VOC_CO2_IDX:
139 case VZ89X_VOC_SHORT_IDX:
140 case VZ89X_VOC_TVOC_IDX:
141 *val = data->buffer[chan->address];
142 return IIO_VAL_INT;
143 case VZ89X_VOC_RESISTANCE_IDX:
144 *val = vz89x_get_resistance_reading(data);
145 return IIO_VAL_INT;
146 default:
147 return -EINVAL;
148 }
149 break;
150 case IIO_CHAN_INFO_SCALE:
151 switch (chan->type) {
152 case IIO_RESISTANCE:
153 *val = 10;
154 return IIO_VAL_INT;
155 default:
156 return -EINVAL;
157 }
158 break;
159 case IIO_CHAN_INFO_OFFSET:
160 switch (chan->address) {
161 case VZ89X_VOC_CO2_IDX:
162 *val = 44;
163 *val2 = 250000;
164 return IIO_VAL_INT_PLUS_MICRO;
165 case VZ89X_VOC_TVOC_IDX:
166 *val = -13;
167 return IIO_VAL_INT;
168 default:
169 return -EINVAL;
170 }
171 }
172
173 return ret;
174}
175
176static const struct iio_info vz89x_info = {
177 .attrs = &vz89x_attrs_group,
178 .read_raw = vz89x_read_raw,
179 .driver_module = THIS_MODULE,
180};
181
182static int vz89x_probe(struct i2c_client *client,
183 const struct i2c_device_id *id)
184{
185 struct iio_dev *indio_dev;
186 struct vz89x_data *data;
187
188 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
189 I2C_FUNC_SMBUS_BYTE))
190 return -ENODEV;
191
192 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
193 if (!indio_dev)
194 return -ENOMEM;
195
196 data = iio_priv(indio_dev);
197 i2c_set_clientdata(client, indio_dev);
198 data->client = client;
199 data->last_update = jiffies - HZ;
200 mutex_init(&data->lock);
201
202 indio_dev->dev.parent = &client->dev;
203 indio_dev->info = &vz89x_info,
204 indio_dev->name = dev_name(&client->dev);
205 indio_dev->modes = INDIO_DIRECT_MODE;
206
207 indio_dev->channels = vz89x_channels;
208 indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
209
210 return devm_iio_device_register(&client->dev, indio_dev);
211}
212
213static const struct i2c_device_id vz89x_id[] = {
214 { "vz89x", 0 },
215 { }
216};
217MODULE_DEVICE_TABLE(i2c, vz89x_id);
218
219static const struct of_device_id vz89x_dt_ids[] = {
220 { .compatible = "sgx,vz89x" },
221 { }
222};
223MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
224
225static struct i2c_driver vz89x_driver = {
226 .driver = {
227 .name = "vz89x",
228 .of_match_table = of_match_ptr(vz89x_dt_ids),
229 },
230 .probe = vz89x_probe,
231 .id_table = vz89x_id,
232};
233module_i2c_driver(vz89x_driver);
234
235MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
236MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
237MODULE_LICENSE("GPL v2");