aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlessandro Zummo <azummo-lists@towertech.it>2007-05-08 11:22:02 -0400
committerJean Delvare <khali@hyperion.delvare>2007-05-08 11:22:02 -0400
commit2d8dd65fc14287f2c004dd755e517ba0f45d446e (patch)
tree5f6a886a3dc2dcaf6a2fef59d0f6a83c76101e5c /drivers
parentd58ee056cc40117afe4d3bb03006ac537d779634 (diff)
hwmon: New AD7416, AD7417 and AD7418 driver
A driver for the Analog Devices AD7416, AD7417 and AD7418 chips. Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ad7418.c373
3 files changed, 384 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e488d691da4a..1691c6c7bbca 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,16 @@ config SENSORS_ABITUGURU
39 This driver can also be built as a module. If so, the module 39 This driver can also be built as a module. If so, the module
40 will be called abituguru. 40 will be called abituguru.
41 41
42config SENSORS_AD7418
43 tristate "Analog Devices AD7416, AD7417 and AD7418"
44 depends on HWMON && I2C && EXPERIMENTAL
45 help
46 If you say yes here you get support for the Analog Devices
47 AD7416, AD7417 and AD7418 temperature monitoring chips.
48
49 This driver can also be built as a module. If so, the module
50 will be called ad7418.
51
42config SENSORS_ADM1021 52config SENSORS_ADM1021
43 tristate "Analog Devices ADM1021 and compatibles" 53 tristate "Analog Devices ADM1021 and compatibles"
44 depends on HWMON && I2C 54 depends on HWMON && I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 33780678bd4b..d83f2380e089 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
14obj-$(CONFIG_SENSORS_W83791D) += w83791d.o 14obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
15 15
16obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o 16obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
17obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
17obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o 18obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
18obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o 19obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
19obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o 20obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
new file mode 100644
index 000000000000..cc8b624a1e51
--- /dev/null
+++ b/drivers/hwmon/ad7418.c
@@ -0,0 +1,373 @@
1/*
2 * An hwmon driver for the Analog Devices AD7416/17/18
3 * Copyright (C) 2006-07 Tower Technologies
4 *
5 * Author: Alessandro Zummo <a.zummo@towertech.it>
6 *
7 * Based on lm75.c
8 * Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License,
12 * as published by the Free Software Foundation - version 2.
13 */
14
15#include <linux/module.h>
16#include <linux/jiffies.h>
17#include <linux/i2c.h>
18#include <linux/hwmon.h>
19#include <linux/hwmon-sysfs.h>
20#include <linux/err.h>
21#include <linux/mutex.h>
22#include <linux/delay.h>
23
24#include "lm75.h"
25
26#define DRV_VERSION "0.3"
27
28/* Addresses to scan */
29static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END };
30/* Insmod parameters */
31I2C_CLIENT_INSMOD_3(ad7416, ad7417, ad7418);
32
33/* AD7418 registers */
34#define AD7418_REG_TEMP_IN 0x00
35#define AD7418_REG_CONF 0x01
36#define AD7418_REG_TEMP_HYST 0x02
37#define AD7418_REG_TEMP_OS 0x03
38#define AD7418_REG_ADC 0x04
39#define AD7418_REG_CONF2 0x05
40
41#define AD7418_REG_ADC_CH(x) ((x) << 5)
42#define AD7418_CH_TEMP AD7418_REG_ADC_CH(0)
43
44static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
45 AD7418_REG_TEMP_HYST,
46 AD7418_REG_TEMP_OS };
47
48struct ad7418_data {
49 struct i2c_client client;
50 struct class_device *class_dev;
51 struct attribute_group attrs;
52 enum chips type;
53 struct mutex lock;
54 int adc_max; /* number of ADC channels */
55 char valid;
56 unsigned long last_updated; /* In jiffies */
57 s16 temp[3]; /* Register values */
58 u16 in[4];
59};
60
61static int ad7418_attach_adapter(struct i2c_adapter *adapter);
62static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind);
63static int ad7418_detach_client(struct i2c_client *client);
64
65static struct i2c_driver ad7418_driver = {
66 .driver = {
67 .name = "ad7418",
68 },
69 .attach_adapter = ad7418_attach_adapter,
70 .detach_client = ad7418_detach_client,
71};
72
73/* All registers are word-sized, except for the configuration registers.
74 * AD7418 uses a high-byte first convention. Do NOT use those functions to
75 * access the configuration registers CONF and CONF2, as they are byte-sized.
76 */
77static inline int ad7418_read(struct i2c_client *client, u8 reg)
78{
79 return swab16(i2c_smbus_read_word_data(client, reg));
80}
81
82static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
83{
84 return i2c_smbus_write_word_data(client, reg, swab16(value));
85}
86
87static void ad7418_init_client(struct i2c_client *client)
88{
89 struct ad7418_data *data = i2c_get_clientdata(client);
90
91 int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
92 if (reg < 0) {
93 dev_err(&client->dev, "cannot read configuration register\n");
94 } else {
95 dev_info(&client->dev, "configuring for mode 1\n");
96 i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
97
98 if (data->type == ad7417 || data->type == ad7418)
99 i2c_smbus_write_byte_data(client,
100 AD7418_REG_CONF2, 0x00);
101 }
102}
103
104static struct ad7418_data *ad7418_update_device(struct device *dev)
105{
106 struct i2c_client *client = to_i2c_client(dev);
107 struct ad7418_data *data = i2c_get_clientdata(client);
108
109 mutex_lock(&data->lock);
110
111 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
112 || !data->valid) {
113 u8 cfg;
114 int i, ch;
115
116 /* read config register and clear channel bits */
117 cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
118 cfg &= 0x1F;
119
120 i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
121 cfg | AD7418_CH_TEMP);
122 udelay(30);
123
124 for (i = 0; i < 3; i++) {
125 data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
126 }
127
128 for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
129 i2c_smbus_write_byte_data(client,
130 AD7418_REG_CONF,
131 cfg | AD7418_REG_ADC_CH(ch));
132
133 udelay(15);
134 data->in[data->adc_max - 1 - i] =
135 ad7418_read(client, AD7418_REG_ADC);
136 }
137
138 /* restore old configuration value */
139 ad7418_write(client, AD7418_REG_CONF, cfg);
140
141 data->last_updated = jiffies;
142 data->valid = 1;
143 }
144
145 mutex_unlock(&data->lock);
146
147 return data;
148}
149
150static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
151 char *buf)
152{
153 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
154 struct ad7418_data *data = ad7418_update_device(dev);
155 return sprintf(buf, "%d\n",
156 LM75_TEMP_FROM_REG(data->temp[attr->index]));
157}
158
159static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
160 char *buf)
161{
162 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
163 struct ad7418_data *data = ad7418_update_device(dev);
164
165 return sprintf(buf, "%d\n",
166 ((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
167}
168
169static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
170 const char *buf, size_t count)
171{
172 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
173 struct i2c_client *client = to_i2c_client(dev);
174 struct ad7418_data *data = i2c_get_clientdata(client);
175 int temp = simple_strtol(buf, NULL, 10);
176
177 mutex_lock(&data->lock);
178 data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
179 ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]);
180 mutex_unlock(&data->lock);
181 return count;
182}
183
184static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
185static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
186 show_temp, set_temp, 1);
187static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
188 show_temp, set_temp, 2);
189
190static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0);
191static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1);
192static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2);
193static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3);
194
195static int ad7418_attach_adapter(struct i2c_adapter *adapter)
196{
197 if (!(adapter->class & I2C_CLASS_HWMON))
198 return 0;
199 return i2c_probe(adapter, &addr_data, ad7418_detect);
200}
201
202static struct attribute *ad7416_attributes[] = {
203 &sensor_dev_attr_temp1_max.dev_attr.attr,
204 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
205 &sensor_dev_attr_temp1_input.dev_attr.attr,
206 NULL
207};
208
209static struct attribute *ad7417_attributes[] = {
210 &sensor_dev_attr_temp1_max.dev_attr.attr,
211 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
212 &sensor_dev_attr_temp1_input.dev_attr.attr,
213 &sensor_dev_attr_in1_input.dev_attr.attr,
214 &sensor_dev_attr_in2_input.dev_attr.attr,
215 &sensor_dev_attr_in3_input.dev_attr.attr,
216 &sensor_dev_attr_in4_input.dev_attr.attr,
217 NULL
218};
219
220static struct attribute *ad7418_attributes[] = {
221 &sensor_dev_attr_temp1_max.dev_attr.attr,
222 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
223 &sensor_dev_attr_temp1_input.dev_attr.attr,
224 &sensor_dev_attr_in1_input.dev_attr.attr,
225 NULL
226};
227
228static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)
229{
230 struct i2c_client *client;
231 struct ad7418_data *data;
232 int err = 0;
233
234 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
235 I2C_FUNC_SMBUS_WORD_DATA))
236 goto exit;
237
238 if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) {
239 err = -ENOMEM;
240 goto exit;
241 }
242
243 client = &data->client;
244 client->addr = address;
245 client->adapter = adapter;
246 client->driver = &ad7418_driver;
247
248 i2c_set_clientdata(client, data);
249
250 mutex_init(&data->lock);
251
252 /* AD7418 has a curious behaviour on registers 6 and 7. They
253 * both always read 0xC071 and are not documented on the datasheet.
254 * We use them to detect the chip.
255 */
256 if (kind <= 0) {
257 int reg, reg6, reg7;
258
259 /* the AD7416 lies within this address range, but I have
260 * no means to check.
261 */
262 if (address >= 0x48 && address <= 0x4f) {
263 /* XXX add tests for AD7416 here */
264 /* data->type = ad7416; */
265 }
266 /* here we might have AD7417 or AD7418 */
267 else if (address >= 0x28 && address <= 0x2f) {
268 reg6 = i2c_smbus_read_word_data(client, 0x06);
269 reg7 = i2c_smbus_read_word_data(client, 0x07);
270
271 if (address == 0x28 && reg6 == 0xC071 && reg7 == 0xC071)
272 data->type = ad7418;
273
274 /* XXX add tests for AD7417 here */
275
276
277 /* both AD7417 and AD7418 have bits 0-5 of
278 * the CONF2 register at 0
279 */
280 reg = i2c_smbus_read_byte_data(client,
281 AD7418_REG_CONF2);
282 if (reg & 0x3F)
283 data->type = any_chip; /* detection failed */
284 }
285 } else {
286 dev_dbg(&adapter->dev, "detection forced\n");
287 }
288
289 if (kind > 0)
290 data->type = kind;
291 else if (kind < 0 && data->type == any_chip) {
292 err = -ENODEV;
293 goto exit_free;
294 }
295
296 switch (data->type) {
297 case any_chip:
298 case ad7416:
299 data->adc_max = 0;
300 data->attrs.attrs = ad7416_attributes;
301 strlcpy(client->name, "ad7416", I2C_NAME_SIZE);
302 break;
303
304 case ad7417:
305 data->adc_max = 4;
306 data->attrs.attrs = ad7417_attributes;
307 strlcpy(client->name, "ad7417", I2C_NAME_SIZE);
308 break;
309
310 case ad7418:
311 data->adc_max = 1;
312 data->attrs.attrs = ad7418_attributes;
313 strlcpy(client->name, "ad7418", I2C_NAME_SIZE);
314 break;
315 }
316
317 if ((err = i2c_attach_client(client)))
318 goto exit_free;
319
320 dev_info(&client->dev, "%s chip found\n", client->name);
321
322 /* Initialize the AD7418 chip */
323 ad7418_init_client(client);
324
325 /* Register sysfs hooks */
326 if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
327 goto exit_detach;
328
329 data->class_dev = hwmon_device_register(&client->dev);
330 if (IS_ERR(data->class_dev)) {
331 err = PTR_ERR(data->class_dev);
332 goto exit_remove;
333 }
334
335 return 0;
336
337exit_remove:
338 sysfs_remove_group(&client->dev.kobj, &data->attrs);
339exit_detach:
340 i2c_detach_client(client);
341exit_free:
342 kfree(data);
343exit:
344 return err;
345}
346
347static int ad7418_detach_client(struct i2c_client *client)
348{
349 struct ad7418_data *data = i2c_get_clientdata(client);
350 hwmon_device_unregister(data->class_dev);
351 sysfs_remove_group(&client->dev.kobj, &data->attrs);
352 i2c_detach_client(client);
353 kfree(data);
354 return 0;
355}
356
357static int __init ad7418_init(void)
358{
359 return i2c_add_driver(&ad7418_driver);
360}
361
362static void __exit ad7418_exit(void)
363{
364 i2c_del_driver(&ad7418_driver);
365}
366
367MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
368MODULE_DESCRIPTION("AD7416/17/18 driver");
369MODULE_LICENSE("GPL");
370MODULE_VERSION(DRV_VERSION);
371
372module_init(ad7418_init);
373module_exit(ad7418_exit);