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