linux/drivers/hwmon/adt7411.c
<<
>>
Prefs
   1/*
   2 *  Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor)
   3 *
   4 *  Copyright (C) 2008, 2010 Pengutronix
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License version 2 as
   8 *  published by the Free Software Foundation.
   9 *
  10 *  TODO: SPI, support for external temperature sensor
  11 *        use power-down mode for suspend?, interrupt handling?
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/err.h>
  18#include <linux/delay.h>
  19#include <linux/mutex.h>
  20#include <linux/jiffies.h>
  21#include <linux/i2c.h>
  22#include <linux/hwmon.h>
  23#include <linux/hwmon-sysfs.h>
  24#include <linux/slab.h>
  25
  26#define ADT7411_REG_INT_TEMP_VDD_LSB            0x03
  27#define ADT7411_REG_EXT_TEMP_AIN14_LSB          0x04
  28#define ADT7411_REG_VDD_MSB                     0x06
  29#define ADT7411_REG_INT_TEMP_MSB                0x07
  30#define ADT7411_REG_EXT_TEMP_AIN1_MSB           0x08
  31
  32#define ADT7411_REG_CFG1                        0x18
  33#define ADT7411_CFG1_START_MONITOR              (1 << 0)
  34
  35#define ADT7411_REG_CFG2                        0x19
  36#define ADT7411_CFG2_DISABLE_AVG                (1 << 5)
  37
  38#define ADT7411_REG_CFG3                        0x1a
  39#define ADT7411_CFG3_ADC_CLK_225                (1 << 0)
  40#define ADT7411_CFG3_REF_VDD                    (1 << 4)
  41
  42#define ADT7411_REG_DEVICE_ID                   0x4d
  43#define ADT7411_REG_MANUFACTURER_ID             0x4e
  44
  45#define ADT7411_DEVICE_ID                       0x2
  46#define ADT7411_MANUFACTURER_ID                 0x41
  47
  48static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END };
  49
  50struct adt7411_data {
  51        struct mutex device_lock;       /* for "atomic" device accesses */
  52        struct mutex update_lock;
  53        unsigned long next_update;
  54        int vref_cached;
  55        struct device *hwmon_dev;
  56};
  57
  58/*
  59 * When reading a register containing (up to 4) lsb, all associated
  60 * msb-registers get locked by the hardware. After _one_ of those msb is read,
  61 * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb
  62 * is protected here with a mutex, too.
  63 */
  64static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg,
  65                                u8 msb_reg, u8 lsb_shift)
  66{
  67        struct adt7411_data *data = i2c_get_clientdata(client);
  68        int val, tmp;
  69
  70        mutex_lock(&data->device_lock);
  71
  72        val = i2c_smbus_read_byte_data(client, lsb_reg);
  73        if (val < 0)
  74                goto exit_unlock;
  75
  76        tmp = (val >> lsb_shift) & 3;
  77        val = i2c_smbus_read_byte_data(client, msb_reg);
  78
  79        if (val >= 0)
  80                val = (val << 2) | tmp;
  81
  82 exit_unlock:
  83        mutex_unlock(&data->device_lock);
  84
  85        return val;
  86}
  87
  88static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit,
  89                                bool flag)
  90{
  91        struct adt7411_data *data = i2c_get_clientdata(client);
  92        int ret, val;
  93
  94        mutex_lock(&data->device_lock);
  95
  96        ret = i2c_smbus_read_byte_data(client, reg);
  97        if (ret < 0)
  98                goto exit_unlock;
  99
 100        if (flag)
 101                val = ret | bit;
 102        else
 103                val = ret & ~bit;
 104
 105        ret = i2c_smbus_write_byte_data(client, reg, val);
 106
 107 exit_unlock:
 108        mutex_unlock(&data->device_lock);
 109        return ret;
 110}
 111
 112static ssize_t adt7411_show_vdd(struct device *dev,
 113                                struct device_attribute *attr, char *buf)
 114{
 115        struct i2c_client *client = to_i2c_client(dev);
 116        int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
 117                        ADT7411_REG_VDD_MSB, 2);
 118
 119        return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024);
 120}
 121
 122static ssize_t adt7411_show_temp(struct device *dev,
 123                        struct device_attribute *attr, char *buf)
 124{
 125        struct i2c_client *client = to_i2c_client(dev);
 126        int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
 127                        ADT7411_REG_INT_TEMP_MSB, 0);
 128
 129        if (val < 0)
 130                return val;
 131
 132        val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */
 133
 134        return sprintf(buf, "%d\n", val * 250);
 135}
 136
 137static ssize_t adt7411_show_input(struct device *dev,
 138                                  struct device_attribute *attr, char *buf)
 139{
 140        int nr = to_sensor_dev_attr(attr)->index;
 141        struct i2c_client *client = to_i2c_client(dev);
 142        struct adt7411_data *data = i2c_get_clientdata(client);
 143        int val;
 144        u8 lsb_reg, lsb_shift;
 145
 146        mutex_lock(&data->update_lock);
 147        if (time_after_eq(jiffies, data->next_update)) {
 148                val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
 149                if (val < 0)
 150                        goto exit_unlock;
 151
 152                if (val & ADT7411_CFG3_REF_VDD) {
 153                        val = adt7411_read_10_bit(client,
 154                                        ADT7411_REG_INT_TEMP_VDD_LSB,
 155                                        ADT7411_REG_VDD_MSB, 2);
 156                        if (val < 0)
 157                                goto exit_unlock;
 158
 159                        data->vref_cached = val * 7000 / 1024;
 160                } else {
 161                        data->vref_cached = 2250;
 162                }
 163
 164                data->next_update = jiffies + HZ;
 165        }
 166
 167        lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
 168        lsb_shift = 2 * (nr & 0x03);
 169        val = adt7411_read_10_bit(client, lsb_reg,
 170                        ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift);
 171        if (val < 0)
 172                goto exit_unlock;
 173
 174        val = sprintf(buf, "%u\n", val * data->vref_cached / 1024);
 175 exit_unlock:
 176        mutex_unlock(&data->update_lock);
 177        return val;
 178}
 179
 180static ssize_t adt7411_show_bit(struct device *dev,
 181                                struct device_attribute *attr, char *buf)
 182{
 183        struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
 184        struct i2c_client *client = to_i2c_client(dev);
 185        int ret = i2c_smbus_read_byte_data(client, attr2->index);
 186
 187        return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr));
 188}
 189
 190static ssize_t adt7411_set_bit(struct device *dev,
 191                               struct device_attribute *attr, const char *buf,
 192                               size_t count)
 193{
 194        struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr);
 195        struct i2c_client *client = to_i2c_client(dev);
 196        struct adt7411_data *data = i2c_get_clientdata(client);
 197        int ret;
 198        unsigned long flag;
 199
 200        ret = kstrtoul(buf, 0, &flag);
 201        if (ret || flag > 1)
 202                return -EINVAL;
 203
 204        ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
 205
 206        /* force update */
 207        mutex_lock(&data->update_lock);
 208        data->next_update = jiffies;
 209        mutex_unlock(&data->update_lock);
 210
 211        return ret < 0 ? ret : count;
 212}
 213
 214#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
 215        SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
 216        adt7411_set_bit, __bit, __reg)
 217
 218static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL);
 219static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
 220static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
 221static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
 222static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2);
 223static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3);
 224static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4);
 225static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5);
 226static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6);
 227static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7);
 228static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG);
 229static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225);
 230static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
 231
 232static struct attribute *adt7411_attrs[] = {
 233        &dev_attr_temp1_input.attr,
 234        &dev_attr_in0_input.attr,
 235        &sensor_dev_attr_in1_input.dev_attr.attr,
 236        &sensor_dev_attr_in2_input.dev_attr.attr,
 237        &sensor_dev_attr_in3_input.dev_attr.attr,
 238        &sensor_dev_attr_in4_input.dev_attr.attr,
 239        &sensor_dev_attr_in5_input.dev_attr.attr,
 240        &sensor_dev_attr_in6_input.dev_attr.attr,
 241        &sensor_dev_attr_in7_input.dev_attr.attr,
 242        &sensor_dev_attr_in8_input.dev_attr.attr,
 243        &sensor_dev_attr_no_average.dev_attr.attr,
 244        &sensor_dev_attr_fast_sampling.dev_attr.attr,
 245        &sensor_dev_attr_adc_ref_vdd.dev_attr.attr,
 246        NULL
 247};
 248
 249static const struct attribute_group adt7411_attr_grp = {
 250        .attrs = adt7411_attrs,
 251};
 252
 253static int adt7411_detect(struct i2c_client *client,
 254                          struct i2c_board_info *info)
 255{
 256        int val;
 257
 258        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 259                return -ENODEV;
 260
 261        val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID);
 262        if (val < 0 || val != ADT7411_MANUFACTURER_ID) {
 263                dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, "
 264                        "expected %d\n", val, ADT7411_MANUFACTURER_ID);
 265                return -ENODEV;
 266        }
 267
 268        val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID);
 269        if (val < 0 || val != ADT7411_DEVICE_ID) {
 270                dev_dbg(&client->dev, "Wrong device ID. Got %d, "
 271                        "expected %d\n", val, ADT7411_DEVICE_ID);
 272                return -ENODEV;
 273        }
 274
 275        strlcpy(info->type, "adt7411", I2C_NAME_SIZE);
 276
 277        return 0;
 278}
 279
 280static int __devinit adt7411_probe(struct i2c_client *client,
 281                                   const struct i2c_device_id *id)
 282{
 283        struct adt7411_data *data;
 284        int ret;
 285
 286        data = kzalloc(sizeof(*data), GFP_KERNEL);
 287        if (!data)
 288                return -ENOMEM;
 289
 290        i2c_set_clientdata(client, data);
 291        mutex_init(&data->device_lock);
 292        mutex_init(&data->update_lock);
 293
 294        ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
 295                                 ADT7411_CFG1_START_MONITOR, 1);
 296        if (ret < 0)
 297                goto exit_free;
 298
 299        /* force update on first occasion */
 300        data->next_update = jiffies;
 301
 302        ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp);
 303        if (ret)
 304                goto exit_free;
 305
 306        data->hwmon_dev = hwmon_device_register(&client->dev);
 307        if (IS_ERR(data->hwmon_dev)) {
 308                ret = PTR_ERR(data->hwmon_dev);
 309                goto exit_remove;
 310        }
 311
 312        dev_info(&client->dev, "successfully registered\n");
 313
 314        return 0;
 315
 316 exit_remove:
 317        sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
 318 exit_free:
 319        kfree(data);
 320        return ret;
 321}
 322
 323static int __devexit adt7411_remove(struct i2c_client *client)
 324{
 325        struct adt7411_data *data = i2c_get_clientdata(client);
 326
 327        hwmon_device_unregister(data->hwmon_dev);
 328        sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
 329        kfree(data);
 330        return 0;
 331}
 332
 333static const struct i2c_device_id adt7411_id[] = {
 334        { "adt7411", 0 },
 335        { }
 336};
 337MODULE_DEVICE_TABLE(i2c, adt7411_id);
 338
 339static struct i2c_driver adt7411_driver = {
 340        .driver         = {
 341                .name           = "adt7411",
 342        },
 343        .probe  = adt7411_probe,
 344        .remove = __devexit_p(adt7411_remove),
 345        .id_table = adt7411_id,
 346        .detect = adt7411_detect,
 347        .address_list = normal_i2c,
 348        .class = I2C_CLASS_HWMON,
 349};
 350
 351module_i2c_driver(adt7411_driver);
 352
 353MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and "
 354        "Wolfram Sang <w.sang@pengutronix.de>");
 355MODULE_DESCRIPTION("ADT7411 driver");
 356MODULE_LICENSE("GPL v2");
 357
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.