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 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 */ 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 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 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 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 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 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 = &vz89x_chips[VZ89X] }, 346 { .compatible = "sgx,vz89te", .data = &vz89x_chips[VZ89TE] }, 347 { } 348 }; 349 MODULE_DEVICE_TABLE(of, vz89x_dt_ids); 350 351 static int vz89x_probe(struct i2c_client *client) 352 { 353 struct device *dev = &client->dev; 354 struct iio_dev *indio_dev; 355 struct vz89x_data *data; 356 357 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 358 if (!indio_dev) 359 return -ENOMEM; 360 data = iio_priv(indio_dev); 361 362 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 363 data->xfer = vz89x_i2c_xfer; 364 else if (i2c_check_functionality(client->adapter, 365 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) 366 data->xfer = vz89x_smbus_xfer; 367 else 368 return -EOPNOTSUPP; 369 370 data->chip = i2c_get_match_data(client); 371 372 i2c_set_clientdata(client, indio_dev); 373 data->client = client; 374 data->last_update = jiffies - HZ; 375 mutex_init(&data->lock); 376 377 indio_dev->info = &vz89x_info; 378 indio_dev->name = dev_name(dev); 379 indio_dev->modes = INDIO_DIRECT_MODE; 380 381 indio_dev->channels = data->chip->channels; 382 indio_dev->num_channels = data->chip->num_channels; 383 384 return devm_iio_device_register(dev, indio_dev); 385 } 386 387 static const struct i2c_device_id vz89x_id[] = { 388 { "vz89x", (kernel_ulong_t)&vz89x_chips[VZ89X] }, 389 { "vz89te", (kernel_ulong_t)&vz89x_chips[VZ89TE] }, 390 { } 391 }; 392 MODULE_DEVICE_TABLE(i2c, vz89x_id); 393 394 static struct i2c_driver vz89x_driver = { 395 .driver = { 396 .name = "vz89x", 397 .of_match_table = vz89x_dt_ids, 398 }, 399 .probe = vz89x_probe, 400 .id_table = vz89x_id, 401 }; 402 module_i2c_driver(vz89x_driver); 403 404 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 405 MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors"); 406 MODULE_LICENSE("GPL v2"); 407