linux/drivers/iio/temperature/tmp006.c
<<
>>
Prefs
   1/*
   2 * tmp006.c - Support for TI TMP006 IR thermopile sensor
   3 *
   4 * Copyright (c) 2013 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 * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
  11 *
  12 * (7-bit I2C slave address 0x40, changeable via ADR pins)
  13 *
  14 * TODO: data ready irq
  15 */
  16
  17#include <linux/err.h>
  18#include <linux/i2c.h>
  19#include <linux/delay.h>
  20#include <linux/module.h>
  21#include <linux/pm.h>
  22#include <linux/bitops.h>
  23
  24#include <linux/iio/iio.h>
  25#include <linux/iio/sysfs.h>
  26
  27#define TMP006_VOBJECT 0x00
  28#define TMP006_TAMBIENT 0x01
  29#define TMP006_CONFIG 0x02
  30#define TMP006_MANUFACTURER_ID 0xfe
  31#define TMP006_DEVICE_ID 0xff
  32
  33#define TMP006_TAMBIENT_SHIFT 2
  34
  35#define TMP006_CONFIG_RESET BIT(15)
  36#define TMP006_CONFIG_DRDY_EN BIT(8)
  37#define TMP006_CONFIG_DRDY BIT(7)
  38
  39#define TMP006_CONFIG_MOD_MASK GENMASK(14, 12)
  40
  41#define TMP006_CONFIG_CR_MASK GENMASK(11, 9)
  42#define TMP006_CONFIG_CR_SHIFT 9
  43
  44#define TMP006_MANUFACTURER_MAGIC 0x5449
  45#define TMP006_DEVICE_MAGIC 0x0067
  46
  47struct tmp006_data {
  48        struct i2c_client *client;
  49        u16 config;
  50};
  51
  52static int tmp006_read_measurement(struct tmp006_data *data, u8 reg)
  53{
  54        s32 ret;
  55        int tries = 50;
  56
  57        while (tries-- > 0) {
  58                ret = i2c_smbus_read_word_swapped(data->client,
  59                        TMP006_CONFIG);
  60                if (ret < 0)
  61                        return ret;
  62                if (ret & TMP006_CONFIG_DRDY)
  63                        break;
  64                msleep(100);
  65        }
  66
  67        if (tries < 0)
  68                return -EIO;
  69
  70        return i2c_smbus_read_word_swapped(data->client, reg);
  71}
  72
  73static const int tmp006_freqs[5][2] = { {4, 0}, {2, 0}, {1, 0},
  74                                        {0, 500000}, {0, 250000} };
  75
  76static int tmp006_read_raw(struct iio_dev *indio_dev,
  77                            struct iio_chan_spec const *channel, int *val,
  78                            int *val2, long mask)
  79{
  80        struct tmp006_data *data = iio_priv(indio_dev);
  81        s32 ret;
  82        int cr;
  83
  84        switch (mask) {
  85        case IIO_CHAN_INFO_RAW:
  86                if (channel->type == IIO_VOLTAGE) {
  87                        /* LSB is 156.25 nV */
  88                        ret = tmp006_read_measurement(data, TMP006_VOBJECT);
  89                        if (ret < 0)
  90                                return ret;
  91                        *val = sign_extend32(ret, 15);
  92                } else if (channel->type == IIO_TEMP) {
  93                        /* LSB is 0.03125 degrees Celsius */
  94                        ret = tmp006_read_measurement(data, TMP006_TAMBIENT);
  95                        if (ret < 0)
  96                                return ret;
  97                        *val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT;
  98                } else {
  99                        break;
 100                }
 101                return IIO_VAL_INT;
 102        case IIO_CHAN_INFO_SCALE:
 103                if (channel->type == IIO_VOLTAGE) {
 104                        *val = 0;
 105                        *val2 = 156250;
 106                } else if (channel->type == IIO_TEMP) {
 107                        *val = 31;
 108                        *val2 = 250000;
 109                } else {
 110                        break;
 111                }
 112                return IIO_VAL_INT_PLUS_MICRO;
 113        case IIO_CHAN_INFO_SAMP_FREQ:
 114                cr = (data->config & TMP006_CONFIG_CR_MASK)
 115                        >> TMP006_CONFIG_CR_SHIFT;
 116                *val = tmp006_freqs[cr][0];
 117                *val2 = tmp006_freqs[cr][1];
 118                return IIO_VAL_INT_PLUS_MICRO;
 119        default:
 120                break;
 121        }
 122
 123        return -EINVAL;
 124}
 125
 126static int tmp006_write_raw(struct iio_dev *indio_dev,
 127                            struct iio_chan_spec const *chan,
 128                            int val,
 129                            int val2,
 130                            long mask)
 131{
 132        struct tmp006_data *data = iio_priv(indio_dev);
 133        int i;
 134
 135        if (mask != IIO_CHAN_INFO_SAMP_FREQ)
 136                return -EINVAL;
 137
 138        for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++)
 139                if ((val == tmp006_freqs[i][0]) &&
 140                    (val2 == tmp006_freqs[i][1])) {
 141                        data->config &= ~TMP006_CONFIG_CR_MASK;
 142                        data->config |= i << TMP006_CONFIG_CR_SHIFT;
 143
 144                        return i2c_smbus_write_word_swapped(data->client,
 145                                                            TMP006_CONFIG,
 146                                                            data->config);
 147
 148                }
 149        return -EINVAL;
 150}
 151
 152static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
 153
 154static struct attribute *tmp006_attributes[] = {
 155        &iio_const_attr_sampling_frequency_available.dev_attr.attr,
 156        NULL
 157};
 158
 159static const struct attribute_group tmp006_attribute_group = {
 160        .attrs = tmp006_attributes,
 161};
 162
 163static const struct iio_chan_spec tmp006_channels[] = {
 164        {
 165                .type = IIO_VOLTAGE,
 166                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 167                        BIT(IIO_CHAN_INFO_SCALE),
 168                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 169        },
 170        {
 171                .type = IIO_TEMP,
 172                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 173                        BIT(IIO_CHAN_INFO_SCALE),
 174                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 175        }
 176};
 177
 178static const struct iio_info tmp006_info = {
 179        .read_raw = tmp006_read_raw,
 180        .write_raw = tmp006_write_raw,
 181        .attrs = &tmp006_attribute_group,
 182        .driver_module = THIS_MODULE,
 183};
 184
 185static bool tmp006_check_identification(struct i2c_client *client)
 186{
 187        int mid, did;
 188
 189        mid = i2c_smbus_read_word_swapped(client, TMP006_MANUFACTURER_ID);
 190        if (mid < 0)
 191                return false;
 192
 193        did = i2c_smbus_read_word_swapped(client, TMP006_DEVICE_ID);
 194        if (did < 0)
 195                return false;
 196
 197        return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
 198}
 199
 200static int tmp006_probe(struct i2c_client *client,
 201                         const struct i2c_device_id *id)
 202{
 203        struct iio_dev *indio_dev;
 204        struct tmp006_data *data;
 205        int ret;
 206
 207        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
 208                return -ENODEV;
 209
 210        if (!tmp006_check_identification(client)) {
 211                dev_err(&client->dev, "no TMP006 sensor\n");
 212                return -ENODEV;
 213        }
 214
 215        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 216        if (!indio_dev)
 217                return -ENOMEM;
 218
 219        data = iio_priv(indio_dev);
 220        i2c_set_clientdata(client, indio_dev);
 221        data->client = client;
 222
 223        indio_dev->dev.parent = &client->dev;
 224        indio_dev->name = dev_name(&client->dev);
 225        indio_dev->modes = INDIO_DIRECT_MODE;
 226        indio_dev->info = &tmp006_info;
 227
 228        indio_dev->channels = tmp006_channels;
 229        indio_dev->num_channels = ARRAY_SIZE(tmp006_channels);
 230
 231        ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG);
 232        if (ret < 0)
 233                return ret;
 234        data->config = ret;
 235
 236        return iio_device_register(indio_dev);
 237}
 238
 239static int tmp006_powerdown(struct tmp006_data *data)
 240{
 241        return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
 242                data->config & ~TMP006_CONFIG_MOD_MASK);
 243}
 244
 245static int tmp006_remove(struct i2c_client *client)
 246{
 247        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 248
 249        iio_device_unregister(indio_dev);
 250        tmp006_powerdown(iio_priv(indio_dev));
 251
 252        return 0;
 253}
 254
 255#ifdef CONFIG_PM_SLEEP
 256static int tmp006_suspend(struct device *dev)
 257{
 258        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 259        return tmp006_powerdown(iio_priv(indio_dev));
 260}
 261
 262static int tmp006_resume(struct device *dev)
 263{
 264        struct tmp006_data *data = iio_priv(i2c_get_clientdata(
 265                to_i2c_client(dev)));
 266        return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
 267                data->config | TMP006_CONFIG_MOD_MASK);
 268}
 269#endif
 270
 271static SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume);
 272
 273static const struct i2c_device_id tmp006_id[] = {
 274        { "tmp006", 0 },
 275        { }
 276};
 277MODULE_DEVICE_TABLE(i2c, tmp006_id);
 278
 279static struct i2c_driver tmp006_driver = {
 280        .driver = {
 281                .name   = "tmp006",
 282                .pm     = &tmp006_pm_ops,
 283        },
 284        .probe = tmp006_probe,
 285        .remove = tmp006_remove,
 286        .id_table = tmp006_id,
 287};
 288module_i2c_driver(tmp006_driver);
 289
 290MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 291MODULE_DESCRIPTION("TI TMP006 IR thermopile sensor driver");
 292MODULE_LICENSE("GPL");
 293