Commit 6c633c3025c75f5fcf3a76d375faff34e3be021b

Authored by Sean MacLennan
Committed by Jean Delvare
1 parent 84f768c163

hwmon: ad7414 driver

Driver for the Analog Devices AD7414 temperature monitoring chip.

Signed-off-by: Sean MacLennan <smaclennan@pikatech.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>

Showing 3 changed files with 279 additions and 0 deletions Side-by-side Diff

drivers/hwmon/Kconfig
... ... @@ -57,6 +57,16 @@
57 57 This driver can also be built as a module. If so, the module
58 58 will be called abituguru3.
59 59  
  60 +config SENSORS_AD7414
  61 + tristate "Analog Devices AD7414"
  62 + depends on I2C && EXPERIMENTAL
  63 + help
  64 + If you say yes here you get support for the Analog Devices
  65 + AD7414 temperature monitoring chip.
  66 +
  67 + This driver can also be built as a module. If so, the module
  68 + will be called ad7414.
  69 +
60 70 config SENSORS_AD7418
61 71 tristate "Analog Devices AD7416, AD7417 and AD7418"
62 72 depends on I2C && EXPERIMENTAL
drivers/hwmon/Makefile
... ... @@ -15,6 +15,7 @@
15 15  
16 16 obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
17 17 obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
  18 +obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
