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