linux/drivers/hwmon/lm77.c
<<
>>
Prefs
   1/*
   2    lm77.c - Part of lm_sensors, Linux kernel modules for hardware
   3             monitoring
   4
   5    Copyright (c) 2004  Andras BALI <drewie@freemail.hu>
   6
   7    Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>.  The LM77
   8    is a temperature sensor and thermal window comparator with 0.5 deg
   9    resolution made by National Semiconductor.  Complete datasheet can be
  10    obtained at their site:
  11       http://www.national.com/pf/LM/LM77.html
  12
  13    This program is free software; you can redistribute it and/or modify
  14    it under the terms of the GNU General Public License as published by
  15    the Free Software Foundation; either version 2 of the License, or
  16    (at your option) any later version.
  17
  18    This program is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22
  23    You should have received a copy of the GNU General Public License
  24    along with this program; if not, write to the Free Software
  25    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26*/
  27
  28#include <linux/module.h>
  29#include <linux/init.h>
  30#include <linux/slab.h>
  31#include <linux/jiffies.h>
  32#include <linux/i2c.h>
  33#include <linux/hwmon.h>
  34#include <linux/hwmon-sysfs.h>
  35#include <linux/err.h>
  36#include <linux/mutex.h>
  37
  38/* Addresses to scan */
  39static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
  40                                                I2C_CLIENT_END };
  41
  42/* The LM77 registers */
  43#define LM77_REG_TEMP           0x00
  44#define LM77_REG_CONF           0x01
  45#define LM77_REG_TEMP_HYST      0x02
  46#define LM77_REG_TEMP_CRIT      0x03
  47#define LM77_REG_TEMP_MIN       0x04
  48#define LM77_REG_TEMP_MAX       0x05
  49
  50/* Each client has this additional data */
  51struct lm77_data {
  52        struct device           *hwmon_dev;
  53        struct mutex            update_lock;
  54        char                    valid;
  55        unsigned long           last_updated;   /* In jiffies */
  56        int                     temp_input;     /* Temperatures */
  57        int                     temp_crit;
  58        int                     temp_min;
  59        int                     temp_max;
  60        int                     temp_hyst;
  61        u8                      alarms;
  62};
  63
  64static int lm77_probe(struct i2c_client *client,
  65                      const struct i2c_device_id *id);
  66static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info);
  67static void lm77_init_client(struct i2c_client *client);
  68static int lm77_remove(struct i2c_client *client);
  69static u16 lm77_read_value(struct i2c_client *client, u8 reg);
  70static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
  71
  72static struct lm77_data *lm77_update_device(struct device *dev);
  73
  74
  75static const struct i2c_device_id lm77_id[] = {
  76        { "lm77", 0 },
  77        { }
  78};
  79MODULE_DEVICE_TABLE(i2c, lm77_id);
  80
  81/* This is the driver that will be inserted */
  82static struct i2c_driver lm77_driver = {
  83        .class          = I2C_CLASS_HWMON,
  84        .driver = {
  85                .name   = "lm77",
  86        },
  87        .probe          = lm77_probe,
  88        .remove         = lm77_remove,
  89        .id_table       = lm77_id,
  90        .detect         = lm77_detect,
  91        .address_list   = normal_i2c,
  92};
  93
  94/* straight from the datasheet */
  95#define LM77_TEMP_MIN (-55000)
  96#define LM77_TEMP_MAX 125000
  97
  98/* In the temperature registers, the low 3 bits are not part of the
  99   temperature values; they are the status bits. */
 100static inline s16 LM77_TEMP_TO_REG(int temp)
 101{
 102        int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX);
 103        return (ntemp / 500) * 8;
 104}
 105
 106static inline int LM77_TEMP_FROM_REG(s16 reg)
 107{
 108        return (reg / 8) * 500;
 109}
 110
 111/* sysfs stuff */
 112
 113/* read routines for temperature limits */
 114#define show(value)     \
 115static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf)       \
 116{                                                               \
 117        struct lm77_data *data = lm77_update_device(dev);       \
 118        return sprintf(buf, "%d\n", data->value);               \
 119}
 120
 121show(temp_input);
 122show(temp_crit);
 123show(temp_min);
 124show(temp_max);
 125
 126/* read routines for hysteresis values */
 127static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf)
 128{
 129        struct lm77_data *data = lm77_update_device(dev);
 130        return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
 131}
 132static ssize_t show_temp_min_hyst(struct device *dev, struct device_attribute *attr, char *buf)
 133{
 134        struct lm77_data *data = lm77_update_device(dev);
 135        return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
 136}
 137static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *attr, char *buf)
 138{
 139        struct lm77_data *data = lm77_update_device(dev);
 140        return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
 141}
 142
 143/* write routines */
 144#define set(value, reg) \
 145static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)    \
 146{                                                                               \
 147        struct i2c_client *client = to_i2c_client(dev);                         \
 148        struct lm77_data *data = i2c_get_clientdata(client);                    \
 149        long val = simple_strtol(buf, NULL, 10);                                \
 150                                                                                \
 151        mutex_lock(&data->update_lock);                                         \
 152        data->value = val;                              \
 153        lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value));           \
 154        mutex_unlock(&data->update_lock);                                       \
 155        return count;                                                           \
 156}
 157
 158set(temp_min, LM77_REG_TEMP_MIN);
 159set(temp_max, LM77_REG_TEMP_MAX);
 160
 161/* hysteresis is stored as a relative value on the chip, so it has to be
 162   converted first */
 163static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 164{
 165        struct i2c_client *client = to_i2c_client(dev);
 166        struct lm77_data *data = i2c_get_clientdata(client);
 167        unsigned long val = simple_strtoul(buf, NULL, 10);
 168
 169        mutex_lock(&data->update_lock);
 170        data->temp_hyst = data->temp_crit - val;
 171        lm77_write_value(client, LM77_REG_TEMP_HYST,
 172                         LM77_TEMP_TO_REG(data->temp_hyst));
 173        mutex_unlock(&data->update_lock);
 174        return count;
 175}
 176
 177/* preserve hysteresis when setting T_crit */
 178static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 179{
 180        struct i2c_client *client = to_i2c_client(dev);
 181        struct lm77_data *data = i2c_get_clientdata(client);
 182        long val = simple_strtoul(buf, NULL, 10);
 183        int oldcrithyst;
 184        
 185        mutex_lock(&data->update_lock);
 186        oldcrithyst = data->temp_crit - data->temp_hyst;
 187        data->temp_crit = val;
 188        data->temp_hyst = data->temp_crit - oldcrithyst;
 189        lm77_write_value(client, LM77_REG_TEMP_CRIT,
 190                         LM77_TEMP_TO_REG(data->temp_crit));
 191        lm77_write_value(client, LM77_REG_TEMP_HYST,
 192                         LM77_TEMP_TO_REG(data->temp_hyst));
 193        mutex_unlock(&data->update_lock);
 194        return count;
 195}
 196
 197static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
 198                          char *buf)
 199{
 200        int bitnr = to_sensor_dev_attr(attr)->index;
 201        struct lm77_data *data = lm77_update_device(dev);
 202        return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
 203}
 204
 205static DEVICE_ATTR(temp1_input, S_IRUGO,
 206                   show_temp_input, NULL);
 207static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
 208                   show_temp_crit, set_temp_crit);
 209static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
 210                   show_temp_min, set_temp_min);
 211static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
 212                   show_temp_max, set_temp_max);
 213
 214static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
 215                   show_temp_crit_hyst, set_temp_crit_hyst);
 216static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
 217                   show_temp_min_hyst, NULL);
 218static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
 219                   show_temp_max_hyst, NULL);
 220
 221static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2);
 222static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
 223static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1);
 224
 225static struct attribute *lm77_attributes[] = {
 226        &dev_attr_temp1_input.attr,
 227        &dev_attr_temp1_crit.attr,
 228        &dev_attr_temp1_min.attr,
 229        &dev_attr_temp1_max.attr,
 230        &dev_attr_temp1_crit_hyst.attr,
 231        &dev_attr_temp1_min_hyst.attr,
 232        &dev_attr_temp1_max_hyst.attr,
 233        &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
 234        &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
 235        &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
 236        NULL
 237};
 238
 239static const struct attribute_group lm77_group = {
 240        .attrs = lm77_attributes,
 241};
 242
 243/* Return 0 if detection is successful, -ENODEV otherwise */
 244static int lm77_detect(struct i2c_client *new_client,
 245                       struct i2c_board_info *info)
 246{
 247        struct i2c_adapter *adapter = new_client->adapter;
 248        int i, cur, conf, hyst, crit, min, max;
 249
 250        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
 251                                     I2C_FUNC_SMBUS_WORD_DATA))
 252                return -ENODEV;
 253
 254        /* Here comes the remaining detection.  Since the LM77 has no
 255           register dedicated to identification, we have to rely on the
 256           following tricks:
 257
 258           1. the high 4 bits represent the sign and thus they should
 259              always be the same
 260           2. the high 3 bits are unused in the configuration register
 261           3. addresses 0x06 and 0x07 return the last read value
 262           4. registers cycling over 8-address boundaries
 263
 264           Word-sized registers are high-byte first. */
 265
 266        /* addresses cycling */
 267        cur = i2c_smbus_read_word_data(new_client, 0);
 268        conf = i2c_smbus_read_byte_data(new_client, 1);
 269        hyst = i2c_smbus_read_word_data(new_client, 2);
 270        crit = i2c_smbus_read_word_data(new_client, 3);
 271        min = i2c_smbus_read_word_data(new_client, 4);
 272        max = i2c_smbus_read_word_data(new_client, 5);
 273        for (i = 8; i <= 0xff; i += 8) {
 274                if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
 275                 || i2c_smbus_read_word_data(new_client, i + 2) != hyst
 276                 || i2c_smbus_read_word_data(new_client, i + 3) != crit
 277                 || i2c_smbus_read_word_data(new_client, i + 4) != min
 278                 || i2c_smbus_read_word_data(new_client, i + 5) != max)
 279                        return -ENODEV;
 280        }
 281
 282        /* sign bits */
 283        if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
 284         || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
 285         || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
 286         || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
 287         || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
 288                return -ENODEV;
 289
 290        /* unused bits */
 291        if (conf & 0xe0)
 292                return -ENODEV;
 293
 294        /* 0x06 and 0x07 return the last read value */
 295        cur = i2c_smbus_read_word_data(new_client, 0);
 296        if (i2c_smbus_read_word_data(new_client, 6) != cur
 297         || i2c_smbus_read_word_data(new_client, 7) != cur)
 298                return -ENODEV;
 299        hyst = i2c_smbus_read_word_data(new_client, 2);
 300        if (i2c_smbus_read_word_data(new_client, 6) != hyst
 301         || i2c_smbus_read_word_data(new_client, 7) != hyst)
 302                return -ENODEV;
 303        min = i2c_smbus_read_word_data(new_client, 4);
 304        if (i2c_smbus_read_word_data(new_client, 6) != min
 305         || i2c_smbus_read_word_data(new_client, 7) != min)
 306                return -ENODEV;
 307
 308        strlcpy(info->type, "lm77", I2C_NAME_SIZE);
 309
 310        return 0;
 311}
 312
 313static int lm77_probe(struct i2c_client *new_client,
 314                      const struct i2c_device_id *id)
 315{
 316        struct lm77_data *data;
 317        int err;
 318
 319        data = kzalloc(sizeof(struct lm77_data), GFP_KERNEL);
 320        if (!data) {
 321                err = -ENOMEM;
 322                goto exit;
 323        }
 324
 325        i2c_set_clientdata(new_client, data);
 326        data->valid = 0;
 327        mutex_init(&data->update_lock);
 328
 329        /* Initialize the LM77 chip */
 330        lm77_init_client(new_client);
 331
 332        /* Register sysfs hooks */
 333        if ((err = sysfs_create_group(&new_client->dev.kobj, &lm77_group)))
 334                goto exit_free;
 335
 336        data->hwmon_dev = hwmon_device_register(&new_client->dev);
 337        if (IS_ERR(data->hwmon_dev)) {
 338                err = PTR_ERR(data->hwmon_dev);
 339                goto exit_remove;
 340        }
 341
 342        return 0;
 343
 344exit_remove:
 345        sysfs_remove_group(&new_client->dev.kobj, &lm77_group);
 346exit_free:
 347        kfree(data);
 348exit:
 349        return err;
 350}
 351
 352static int lm77_remove(struct i2c_client *client)
 353{
 354        struct lm77_data *data = i2c_get_clientdata(client);
 355        hwmon_device_unregister(data->hwmon_dev);
 356        sysfs_remove_group(&client->dev.kobj, &lm77_group);
 357        kfree(data);
 358        return 0;
 359}
 360
 361/* All registers are word-sized, except for the configuration register.
 362   The LM77 uses the high-byte first convention. */
 363static u16 lm77_read_value(struct i2c_client *client, u8 reg)
 364{
 365        if (reg == LM77_REG_CONF)
 366                return i2c_smbus_read_byte_data(client, reg);
 367        else
 368                return i2c_smbus_read_word_swapped(client, reg);
 369}
 370
 371static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
 372{
 373        if (reg == LM77_REG_CONF)
 374                return i2c_smbus_write_byte_data(client, reg, value);
 375        else
 376                return i2c_smbus_write_word_swapped(client, reg, value);
 377}
 378
 379static void lm77_init_client(struct i2c_client *client)
 380{
 381        /* Initialize the LM77 chip - turn off shutdown mode */
 382        int conf = lm77_read_value(client, LM77_REG_CONF);
 383        if (conf & 1)
 384                lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
 385}
 386
 387static struct lm77_data *lm77_update_device(struct device *dev)
 388{
 389        struct i2c_client *client = to_i2c_client(dev);
 390        struct lm77_data *data = i2c_get_clientdata(client);
 391
 392        mutex_lock(&data->update_lock);
 393
 394        if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
 395            || !data->valid) {
 396                dev_dbg(&client->dev, "Starting lm77 update\n");
 397                data->temp_input =
 398                        LM77_TEMP_FROM_REG(lm77_read_value(client,
 399                                                           LM77_REG_TEMP));
 400                data->temp_hyst =
 401                        LM77_TEMP_FROM_REG(lm77_read_value(client,
 402                                                           LM77_REG_TEMP_HYST));
 403                data->temp_crit =
 404                        LM77_TEMP_FROM_REG(lm77_read_value(client,
 405                                                           LM77_REG_TEMP_CRIT));
 406                data->temp_min =
 407                        LM77_TEMP_FROM_REG(lm77_read_value(client,
 408                                                           LM77_REG_TEMP_MIN));
 409                data->temp_max =
 410                        LM77_TEMP_FROM_REG(lm77_read_value(client,
 411                                                           LM77_REG_TEMP_MAX));
 412                data->alarms =
 413                        lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
 414                data->last_updated = jiffies;
 415                data->valid = 1;
 416        }
 417
 418        mutex_unlock(&data->update_lock);
 419
 420        return data;
 421}
 422
 423static int __init sensors_lm77_init(void)
 424{
 425        return i2c_add_driver(&lm77_driver);
 426}
 427
 428static void __exit sensors_lm77_exit(void)
 429{
 430        i2c_del_driver(&lm77_driver);
 431}
 432
 433MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
 434MODULE_DESCRIPTION("LM77 driver");
 435MODULE_LICENSE("GPL");
 436
 437module_init(sensors_lm77_init);
 438module_exit(sensors_lm77_exit);
 439
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.