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