0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/mod_devicetable.h>
0011 #include <linux/mutex.h>
0012 #include <linux/init.h>
0013 #include <linux/i2c.h>
0014 #include <linux/iio/iio.h>
0015
0016 #define AMS_IAQCORE_DATA_SIZE 9
0017
0018 #define AMS_IAQCORE_VOC_CO2_IDX 0
0019 #define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
0020 #define AMS_IAQCORE_VOC_TVOC_IDX 2
0021
0022 struct ams_iaqcore_reading {
0023 __be16 co2_ppm;
0024 u8 status;
0025 __be32 resistance;
0026 __be16 voc_ppb;
0027 } __attribute__((__packed__));
0028
0029 struct ams_iaqcore_data {
0030 struct i2c_client *client;
0031 struct mutex lock;
0032 unsigned long last_update;
0033
0034 struct ams_iaqcore_reading buffer;
0035 };
0036
0037 static const struct iio_chan_spec ams_iaqcore_channels[] = {
0038 {
0039 .type = IIO_CONCENTRATION,
0040 .channel2 = IIO_MOD_CO2,
0041 .modified = 1,
0042 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
0043 .address = AMS_IAQCORE_VOC_CO2_IDX,
0044 },
0045 {
0046 .type = IIO_RESISTANCE,
0047 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
0048 .address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
0049 },
0050 {
0051 .type = IIO_CONCENTRATION,
0052 .channel2 = IIO_MOD_VOC,
0053 .modified = 1,
0054 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
0055 .address = AMS_IAQCORE_VOC_TVOC_IDX,
0056 },
0057 };
0058
0059 static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
0060 {
0061 struct i2c_client *client = data->client;
0062 int ret;
0063
0064 struct i2c_msg msg = {
0065 .addr = client->addr,
0066 .flags = client->flags | I2C_M_RD,
0067 .len = AMS_IAQCORE_DATA_SIZE,
0068 .buf = (char *) &data->buffer,
0069 };
0070
0071 ret = i2c_transfer(client->adapter, &msg, 1);
0072
0073 return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
0074 }
0075
0076 static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
0077 {
0078 int ret;
0079
0080
0081 if (!time_after(jiffies, data->last_update + HZ))
0082 return 0;
0083
0084 ret = ams_iaqcore_read_measurement(data);
0085 if (ret < 0)
0086 return ret;
0087
0088 data->last_update = jiffies;
0089
0090 return 0;
0091 }
0092
0093 static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
0094 struct iio_chan_spec const *chan, int *val,
0095 int *val2, long mask)
0096 {
0097 struct ams_iaqcore_data *data = iio_priv(indio_dev);
0098 int ret;
0099
0100 if (mask != IIO_CHAN_INFO_PROCESSED)
0101 return -EINVAL;
0102
0103 mutex_lock(&data->lock);
0104 ret = ams_iaqcore_get_measurement(data);
0105
0106 if (ret)
0107 goto err_out;
0108
0109 switch (chan->address) {
0110 case AMS_IAQCORE_VOC_CO2_IDX:
0111 *val = 0;
0112 *val2 = be16_to_cpu(data->buffer.co2_ppm);
0113 ret = IIO_VAL_INT_PLUS_MICRO;
0114 break;
0115 case AMS_IAQCORE_VOC_RESISTANCE_IDX:
0116 *val = be32_to_cpu(data->buffer.resistance);
0117 ret = IIO_VAL_INT;
0118 break;
0119 case AMS_IAQCORE_VOC_TVOC_IDX:
0120 *val = 0;
0121 *val2 = be16_to_cpu(data->buffer.voc_ppb);
0122 ret = IIO_VAL_INT_PLUS_NANO;
0123 break;
0124 default:
0125 ret = -EINVAL;
0126 }
0127
0128 err_out:
0129 mutex_unlock(&data->lock);
0130
0131 return ret;
0132 }
0133
0134 static const struct iio_info ams_iaqcore_info = {
0135 .read_raw = ams_iaqcore_read_raw,
0136 };
0137
0138 static int ams_iaqcore_probe(struct i2c_client *client,
0139 const struct i2c_device_id *id)
0140 {
0141 struct iio_dev *indio_dev;
0142 struct ams_iaqcore_data *data;
0143
0144 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0145 if (!indio_dev)
0146 return -ENOMEM;
0147
0148 data = iio_priv(indio_dev);
0149 i2c_set_clientdata(client, indio_dev);
0150 data->client = client;
0151
0152
0153 data->last_update = jiffies - HZ;
0154 mutex_init(&data->lock);
0155
0156 indio_dev->info = &ams_iaqcore_info;
0157 indio_dev->name = dev_name(&client->dev);
0158 indio_dev->modes = INDIO_DIRECT_MODE;
0159
0160 indio_dev->channels = ams_iaqcore_channels;
0161 indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
0162
0163 return devm_iio_device_register(&client->dev, indio_dev);
0164 }
0165
0166 static const struct i2c_device_id ams_iaqcore_id[] = {
0167 { "ams-iaq-core", 0 },
0168 { }
0169 };
0170 MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
0171
0172 static const struct of_device_id ams_iaqcore_dt_ids[] = {
0173 { .compatible = "ams,iaq-core" },
0174 { }
0175 };
0176 MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
0177
0178 static struct i2c_driver ams_iaqcore_driver = {
0179 .driver = {
0180 .name = "ams-iaq-core",
0181 .of_match_table = ams_iaqcore_dt_ids,
0182 },
0183 .probe = ams_iaqcore_probe,
0184 .id_table = ams_iaqcore_id,
0185 };
0186 module_i2c_driver(ams_iaqcore_driver);
0187
0188 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
0189 MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
0190 MODULE_LICENSE("GPL v2");