diff options
author | Alessandro Zummo <azummo-lists@towertech.it> | 2007-05-08 11:22:02 -0400 |
---|---|---|
committer | Jean Delvare <khali@hyperion.delvare> | 2007-05-08 11:22:02 -0400 |
commit | 2d8dd65fc14287f2c004dd755e517ba0f45d446e (patch) | |
tree | 5f6a886a3dc2dcaf6a2fef59d0f6a83c76101e5c /drivers/hwmon | |
parent | d58ee056cc40117afe4d3bb03006ac537d779634 (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/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ad7418.c | 373 |
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 | ||
42 | config 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 | |||
42 | config SENSORS_ADM1021 | 52 | config 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 | |||
14 | obj-$(CONFIG_SENSORS_W83791D) += w83791d.o | 14 | obj-$(CONFIG_SENSORS_W83791D) += w83791d.o |
15 | 15 | ||
16 | obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o | 16 | obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o |
17 | obj-$(CONFIG_SENSORS_AD7418) += ad7418.o | ||
17 | obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o | 18 | obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o |
18 | obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o | 19 | obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o |
19 | obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o | 20 | obj-$(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 */ | ||
29 | static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END }; | ||
30 | /* Insmod parameters */ | ||
31 | I2C_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 | |||
44 | static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN, | ||
45 | AD7418_REG_TEMP_HYST, | ||
46 | AD7418_REG_TEMP_OS }; | ||
47 | |||
48 | struct 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 | |||
61 | static int ad7418_attach_adapter(struct i2c_adapter *adapter); | ||
62 | static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind); | ||
63 | static int ad7418_detach_client(struct i2c_client *client); | ||
64 | |||
65 | static 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 | */ | ||
77 | static inline int ad7418_read(struct i2c_client *client, u8 reg) | ||
78 | { | ||
79 | return swab16(i2c_smbus_read_word_data(client, reg)); | ||
80 | } | ||
81 | |||
82 | static 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 | |||
87 | static 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 | |||
104 | static 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 | |||
150 | static 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 | |||
159 | static 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 | |||
169 | static 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 | |||
184 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); | ||
185 | static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, | ||
186 | show_temp, set_temp, 1); | ||
187 | static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, | ||
188 | show_temp, set_temp, 2); | ||
189 | |||
190 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0); | ||
191 | static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1); | ||
192 | static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2); | ||
193 | static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3); | ||
194 | |||
195 | static 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 | |||
202 | static 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 | |||
209 | static 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 | |||
220 | static 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 | |||
228 | static 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 | |||
337 | exit_remove: | ||
338 | sysfs_remove_group(&client->dev.kobj, &data->attrs); | ||
339 | exit_detach: | ||
340 | i2c_detach_client(client); | ||
341 | exit_free: | ||
342 | kfree(data); | ||
343 | exit: | ||
344 | return err; | ||
345 | } | ||
346 | |||
347 | static 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 | |||
357 | static int __init ad7418_init(void) | ||
358 | { | ||
359 | return i2c_add_driver(&ad7418_driver); | ||
360 | } | ||
361 | |||
362 | static void __exit ad7418_exit(void) | ||
363 | { | ||
364 | i2c_del_driver(&ad7418_driver); | ||
365 | } | ||
366 | |||
367 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
368 | MODULE_DESCRIPTION("AD7416/17/18 driver"); | ||
369 | MODULE_LICENSE("GPL"); | ||
370 | MODULE_VERSION(DRV_VERSION); | ||
371 | |||
372 | module_init(ad7418_init); | ||
373 | module_exit(ad7418_exit); | ||