linux/drivers/iio/humidity/si7005.c
<<
>>
Prefs
   1/*
   2 * si7005.c - Support for Silabs Si7005 humidity and temperature sensor
   3 *
   4 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
   5 *
   6 * This file is subject to the terms and conditions of version 2 of
   7 * the GNU General Public License.  See the file COPYING in the main
   8 * directory of this archive for more details.
   9 *
  10 * (7-bit I2C slave address 0x40)
  11 *
  12 * TODO: heater, fast mode, processed mode (temp. / linearity compensation)
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/i2c.h>
  17#include <linux/delay.h>
  18#include <linux/module.h>
  19#include <linux/pm.h>
  20
  21#include <linux/iio/iio.h>
  22#include <linux/iio/sysfs.h>
  23
  24#define SI7005_STATUS 0x00
  25#define SI7005_DATA 0x01 /* 16-bit, MSB */
  26#define SI7005_CONFIG 0x03
  27#define SI7005_ID 0x11
  28
  29#define SI7005_STATUS_NRDY BIT(0)
  30#define SI7005_CONFIG_TEMP BIT(4)
  31#define SI7005_CONFIG_START BIT(0)
  32
  33#define SI7005_ID_7005 0x50
  34#define SI7005_ID_7015 0xf0
  35
  36struct si7005_data {
  37        struct i2c_client *client;
  38        struct mutex lock;
  39        u8 config;
  40};
  41
  42static int si7005_read_measurement(struct si7005_data *data, bool temp)
  43{
  44        int tries = 50;
  45        int ret;
  46
  47        mutex_lock(&data->lock);
  48
  49        ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG,
  50                data->config | SI7005_CONFIG_START |
  51                (temp ? SI7005_CONFIG_TEMP : 0));
  52        if (ret < 0)
  53                goto done;
  54
  55        while (tries-- > 0) {
  56                msleep(20);
  57                ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS);
  58                if (ret < 0)
  59                        goto done;
  60                if (!(ret & SI7005_STATUS_NRDY))
  61                        break;
  62        }
  63        if (tries < 0) {
  64                ret = -EIO;
  65                goto done;
  66        }
  67
  68        ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA);
  69
  70done:
  71        mutex_unlock(&data->lock);
  72
  73        return ret;
  74}
  75
  76static int si7005_read_raw(struct iio_dev *indio_dev,
  77                            struct iio_chan_spec const *chan, int *val,
  78                            int *val2, long mask)
  79{
  80        struct si7005_data *data = iio_priv(indio_dev);
  81        int ret;
  82
  83        switch (mask) {
  84        case IIO_CHAN_INFO_RAW:
  85                ret = si7005_read_measurement(data, chan->type == IIO_TEMP);
  86                if (ret < 0)
  87                        return ret;
  88                *val = ret;
  89                return IIO_VAL_INT;
  90        case IIO_CHAN_INFO_SCALE:
  91                if (chan->type == IIO_TEMP) {
  92                        *val = 7;
  93                        *val2 = 812500;
  94                } else {
  95                        *val = 3;
  96                        *val2 = 906250;
  97                }
  98                return IIO_VAL_INT_PLUS_MICRO;
  99        case IIO_CHAN_INFO_OFFSET:
 100                if (chan->type == IIO_TEMP)
 101                        *val = -50 * 32 * 4;
 102                else
 103                        *val = -24 * 16 * 16;
 104                return IIO_VAL_INT;
 105        default:
 106                break;
 107        }
 108
 109        return -EINVAL;
 110}
 111
 112static const struct iio_chan_spec si7005_channels[] = {
 113        {
 114                .type = IIO_HUMIDITYRELATIVE,
 115                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 116                        BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 117        },
 118        {
 119                .type = IIO_TEMP,
 120                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 121                        BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
 122        }
 123};
 124
 125static const struct iio_info si7005_info = {
 126        .read_raw = si7005_read_raw,
 127        .driver_module = THIS_MODULE,
 128};
 129
 130static int si7005_probe(struct i2c_client *client,
 131                         const struct i2c_device_id *id)
 132{
 133        struct iio_dev *indio_dev;
 134        struct si7005_data *data;
 135        int ret;
 136
 137        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
 138                return -EOPNOTSUPP;
 139
 140        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 141        if (!indio_dev)
 142                return -ENOMEM;
 143
 144        data = iio_priv(indio_dev);
 145        i2c_set_clientdata(client, indio_dev);
 146        data->client = client;
 147        mutex_init(&data->lock);
 148
 149        indio_dev->dev.parent = &client->dev;
 150        indio_dev->name = dev_name(&client->dev);
 151        indio_dev->modes = INDIO_DIRECT_MODE;
 152        indio_dev->info = &si7005_info;
 153
 154        indio_dev->channels = si7005_channels;
 155        indio_dev->num_channels = ARRAY_SIZE(si7005_channels);
 156
 157        ret = i2c_smbus_read_byte_data(client, SI7005_ID);
 158        if (ret < 0)
 159                return ret;
 160        if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015)
 161                return -ENODEV;
 162
 163        ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG);
 164        if (ret < 0)
 165                return ret;
 166        data->config = ret;
 167
 168        return devm_iio_device_register(&client->dev, indio_dev);
 169}
 170
 171static const struct i2c_device_id si7005_id[] = {
 172        { "si7005", 0 },
 173        { "th02", 0 },
 174        { }
 175};
 176MODULE_DEVICE_TABLE(i2c, si7005_id);
 177
 178static struct i2c_driver si7005_driver = {
 179        .driver = {
 180                .name   = "si7005",
 181        },
 182        .probe = si7005_probe,
 183        .id_table = si7005_id,
 184};
 185module_i2c_driver(si7005_driver);
 186
 187MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 188MODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver");
 189MODULE_LICENSE("GPL");
 190