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