root/drivers/iio/chemical/vz89x.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vz89x_measurement_is_valid
  2. vz89te_measurement_is_valid
  3. vz89x_i2c_xfer
  4. vz89x_smbus_xfer
  5. vz89x_get_measurement
  6. vz89x_get_resistance_reading
  7. vz89x_read_raw
  8. vz89x_probe

   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");

/* [<][>][^][v][top][bottom][index][help] */