xref: /linux/drivers/iio/chemical/vz89x.c (revision e58e871becec2d3b04ed91c0c16fe8deac9c9dfa)
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 #include <linux/of.h>
23 #include <linux/of_device.h>
24 
25 #include <linux/iio/iio.h>
26 #include <linux/iio/sysfs.h>
27 
28 #define VZ89X_REG_MEASUREMENT		0x09
29 #define VZ89X_REG_MEASUREMENT_RD_SIZE	6
30 #define VZ89X_REG_MEASUREMENT_WR_SIZE	3
31 
32 #define VZ89X_VOC_CO2_IDX		0
33 #define VZ89X_VOC_SHORT_IDX		1
34 #define VZ89X_VOC_TVOC_IDX		2
35 #define VZ89X_VOC_RESISTANCE_IDX	3
36 
37 #define VZ89TE_REG_MEASUREMENT		0x0c
38 #define VZ89TE_REG_MEASUREMENT_RD_SIZE	7
39 #define VZ89TE_REG_MEASUREMENT_WR_SIZE	6
40 
41 #define VZ89TE_VOC_TVOC_IDX		0
42 #define VZ89TE_VOC_CO2_IDX		1
43 #define VZ89TE_VOC_RESISTANCE_IDX	2
44 
45 enum {
46 	VZ89X,
47 	VZ89TE,
48 };
49 
50 struct vz89x_chip_data;
51 
52 struct vz89x_data {
53 	struct i2c_client *client;
54 	const struct vz89x_chip_data *chip;
55 	struct mutex lock;
56 	int (*xfer)(struct vz89x_data *data, u8 cmd);
57 
58 	bool is_valid;
59 	unsigned long last_update;
60 	u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE];
61 };
62 
63 struct vz89x_chip_data {
64 	bool (*valid)(struct vz89x_data *data);
65 	const struct iio_chan_spec *channels;
66 	u8 num_channels;
67 
68 	u8 cmd;
69 	u8 read_size;
70 	u8 write_size;
71 };
72 
73 static const struct iio_chan_spec vz89x_channels[] = {
74 	{
75 		.type = IIO_CONCENTRATION,
76 		.channel2 = IIO_MOD_CO2,
77 		.modified = 1,
78 		.info_mask_separate =
79 			BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
80 		.address = VZ89X_VOC_CO2_IDX,
81 	},
82 	{
83 		.type = IIO_CONCENTRATION,
84 		.channel2 = IIO_MOD_VOC,
85 		.modified = 1,
86 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
87 		.address = VZ89X_VOC_SHORT_IDX,
88 		.extend_name = "short",
89 	},
90 	{
91 		.type = IIO_CONCENTRATION,
92 		.channel2 = IIO_MOD_VOC,
93 		.modified = 1,
94 		.info_mask_separate =
95 			BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
96 		.address = VZ89X_VOC_TVOC_IDX,
97 	},
98 	{
99 		.type = IIO_RESISTANCE,
100 		.info_mask_separate =
101 			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
102 		.address = VZ89X_VOC_RESISTANCE_IDX,
103 		.scan_index = -1,
104 		.scan_type = {
105 			.endianness = IIO_LE,
106 		},
107 	},
108 };
109 
110 static const struct iio_chan_spec vz89te_channels[] = {
111 	{
112 		.type = IIO_CONCENTRATION,
113 		.channel2 = IIO_MOD_VOC,
114 		.modified = 1,
115 		.info_mask_separate =
116 			BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
117 		.address = VZ89TE_VOC_TVOC_IDX,
118 	},
119 
120 	{
121 		.type = IIO_CONCENTRATION,
122 		.channel2 = IIO_MOD_CO2,
123 		.modified = 1,
124 		.info_mask_separate =
125 			BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
126 		.address = VZ89TE_VOC_CO2_IDX,
127 	},
128 	{
129 		.type = IIO_RESISTANCE,
130 		.info_mask_separate =
131 			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
132 		.address = VZ89TE_VOC_RESISTANCE_IDX,
133 		.scan_index = -1,
134 		.scan_type = {
135 			.endianness = IIO_BE,
136 		},
137 	},
138 };
139 
140 static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
141 static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
142 
143 static struct attribute *vz89x_attributes[] = {
144 	&iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
145 	&iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
146 	NULL,
147 };
148 
149 static const struct attribute_group vz89x_attrs_group = {
150 	.attrs = vz89x_attributes,
151 };
152 
153 /*
154  * Chipset sometime updates in the middle of a reading causing it to reset the
155  * data pointer, and causing invalid reading of previous data.
156  * We can check for this by reading MSB of the resistance reading that is
157  * always zero, and by also confirming the VOC_short isn't zero.
158  */
159 
160 static bool vz89x_measurement_is_valid(struct vz89x_data *data)
161 {
162 	if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0)
163 		return true;
164 
165 	return !!(data->buffer[data->chip->read_size - 1] > 0);
166 }
167 
168 /* VZ89TE device has a modified CRC-8 two complement check */
169 static bool vz89te_measurement_is_valid(struct vz89x_data *data)
170 {
171 	u8 crc = 0;
172 	int i, sum = 0;
173 
174 	for (i = 0; i < (data->chip->read_size - 1); i++) {
175 		sum = crc + data->buffer[i];
176 		crc = sum;
177 		crc += sum / 256;
178 	}
179 
180 	return !((0xff - crc) == data->buffer[data->chip->read_size - 1]);
181 }
182 
183 static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
184 {
185 	const struct vz89x_chip_data *chip = data->chip;
186 	struct i2c_client *client = data->client;
187 	struct i2c_msg msg[2];
188 	int ret;
189 	u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 };
190 
191 	msg[0].addr = client->addr;
192 	msg[0].flags = client->flags;
193 	msg[0].len = chip->write_size;
194 	msg[0].buf  = (char *) &buf;
195 
196 	msg[1].addr = client->addr;
197 	msg[1].flags = client->flags | I2C_M_RD;
198 	msg[1].len = chip->read_size;
199 	msg[1].buf = (char *) &data->buffer;
200 
201 	ret = i2c_transfer(client->adapter, msg, 2);
202 
203 	return (ret == 2) ? 0 : ret;
204 }
205 
206 static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
207 {
208 	struct i2c_client *client = data->client;
209 	int ret;
210 	int i;
211 
212 	ret = i2c_smbus_write_word_data(client, cmd, 0);
213 	if (ret < 0)
214 		return ret;
215 
216 	for (i = 0; i < data->chip->read_size; i++) {
217 		ret = i2c_smbus_read_byte(client);
218 		if (ret < 0)
219 			return ret;
220 		data->buffer[i] = ret;
221 	}
222 
223 	return 0;
224 }
225 
226 static int vz89x_get_measurement(struct vz89x_data *data)
227 {
228 	const struct vz89x_chip_data *chip = data->chip;
229 	int ret;
230 
231 	/* sensor can only be polled once a second max per datasheet */
232 	if (!time_after(jiffies, data->last_update + HZ))
233 		return data->is_valid ? 0 : -EAGAIN;
234 
235 	data->is_valid = false;
236 	data->last_update = jiffies;
237 
238 	ret = data->xfer(data, chip->cmd);
239 	if (ret < 0)
240 		return ret;
241 
242 	ret = chip->valid(data);
243 	if (ret)
244 		return -EAGAIN;
245 
246 	data->is_valid = true;
247 
248 	return 0;
249 }
250 
251 static int vz89x_get_resistance_reading(struct vz89x_data *data,
252 					struct iio_chan_spec const *chan,
253 					int *val)
254 {
255 	u8 *tmp = (u8 *) &data->buffer[chan->address];
256 
257 	switch (chan->scan_type.endianness) {
258 	case IIO_LE:
259 		*val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0);
260 		break;
261 	case IIO_BE:
262 		*val = be32_to_cpup((__be32 *) tmp) >> 8;
263 		break;
264 	default:
265 		return -EINVAL;
266 	}
267 
268 	return 0;
269 }
270 
271 static int vz89x_read_raw(struct iio_dev *indio_dev,
272 			  struct iio_chan_spec const *chan, int *val,
273 			  int *val2, long mask)
274 {
275 	struct vz89x_data *data = iio_priv(indio_dev);
276 	int ret = -EINVAL;
277 
278 	switch (mask) {
279 	case IIO_CHAN_INFO_RAW:
280 		mutex_lock(&data->lock);
281 		ret = vz89x_get_measurement(data);
282 		mutex_unlock(&data->lock);
283 
284 		if (ret)
285 			return ret;
286 
287 		switch (chan->type) {
288 		case IIO_CONCENTRATION:
289 			*val = data->buffer[chan->address];
290 			return IIO_VAL_INT;
291 		case IIO_RESISTANCE:
292 			ret = vz89x_get_resistance_reading(data, chan, val);
293 			if (!ret)
294 				return IIO_VAL_INT;
295 			break;
296 		default:
297 			return -EINVAL;
298 		}
299 		break;
300 	case IIO_CHAN_INFO_SCALE:
301 		switch (chan->type) {
302 		case IIO_RESISTANCE:
303 			*val = 10;
304 			return IIO_VAL_INT;
305 		default:
306 			return -EINVAL;
307 		}
308 		break;
309 	case IIO_CHAN_INFO_OFFSET:
310 		switch (chan->channel2) {
311 		case IIO_MOD_CO2:
312 			*val = 44;
313 			*val2 = 250000;
314 			return IIO_VAL_INT_PLUS_MICRO;
315 		case IIO_MOD_VOC:
316 			*val = -13;
317 			return IIO_VAL_INT;
318 		default:
319 			return -EINVAL;
320 		}
321 	}
322 
323 	return ret;
324 }
325 
326 static const struct iio_info vz89x_info = {
327 	.attrs		= &vz89x_attrs_group,
328 	.read_raw	= vz89x_read_raw,
329 	.driver_module	= THIS_MODULE,
330 };
331 
332 static const struct vz89x_chip_data vz89x_chips[] = {
333 	{
334 		.valid = vz89x_measurement_is_valid,
335 
336 		.cmd = VZ89X_REG_MEASUREMENT,
337 		.read_size = VZ89X_REG_MEASUREMENT_RD_SIZE,
338 		.write_size = VZ89X_REG_MEASUREMENT_WR_SIZE,
339 
340 		.channels = vz89x_channels,
341 		.num_channels = ARRAY_SIZE(vz89x_channels),
342 	},
343 	{
344 		.valid = vz89te_measurement_is_valid,
345 
346 		.cmd = VZ89TE_REG_MEASUREMENT,
347 		.read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE,
348 		.write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE,
349 
350 		.channels = vz89te_channels,
351 		.num_channels = ARRAY_SIZE(vz89te_channels),
352 	},
353 };
354 
355 static const struct of_device_id vz89x_dt_ids[] = {
356 	{ .compatible = "sgx,vz89x", .data = (void *) VZ89X },
357 	{ .compatible = "sgx,vz89te", .data = (void *) VZ89TE },
358 	{ }
359 };
360 MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
361 
362 static int vz89x_probe(struct i2c_client *client,
363 		       const struct i2c_device_id *id)
364 {
365 	struct iio_dev *indio_dev;
366 	struct vz89x_data *data;
367 	const struct of_device_id *of_id;
368 	int chip_id;
369 
370 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
371 	if (!indio_dev)
372 		return -ENOMEM;
373 	data = iio_priv(indio_dev);
374 
375 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
376 		data->xfer = vz89x_i2c_xfer;
377 	else if (i2c_check_functionality(client->adapter,
378 				I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
379 		data->xfer = vz89x_smbus_xfer;
380 	else
381 		return -EOPNOTSUPP;
382 
383 	of_id = of_match_device(vz89x_dt_ids, &client->dev);
384 	if (!of_id)
385 		chip_id = id->driver_data;
386 	else
387 		chip_id = (unsigned long)of_id->data;
388 
389 	i2c_set_clientdata(client, indio_dev);
390 	data->client = client;
391 	data->chip = &vz89x_chips[chip_id];
392 	data->last_update = jiffies - HZ;
393 	mutex_init(&data->lock);
394 
395 	indio_dev->dev.parent = &client->dev;
396 	indio_dev->info = &vz89x_info;
397 	indio_dev->name = dev_name(&client->dev);
398 	indio_dev->modes = INDIO_DIRECT_MODE;
399 
400 	indio_dev->channels = data->chip->channels;
401 	indio_dev->num_channels = data->chip->num_channels;
402 
403 	return devm_iio_device_register(&client->dev, indio_dev);
404 }
405 
406 static const struct i2c_device_id vz89x_id[] = {
407 	{ "vz89x", VZ89X },
408 	{ "vz89te", VZ89TE },
409 	{ }
410 };
411 MODULE_DEVICE_TABLE(i2c, vz89x_id);
412 
413 static struct i2c_driver vz89x_driver = {
414 	.driver = {
415 		.name	= "vz89x",
416 		.of_match_table = of_match_ptr(vz89x_dt_ids),
417 	},
418 	.probe = vz89x_probe,
419 	.id_table = vz89x_id,
420 };
421 module_i2c_driver(vz89x_driver);
422 
423 MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
424 MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
425 MODULE_LICENSE("GPL v2");
426