root/drivers/iio/chemical/ams-iaq-core.c

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

DEFINITIONS

This source file includes following definitions.
  1. ams_iaqcore_read_measurement
  2. ams_iaqcore_get_measurement
  3. ams_iaqcore_read_raw
  4. ams_iaqcore_probe

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * ams-iaq-core.c - Support for AMS iAQ-Core 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/iio/iio.h>
  14 
  15 #define AMS_IAQCORE_DATA_SIZE           9
  16 
  17 #define AMS_IAQCORE_VOC_CO2_IDX         0
  18 #define AMS_IAQCORE_VOC_RESISTANCE_IDX  1
  19 #define AMS_IAQCORE_VOC_TVOC_IDX        2
  20 
  21 struct ams_iaqcore_reading {
  22         __be16 co2_ppm;
  23         u8 status;
  24         __be32 resistance;
  25         __be16 voc_ppb;
  26 } __attribute__((__packed__));
  27 
  28 struct ams_iaqcore_data {
  29         struct i2c_client *client;
  30         struct mutex lock;
  31         unsigned long last_update;
  32 
  33         struct ams_iaqcore_reading buffer;
  34 };
  35 
  36 static const struct iio_chan_spec ams_iaqcore_channels[] = {
  37         {
  38                 .type = IIO_CONCENTRATION,
  39                 .channel2 = IIO_MOD_CO2,
  40                 .modified = 1,
  41                 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
  42                 .address = AMS_IAQCORE_VOC_CO2_IDX,
  43         },
  44         {
  45                 .type = IIO_RESISTANCE,
  46                 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
  47                 .address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
  48         },
  49         {
  50                 .type = IIO_CONCENTRATION,
  51                 .channel2 = IIO_MOD_VOC,
  52                 .modified = 1,
  53                 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
  54                 .address = AMS_IAQCORE_VOC_TVOC_IDX,
  55         },
  56 };
  57 
  58 static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
  59 {
  60         struct i2c_client *client = data->client;
  61         int ret;
  62 
  63         struct i2c_msg msg = {
  64                 .addr = client->addr,
  65                 .flags = client->flags | I2C_M_RD,
  66                 .len = AMS_IAQCORE_DATA_SIZE,
  67                 .buf = (char *) &data->buffer,
  68         };
  69 
  70         ret = i2c_transfer(client->adapter, &msg, 1);
  71 
  72         return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
  73 }
  74 
  75 static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
  76 {
  77         int ret;
  78 
  79         /* sensor can only be polled once a second max per datasheet */
  80         if (!time_after(jiffies, data->last_update + HZ))
  81                 return 0;
  82 
  83         ret = ams_iaqcore_read_measurement(data);
  84         if (ret < 0)
  85                 return ret;
  86 
  87         data->last_update = jiffies;
  88 
  89         return 0;
  90 }
  91 
  92 static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
  93                                 struct iio_chan_spec const *chan, int *val,
  94                                 int *val2, long mask)
  95 {
  96         struct ams_iaqcore_data *data = iio_priv(indio_dev);
  97         int ret;
  98 
  99         if (mask != IIO_CHAN_INFO_PROCESSED)
 100                 return -EINVAL;
 101 
 102         mutex_lock(&data->lock);
 103         ret = ams_iaqcore_get_measurement(data);
 104 
 105         if (ret)
 106                 goto err_out;
 107 
 108         switch (chan->address) {
 109         case AMS_IAQCORE_VOC_CO2_IDX:
 110                 *val = 0;
 111                 *val2 = be16_to_cpu(data->buffer.co2_ppm);
 112                 ret = IIO_VAL_INT_PLUS_MICRO;
 113                 break;
 114         case AMS_IAQCORE_VOC_RESISTANCE_IDX:
 115                 *val = be32_to_cpu(data->buffer.resistance);
 116                 ret = IIO_VAL_INT;
 117                 break;
 118         case AMS_IAQCORE_VOC_TVOC_IDX:
 119                 *val = 0;
 120                 *val2 = be16_to_cpu(data->buffer.voc_ppb);
 121                 ret = IIO_VAL_INT_PLUS_NANO;
 122                 break;
 123         default:
 124                 ret = -EINVAL;
 125         }
 126 
 127 err_out:
 128         mutex_unlock(&data->lock);
 129 
 130         return ret;
 131 }
 132 
 133 static const struct iio_info ams_iaqcore_info = {
 134         .read_raw       = ams_iaqcore_read_raw,
 135 };
 136 
 137 static int ams_iaqcore_probe(struct i2c_client *client,
 138                              const struct i2c_device_id *id)
 139 {
 140         struct iio_dev *indio_dev;
 141         struct ams_iaqcore_data *data;
 142 
 143         indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 144         if (!indio_dev)
 145                 return -ENOMEM;
 146 
 147         data = iio_priv(indio_dev);
 148         i2c_set_clientdata(client, indio_dev);
 149         data->client = client;
 150 
 151         /* so initial reading will complete */
 152         data->last_update = jiffies - HZ;
 153         mutex_init(&data->lock);
 154 
 155         indio_dev->dev.parent = &client->dev;
 156         indio_dev->info = &ams_iaqcore_info;
 157         indio_dev->name = dev_name(&client->dev);
 158         indio_dev->modes = INDIO_DIRECT_MODE;
 159 
 160         indio_dev->channels = ams_iaqcore_channels;
 161         indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
 162 
 163         return devm_iio_device_register(&client->dev, indio_dev);
 164 }
 165 
 166 static const struct i2c_device_id ams_iaqcore_id[] = {
 167         { "ams-iaq-core", 0 },
 168         { }
 169 };
 170 MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
 171 
 172 static const struct of_device_id ams_iaqcore_dt_ids[] = {
 173         { .compatible = "ams,iaq-core" },
 174         { }
 175 };
 176 MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
 177 
 178 static struct i2c_driver ams_iaqcore_driver = {
 179         .driver = {
 180                 .name   = "ams-iaq-core",
 181                 .of_match_table = of_match_ptr(ams_iaqcore_dt_ids),
 182         },
 183         .probe = ams_iaqcore_probe,
 184         .id_table = ams_iaqcore_id,
 185 };
 186 module_i2c_driver(ams_iaqcore_driver);
 187 
 188 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
 189 MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
 190 MODULE_LICENSE("GPL v2");

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