18 19 obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
19 20 obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
20 21 obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
drivers/hwmon/ad7414.c
  1 +/*
  2 + * An hwmon driver for the Analog Devices AD7414
  3 + *
  4 + * Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
  5 + *
  6 + * Copyright (c) 2008 PIKA Technologies
  7 + * Sean MacLennan <smaclennan@pikatech.com>
  8 + *
  9 + * Copyright (c) 2008 Spansion Inc.
  10 + * Frank Edelhaeuser <frank.edelhaeuser at spansion.com>
  11 + * (converted to "new style" I2C driver model, removed checkpatch.pl warnings)
  12 + *
  13 + * Based on ad7418.c
  14 + * Copyright 2006 Tower Technologies, Alessandro Zummo <a.zummo at towertech.it>
  15 + *
  16 + * This program is free software; you can redistribute it and/or modify
  17 + * it under the terms of the GNU General Public License as published by
  18 + * the Free Software Foundation; either version 2 of the License, or
  19 + * (at your option) any later version.
  20 + */
  21 +
  22 +#include <linux/module.h>
  23 +#include <linux/jiffies.h>
  24 +#include <linux/i2c.h>
  25 +#include <linux/hwmon.h>
  26 +#include <linux/hwmon-sysfs.h>
  27 +#include <linux/err.h>
  28 +#include <linux/mutex.h>
  29 +#include <linux/sysfs.h>
  30 +
  31 +
  32 +/* AD7414 registers */
  33 +#define AD7414_REG_TEMP 0x00
  34 +#define AD7414_REG_CONF 0x01
  35 +#define AD7414_REG_T_HIGH 0x02
  36 +#define AD7414_REG_T_LOW 0x03
  37 +
  38 +static u8 AD7414_REG_LIMIT[] = { AD7414_REG_T_HIGH, AD7414_REG_T_LOW };
  39 +
  40 +struct ad7414_data {
  41 + struct device *hwmon_dev;
  42 + struct mutex lock; /* atomic read data updates */
  43 + char valid; /* !=0 if following fields are valid */
  44 + unsigned long next_update; /* In jiffies */
  45 + s16 temp_input; /* Register values */
  46 + s8 temps[ARRAY_SIZE(AD7414_REG_LIMIT)];
  47 +};
  48 +
  49 +/* REG: (0.25C/bit, two's complement) << 6 */
  50 +static inline int ad7414_temp_from_reg(s16 reg)
  51 +{
  52 + /* use integer division instead of equivalent right shift to
  53 + * guarantee arithmetic shift and preserve the sign
  54 + */
  55 + return ((int)reg / 64) * 250;
  56 +}
  57 +
  58 +static inline int ad7414_read(struct i2c_client *client, u8 reg)
  59 +{
  60 + if (reg == AD7414_REG_TEMP) {
  61 + int value = i2c_smbus_read_word_data(client, reg);
  62 + return (value < 0) ? value : swab16(value);
  63 + } else
  64 + return i2c_smbus_read_byte_data(client, reg);
  65 +}
  66 +
  67 +static inline int ad7414_write(struct i2c_client *client, u8 reg, u8 value)
  68 +{
  69 + return i2c_smbus_write_byte_data(client, reg, value);
  70 +}
  71 +
  72 +struct ad7414_data *ad7414_update_device(struct device *dev)
  73 +{
  74 + struct i2c_client *client = to_i2c_client(dev);
  75 + struct ad7414_data *data = i2c_get_clientdata(client);
  76 +
  77 + mutex_lock(&data->lock);
  78 +
  79 + if (time_after(jiffies, data->next_update) || !data->valid) {
  80 + int value, i;
  81 +
  82 + dev_dbg(&client->dev, "starting ad7414 update\n");
  83 +
  84 + value = ad7414_read(client, AD7414_REG_TEMP);
  85 + if (value < 0)
  86 + dev_dbg(&client->dev, "AD7414_REG_TEMP err %d\n",
  87 + value);
  88 + else
  89 + data->temp_input = value;
  90 +
  91 + for (i = 0; i < ARRAY_SIZE(AD7414_REG_LIMIT); ++i) {
  92 + value = ad7414_read(client, AD7414_REG_LIMIT[i]);
  93 + if (value < 0)
  94 + dev_dbg(&client->dev, "AD7414 reg %d err %d\n",
  95 + AD7414_REG_LIMIT[i], value);
  96 + else
  97 + data->temps[i] = value;
  98 + }
  99 +
  100 + data->next_update = jiffies + HZ + HZ / 2;
  101 + data->valid = 1;
  102 + }
  103 +
  104 + mutex_unlock(&data->lock);
  105 +
  106 + return data;
  107 +}
  108 +
  109 +static ssize_t show_temp_input(struct device *dev,
  110 + struct device_attribute *attr, char *buf)
  111 +{
  112 + struct ad7414_data *data = ad7414_update_device(dev);
  113 + return sprintf(buf, "%d\n", ad7414_temp_from_reg(data->temp_input));
  114 +}
  115 +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
  116 +
  117 +static ssize_t show_max_min(struct device *dev, struct device_attribute *attr,
  118 + char *buf)
  119 +{
  120 + int index = to_sensor_dev_attr(attr)->index;
  121 + struct ad7414_data *data = ad7414_update_device(dev);
  122 + return sprintf(buf, "%d\n", data->temps[index] * 1000);
  123 +}
  124 +
  125 +static ssize_t set_max_min(struct device *dev,
  126 + struct device_attribute *attr,
  127 + const char *buf, size_t count)
  128 +{
  129 + struct i2c_client *client = to_i2c_client(dev);
  130 + struct ad7414_data *data = i2c_get_clientdata(client);
  131 + int index = to_sensor_dev_attr(attr)->index;
  132 + u8 reg = AD7414_REG_LIMIT[index];
  133 + long temp = simple_strtol(buf, NULL, 10);
  134 +
  135 + temp = SENSORS_LIMIT(temp, -40000, 85000);
  136 + temp = (temp + (temp < 0 ? -500 : 500)) / 1000;
  137 +
  138 + mutex_lock(&data->lock);
  139 + data->temps[index] = temp;
  140 + ad7414_write(client, reg, temp);
  141 + mutex_unlock(&data->lock);
  142 + return count;
  143 +}
  144 +
  145 +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
  146 + show_max_min, set_max_min, 0);
  147 +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
  148 + show_max_min, set_max_min, 1);
  149 +
  150 +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
  151 + char *buf)
  152 +{
  153 + int bitnr = to_sensor_dev_attr(attr)->index;
  154 + struct ad7414_data *data = ad7414_update_device(dev);
  155 + int value = (data->temp_input >> bitnr) & 1;
  156 + return sprintf(buf, "%d\n", value);
  157 +}
  158 +
  159 +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 3);
  160 +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 4);
  161 +
  162 +static struct attribute *ad7414_attributes[] = {
  163 + &sensor_dev_attr_temp1_input.dev_attr.attr,
  164 + &sensor_dev_attr_temp1_max.dev_attr.attr,
  165 + &sensor_dev_attr_temp1_min.dev_attr.attr,
  166 + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
  167 + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
  168 + NULL
  169 +};
  170 +
  171 +static const struct attribute_group ad7414_group = {
  172 + .attrs = ad7414_attributes,
  173 +};
  174 +
  175 +static int ad7414_probe(struct i2c_client *client,
  176 + const struct i2c_device_id *dev_id)
  177 +{
  178 + struct ad7414_data *data;
  179 + int conf;
  180 + int err = 0;
  181 +
  182 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
  183 + I2C_FUNC_SMBUS_READ_WORD_DATA))
  184 + goto exit;
  185 +
  186 + data = kzalloc(sizeof(struct ad7414_data), GFP_KERNEL);
  187 + if (!data) {
  188 + err = -ENOMEM;
  189 + goto exit;
  190 + }
  191 +
  192 + i2c_set_clientdata(client, data);
  193 + mutex_init(&data->lock);
  194 +
  195 + dev_info(&client->dev, "chip found\n");
  196 +
  197 + /* Make sure the chip is powered up. */
  198 + conf = i2c_smbus_read_byte_data(client, AD7414_REG_CONF);
  199 + if (conf < 0)
  200 + dev_warn(&client->dev,
  201 + "ad7414_probe unable to read config register.\n");
  202 + else {
  203 + conf &= ~(1 << 7);
  204 + i2c_smbus_write_byte_data(client, AD7414_REG_CONF, conf);
  205 + }
  206 +
  207 + /* Register sysfs hooks */
  208 + err = sysfs_create_group(&client->dev.kobj, &ad7414_group);
  209 + if (err)
  210 + goto exit_free;
  211 +
  212 + data->hwmon_dev = hwmon_device_register(&client->dev);
  213 + if (IS_ERR(data->hwmon_dev)) {
  214 + err = PTR_ERR(data->hwmon_dev);
  215 + goto exit_remove;
  216 + }
  217 +
  218 + return 0;
  219 +
  220 +exit_remove:
  221 + sysfs_remove_group(&client->dev.kobj, &ad7414_group);
  222 +exit_free:
  223 + kfree(data);
  224 +exit:
  225 + return err;
  226 +}
  227 +
  228 +static int __devexit ad7414_remove(struct i2c_client *client)
  229 +{
  230 + struct ad7414_data *data = i2c_get_clientdata(client);
  231 +
  232 + hwmon_device_unregister(data->hwmon_dev);
  233 + sysfs_remove_group(&client->dev.kobj, &ad7414_group);
  234 + kfree(data);
  235 + return 0;
  236 +}
  237 +
  238 +static const struct i2c_device_id ad7414_id[] = {
  239 + { "ad7414", 0 },
  240 + {}
  241 +};
  242 +
  243 +static struct i2c_driver ad7414_driver = {
  244 + .driver = {
  245 + .name = "ad7414",
  246 + },
  247 + .probe = ad7414_probe,
  248 + .remove = __devexit_p(ad7414_remove),
  249 + .id_table = ad7414_id,
  250 +};
  251 +
  252 +static int __init ad7414_init(void)
  253 +{
  254 + return i2c_add_driver(&ad7414_driver);
  255 +}
  256 +module_init(ad7414_init);
  257 +
  258 +static void __exit ad7414_exit(void)
  259 +{
  260 + i2c_del_driver(&ad7414_driver);
  261 +}
  262 +module_exit(ad7414_exit);
  263 +
  264 +MODULE_AUTHOR("Stefan Roese <sr at denx.de>, "
  265 + "Frank Edelhaeuser <frank.edelhaeuser at spansion.com>");
  266 +
  267 +MODULE_DESCRIPTION("AD7414 driver");
  268 +MODULE_LICENSE("GPL");