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 0x7000
  40
  41#define TMP006_CONFIG_CR_MASK 0x0e00
  42#define TMP006_CONFIG_CR_SHIFT 9
  43
  44#define MANUFACTURER_MAGIC 0x5449
  45#define 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        for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++)
 136                if ((val == tmp006_freqs[i][0]) &&
 137                    (val2 == tmp006_freqs[i][1])) {
 138                        data->config &= ~TMP006_CONFIG_CR_MASK;
 139                        data->config |= i << TMP006_CONFIG_CR_SHIFT;
 140
 141                        return i2c_smbus_write_word_swapped(data->client,
 142                                                            TMP006_CONFIG,
 143                                                            data->config);
 144
 145                }
 146        return -EINVAL;
 147}
 148
 149static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
 150
 151static struct attribute *tmp006_attributes[] = {
 152        &iio_const_attr_sampling_frequency_available.dev_attr.attr,
 153        NULL
 154};
 155
 156static const struct attribute_group tmp006_attribute_group = {
 157        .attrs = tmp006_attributes,
 158};
 159
 160static const struct iio_chan_spec tmp006_channels[] = {
 161        {
 162                .type = IIO_VOLTAGE,
 163                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 164                        BIT(IIO_CHAN_INFO_SCALE),
 165                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 166        },
 167        {
 168                .type = IIO_TEMP,
 169                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 170                        BIT(IIO_CHAN_INFO_SCALE),
 171                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 172        }
 173};
 174
 175static const struct iio_info tmp006_info = {
 176        .read_raw = tmp006_read_raw,
 177        .write_raw = tmp006_write_raw,
 178        .attrs = &tmp006_attribute_group,
 179        .driver_module = THIS_MODULE,
 180};
 181
 182static bool tmp006_check_identification(struct i2c_client *client)
 183{
 184        int mid, did;
 185
 186        mid = i2c_smbus_read_word_swapped(client, TMP006_MANUFACTURER_ID);
 187        if (mid < 0)
 188                return false;
 189
 190        did = i2c_smbus_read_word_swapped(client, TMP006_DEVICE_ID);
 191        if (did < 0)
 192                return false;
 193
 194        return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
 195}
 196
 197static int tmp006_probe(struct i2c_client *client,
 198                         const struct i2c_device_id *id)
 199{
 200        struct iio_dev *indio_dev;
 201        struct tmp006_data *data;
 202        int ret;
 203
 204        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
 205                return -ENODEV;
 206
 207        if (!tmp006_check_identification(client)) {
 208                dev_err(&client->dev, "no TMP006 sensor\n");
 209                return -ENODEV;
 210        }
 211
 212        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 213        if (!indio_dev)
 214                return -ENOMEM;
 215
 216        data = iio_priv(indio_dev);
 217        i2c_set_clientdata(client, indio_dev);
 218        data->client = client;
 219
 220        indio_dev->dev.parent = &client->dev;
 221        indio_dev->name = dev_name(&client->dev);
 222        indio_dev->modes = INDIO_DIRECT_MODE;
 223        indio_dev->info = &tmp006_info;
 224
 225        indio_dev->channels = tmp006_channels;
 226        indio_dev->num_channels = ARRAY_SIZE(tmp006_channels);
 227
 228        ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG);
 229        if (ret < 0)
 230                return ret;
 231        data->config = ret;
 232
 233        return iio_device_register(indio_dev);
 234}
 235
 236static int tmp006_powerdown(struct tmp006_data *data)
 237{
 238        return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
 239                data->config & ~TMP006_CONFIG_MOD_MASK);
 240}
 241
 242static int tmp006_remove(struct i2c_client *client)
 243{
 244        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 245
 246        iio_device_unregister(indio_dev);
 247        tmp006_powerdown(iio_priv(indio_dev));
 248
 249        return 0;
 250}
 251
 252#ifdef CONFIG_PM_SLEEP
 253static int tmp006_suspend(struct device *dev)
 254{
 255        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 256        return tmp006_powerdown(iio_priv(indio_dev));
 257}
 258
 259static int tmp006_resume(struct device *dev)
 260{
 261        struct tmp006_data *data = iio_priv(i2c_get_clientdata(
 262                to_i2c_client(dev)));
 263        return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
 264                data->config | TMP006_CONFIG_MOD_MASK);
 265}
 266#endif
 267
 268static SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume);
 269
 270static const struct i2c_device_id tmp006_id[] = {
 271        { "tmp006", 0 },
 272        { }
 273};
 274MODULE_DEVICE_TABLE(i2c, tmp006_id);
 275
 276static struct i2c_driver tmp006_driver = {
 277        .driver = {
 278                .name   = "tmp006",
 279                .pm     = &tmp006_pm_ops,
 280                .owner  = THIS_MODULE,
 281        },
 282        .probe = tmp006_probe,
 283        .remove = tmp006_remove,
 284        .id_table = tmp006_id,
 285};
 286module_i2c_driver(tmp006_driver);
 287
 288MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 289MODULE_DESCRIPTION("TI TMP006 IR thermopile sensor driver");
 290MODULE_LICENSE("GPL");
 291