0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/mutex.h>
0011 #include <linux/init.h>
0012 #include <linux/i2c.h>
0013 #include <linux/mod_devicetable.h>
0014
0015 #include <linux/iio/iio.h>
0016 #include <linux/iio/sysfs.h>
0017
0018 #define VZ89X_REG_MEASUREMENT 0x09
0019 #define VZ89X_REG_MEASUREMENT_RD_SIZE 6
0020 #define VZ89X_REG_MEASUREMENT_WR_SIZE 3
0021
0022 #define VZ89X_VOC_CO2_IDX 0
0023 #define VZ89X_VOC_SHORT_IDX 1
0024 #define VZ89X_VOC_TVOC_IDX 2
0025 #define VZ89X_VOC_RESISTANCE_IDX 3
0026
0027 #define VZ89TE_REG_MEASUREMENT 0x0c
0028 #define VZ89TE_REG_MEASUREMENT_RD_SIZE 7
0029 #define VZ89TE_REG_MEASUREMENT_WR_SIZE 6
0030
0031 #define VZ89TE_VOC_TVOC_IDX 0
0032 #define VZ89TE_VOC_CO2_IDX 1
0033 #define VZ89TE_VOC_RESISTANCE_IDX 2
0034
0035 enum {
0036 VZ89X,
0037 VZ89TE,
0038 };
0039
0040 struct vz89x_chip_data;
0041
0042 struct vz89x_data {
0043 struct i2c_client *client;
0044 const struct vz89x_chip_data *chip;
0045 struct mutex lock;
0046 int (*xfer)(struct vz89x_data *data, u8 cmd);
0047
0048 bool is_valid;
0049 unsigned long last_update;
0050 u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE];
0051 };
0052
0053 struct vz89x_chip_data {
0054 bool (*valid)(struct vz89x_data *data);
0055 const struct iio_chan_spec *channels;
0056 u8 num_channels;
0057
0058 u8 cmd;
0059 u8 read_size;
0060 u8 write_size;
0061 };
0062
0063 static const struct iio_chan_spec vz89x_channels[] = {
0064 {
0065 .type = IIO_CONCENTRATION,
0066 .channel2 = IIO_MOD_CO2,
0067 .modified = 1,
0068 .info_mask_separate =
0069 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
0070 .address = VZ89X_VOC_CO2_IDX,
0071 },
0072 {
0073 .type = IIO_CONCENTRATION,
0074 .channel2 = IIO_MOD_VOC,
0075 .modified = 1,
0076 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0077 .address = VZ89X_VOC_SHORT_IDX,
0078 .extend_name = "short",
0079 },
0080 {
0081 .type = IIO_CONCENTRATION,
0082 .channel2 = IIO_MOD_VOC,
0083 .modified = 1,
0084 .info_mask_separate =
0085 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
0086 .address = VZ89X_VOC_TVOC_IDX,
0087 },
0088 {
0089 .type = IIO_RESISTANCE,
0090 .info_mask_separate =
0091 BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
0092 .address = VZ89X_VOC_RESISTANCE_IDX,
0093 .scan_index = -1,
0094 .scan_type = {
0095 .endianness = IIO_LE,
0096 },
0097 },
0098 };
0099
0100 static const struct iio_chan_spec vz89te_channels[] = {
0101 {
0102 .type = IIO_CONCENTRATION,
0103 .channel2 = IIO_MOD_VOC,
0104 .modified = 1,
0105 .info_mask_separate =
0106 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
0107 .address = VZ89TE_VOC_TVOC_IDX,
0108 },
0109
0110 {
0111 .type = IIO_CONCENTRATION,
0112 .channel2 = IIO_MOD_CO2,
0113 .modified = 1,
0114 .info_mask_separate =
0115 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
0116 .address = VZ89TE_VOC_CO2_IDX,
0117 },
0118 {
0119 .type = IIO_RESISTANCE,
0120 .info_mask_separate =
0121 BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
0122 .address = VZ89TE_VOC_RESISTANCE_IDX,
0123 .scan_index = -1,
0124 .scan_type = {
0125 .endianness = IIO_BE,
0126 },
0127 },
0128 };
0129
0130 static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
0131 static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
0132
0133 static struct attribute *vz89x_attributes[] = {
0134 &iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
0135 &iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
0136 NULL,
0137 };
0138
0139 static const struct attribute_group vz89x_attrs_group = {
0140 .attrs = vz89x_attributes,
0141 };
0142
0143
0144
0145
0146
0147
0148
0149
0150 static bool vz89x_measurement_is_valid(struct vz89x_data *data)
0151 {
0152 if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0)
0153 return true;
0154
0155 return !!(data->buffer[data->chip->read_size - 1] > 0);
0156 }
0157
0158
0159 static bool vz89te_measurement_is_valid(struct vz89x_data *data)
0160 {
0161 u8 crc = 0;
0162 int i, sum = 0;
0163
0164 for (i = 0; i < (data->chip->read_size - 1); i++) {
0165 sum = crc + data->buffer[i];
0166 crc = sum;
0167 crc += sum / 256;
0168 }
0169
0170 return !((0xff - crc) == data->buffer[data->chip->read_size - 1]);
0171 }
0172
0173 static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
0174 {
0175 const struct vz89x_chip_data *chip = data->chip;
0176 struct i2c_client *client = data->client;
0177 struct i2c_msg msg[2];
0178 int ret;
0179 u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 };
0180
0181 msg[0].addr = client->addr;
0182 msg[0].flags = client->flags;
0183 msg[0].len = chip->write_size;
0184 msg[0].buf = (char *) &buf;
0185
0186 msg[1].addr = client->addr;
0187 msg[1].flags = client->flags | I2C_M_RD;
0188 msg[1].len = chip->read_size;
0189 msg[1].buf = (char *) &data->buffer;
0190
0191 ret = i2c_transfer(client->adapter, msg, 2);
0192
0193 return (ret == 2) ? 0 : ret;
0194 }
0195
0196 static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
0197 {
0198 struct i2c_client *client = data->client;
0199 int ret;
0200 int i;
0201
0202 ret = i2c_smbus_write_word_data(client, cmd, 0);
0203 if (ret < 0)
0204 return ret;
0205
0206 for (i = 0; i < data->chip->read_size; i++) {
0207 ret = i2c_smbus_read_byte(client);
0208 if (ret < 0)
0209 return ret;
0210 data->buffer[i] = ret;
0211 }
0212
0213 return 0;
0214 }
0215
0216 static int vz89x_get_measurement(struct vz89x_data *data)
0217 {
0218 const struct vz89x_chip_data *chip = data->chip;
0219 int ret;
0220
0221
0222 if (!time_after(jiffies, data->last_update + HZ))
0223 return data->is_valid ? 0 : -EAGAIN;
0224
0225 data->is_valid = false;
0226 data->last_update = jiffies;
0227
0228 ret = data->xfer(data, chip->cmd);
0229 if (ret < 0)
0230 return ret;
0231
0232 ret = chip->valid(data);
0233 if (ret)
0234 return -EAGAIN;
0235
0236 data->is_valid = true;
0237
0238 return 0;
0239 }
0240
0241 static int vz89x_get_resistance_reading(struct vz89x_data *data,
0242 struct iio_chan_spec const *chan,
0243 int *val)
0244 {
0245 u8 *tmp = &data->buffer[chan->address];
0246
0247 switch (chan->scan_type.endianness) {
0248 case IIO_LE:
0249 *val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0);
0250 break;
0251 case IIO_BE:
0252 *val = be32_to_cpup((__be32 *) tmp) >> 8;
0253 break;
0254 default:
0255 return -EINVAL;
0256 }
0257
0258 return 0;
0259 }
0260
0261 static int vz89x_read_raw(struct iio_dev *indio_dev,
0262 struct iio_chan_spec const *chan, int *val,
0263 int *val2, long mask)
0264 {
0265 struct vz89x_data *data = iio_priv(indio_dev);
0266 int ret = -EINVAL;
0267
0268 switch (mask) {
0269 case IIO_CHAN_INFO_RAW:
0270 mutex_lock(&data->lock);
0271 ret = vz89x_get_measurement(data);
0272 mutex_unlock(&data->lock);
0273
0274 if (ret)
0275 return ret;
0276
0277 switch (chan->type) {
0278 case IIO_CONCENTRATION:
0279 *val = data->buffer[chan->address];
0280 return IIO_VAL_INT;
0281 case IIO_RESISTANCE:
0282 ret = vz89x_get_resistance_reading(data, chan, val);
0283 if (!ret)
0284 return IIO_VAL_INT;
0285 break;
0286 default:
0287 return -EINVAL;
0288 }
0289 break;
0290 case IIO_CHAN_INFO_SCALE:
0291 switch (chan->type) {
0292 case IIO_RESISTANCE:
0293 *val = 10;
0294 return IIO_VAL_INT;
0295 default:
0296 return -EINVAL;
0297 }
0298 break;
0299 case IIO_CHAN_INFO_OFFSET:
0300 switch (chan->channel2) {
0301 case IIO_MOD_CO2:
0302 *val = 44;
0303 *val2 = 250000;
0304 return IIO_VAL_INT_PLUS_MICRO;
0305 case IIO_MOD_VOC:
0306 *val = -13;
0307 return IIO_VAL_INT;
0308 default:
0309 return -EINVAL;
0310 }
0311 }
0312
0313 return ret;
0314 }
0315
0316 static const struct iio_info vz89x_info = {
0317 .attrs = &vz89x_attrs_group,
0318 .read_raw = vz89x_read_raw,
0319 };
0320
0321 static const struct vz89x_chip_data vz89x_chips[] = {
0322 {
0323 .valid = vz89x_measurement_is_valid,
0324
0325 .cmd = VZ89X_REG_MEASUREMENT,
0326 .read_size = VZ89X_REG_MEASUREMENT_RD_SIZE,
0327 .write_size = VZ89X_REG_MEASUREMENT_WR_SIZE,
0328
0329 .channels = vz89x_channels,
0330 .num_channels = ARRAY_SIZE(vz89x_channels),
0331 },
0332 {
0333 .valid = vz89te_measurement_is_valid,
0334
0335 .cmd = VZ89TE_REG_MEASUREMENT,
0336 .read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE,
0337 .write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE,
0338
0339 .channels = vz89te_channels,
0340 .num_channels = ARRAY_SIZE(vz89te_channels),
0341 },
0342 };
0343
0344 static const struct of_device_id vz89x_dt_ids[] = {
0345 { .compatible = "sgx,vz89x", .data = (void *) VZ89X },
0346 { .compatible = "sgx,vz89te", .data = (void *) VZ89TE },
0347 { }
0348 };
0349 MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
0350
0351 static int vz89x_probe(struct i2c_client *client,
0352 const struct i2c_device_id *id)
0353 {
0354 struct device *dev = &client->dev;
0355 struct iio_dev *indio_dev;
0356 struct vz89x_data *data;
0357 int chip_id;
0358
0359 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
0360 if (!indio_dev)
0361 return -ENOMEM;
0362 data = iio_priv(indio_dev);
0363
0364 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
0365 data->xfer = vz89x_i2c_xfer;
0366 else if (i2c_check_functionality(client->adapter,
0367 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
0368 data->xfer = vz89x_smbus_xfer;
0369 else
0370 return -EOPNOTSUPP;
0371
0372 if (!dev_fwnode(dev))
0373 chip_id = id->driver_data;
0374 else
0375 chip_id = (unsigned long)device_get_match_data(dev);
0376
0377 i2c_set_clientdata(client, indio_dev);
0378 data->client = client;
0379 data->chip = &vz89x_chips[chip_id];
0380 data->last_update = jiffies - HZ;
0381 mutex_init(&data->lock);
0382
0383 indio_dev->info = &vz89x_info;
0384 indio_dev->name = dev_name(dev);
0385 indio_dev->modes = INDIO_DIRECT_MODE;
0386
0387 indio_dev->channels = data->chip->channels;
0388 indio_dev->num_channels = data->chip->num_channels;
0389
0390 return devm_iio_device_register(dev, indio_dev);
0391 }
0392
0393 static const struct i2c_device_id vz89x_id[] = {
0394 { "vz89x", VZ89X },
0395 { "vz89te", VZ89TE },
0396 { }
0397 };
0398 MODULE_DEVICE_TABLE(i2c, vz89x_id);
0399
0400 static struct i2c_driver vz89x_driver = {
0401 .driver = {
0402 .name = "vz89x",
0403 .of_match_table = vz89x_dt_ids,
0404 },
0405 .probe = vz89x_probe,
0406 .id_table = vz89x_id,
0407 };
0408 module_i2c_driver(vz89x_driver);
0409
0410 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
0411 MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
0412 MODULE_LICENSE("GPL v2");