aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorRoland Stigge <stigge@antcom.de>2011-01-09 09:31:39 -0500
committerGuenter Roeck <guenter.roeck@ericsson.com>2011-01-09 12:10:10 -0500
commit6099469805c24af14250e182bb9ca082b8a6b716 (patch)
tree9b08dd1a10ff89f2557575f5bf95b07babf92beb /drivers/hwmon
parent430400b86304ea729ba10f2966a8af67da60a37f (diff)
hwmon: Support for Dallas Semiconductor DS620
Driver for Dallas Semiconductor DS620 temperature sensor and thermostat Signed-off-by: Roland Stigge <stigge@antcom.de> Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ds620.c337
3 files changed, 348 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0f09567257fd..bdc13d28b1ea 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -274,6 +274,16 @@ config SENSORS_ATXP1
274 This driver can also be built as a module. If so, the module 274 This driver can also be built as a module. If so, the module
275 will be called atxp1. 275 will be called atxp1.
276 276
277config SENSORS_DS620
278 tristate "Dallas Semiconductor DS620"
279 depends on I2C
280 help
281 If you say yes here you get support for Dallas Semiconductor
282 DS620 sensor chip.
283
284 This driver can also be built as a module. If so, the module
285 will be called ds620.
286
277config SENSORS_DS1621 287config SENSORS_DS1621
278 tristate "Dallas Semiconductor DS1621 and DS1625" 288 tristate "Dallas Semiconductor DS1621 and DS1625"
279 depends on I2C 289 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 20e74086e681..dde02d99c238 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
41obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o 41obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
42obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o 42obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
43obj-$(CONFIG_SENSORS_DME1737) += dme1737.o 43obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
44obj-$(CONFIG_SENSORS_DS620) += ds620.o
44obj-$(CONFIG_SENSORS_DS1621) += ds1621.o 45obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
45obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o 46obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
46obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o 47obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
new file mode 100644
index 000000000000..257957c69d92
--- /dev/null
+++ b/drivers/hwmon/ds620.c
@@ -0,0 +1,337 @@
1/*
2 * ds620.c - Support for temperature sensor and thermostat DS620
3 *
4 * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
5 *
6 * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/slab.h>
26#include <linux/jiffies.h>
27#include <linux/i2c.h>
28#include <linux/hwmon.h>
29#include <linux/hwmon-sysfs.h>
30#include <linux/err.h>
31#include <linux/mutex.h>
32#include <linux/sysfs.h>
33#include <linux/i2c/ds620.h>
34
35/*
36 * Many DS620 constants specified below
37 * 15 14 13 12 11 10 09 08
38 * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT|
39 *
40 * 07 06 05 04 03 02 01 00
41 * |PO2 |PO1 |A2 |A1 |A0 | | | |
42 */
43#define DS620_REG_CONFIG_DONE 0x8000
44#define DS620_REG_CONFIG_NVB 0x4000
45#define DS620_REG_CONFIG_THF 0x2000
46#define DS620_REG_CONFIG_TLF 0x1000
47#define DS620_REG_CONFIG_R1 0x0800
48#define DS620_REG_CONFIG_R0 0x0400
49#define DS620_REG_CONFIG_AUTOC 0x0200
50#define DS620_REG_CONFIG_1SHOT 0x0100
51#define DS620_REG_CONFIG_PO2 0x0080
52#define DS620_REG_CONFIG_PO1 0x0040
53#define DS620_REG_CONFIG_A2 0x0020
54#define DS620_REG_CONFIG_A1 0x0010
55#define DS620_REG_CONFIG_A0 0x0008
56
57/* The DS620 registers */
58static const u8 DS620_REG_TEMP[3] = {
59 0xAA, /* input, word, RO */
60 0xA2, /* min, word, RW */
61 0xA0, /* max, word, RW */
62};
63
64#define DS620_REG_CONF 0xAC /* word, RW */
65#define DS620_COM_START 0x51 /* no data */
66#define DS620_COM_STOP 0x22 /* no data */
67
68/* Each client has this additional data */
69struct ds620_data {
70 struct device *hwmon_dev;
71 struct mutex update_lock;
72 char valid; /* !=0 if following fields are valid */
73 unsigned long last_updated; /* In jiffies */
74
75 u16 temp[3]; /* Register values, word */
76};
77
78/*
79 * Temperature registers are word-sized.
80 * DS620 uses a high-byte first convention, which is exactly opposite to
81 * the SMBus standard.
82 */
83static int ds620_read_temp(struct i2c_client *client, u8 reg)
84{
85 int ret;
86
87 ret = i2c_smbus_read_word_data(client, reg);
88 if (ret < 0)
89 return ret;
90 return swab16(ret);
91}
92
93static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value)
94{
95 return i2c_smbus_write_word_data(client, reg, swab16(value));
96}
97
98static void ds620_init_client(struct i2c_client *client)
99{
100 struct ds620_platform_data *ds620_info = client->dev.platform_data;
101 u16 conf, new_conf;
102
103 new_conf = conf =
104 swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF));
105
106 /* switch to continuous conversion mode */
107 new_conf &= ~DS620_REG_CONFIG_1SHOT;
108 /* already high at power-on, but don't trust the BIOS! */
109 new_conf |= DS620_REG_CONFIG_PO2;
110 /* thermostat mode according to platform data */
111 if (ds620_info && ds620_info->pomode == 1)
112 new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */
113 else if (ds620_info && ds620_info->pomode == 2)
114 new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */
115 else
116 new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */
117 /* with highest precision */
118 new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0;
119
120 if (conf != new_conf)
121 i2c_smbus_write_word_data(client, DS620_REG_CONF,
122 swab16(new_conf));
123
124 /* start conversion */
125 i2c_smbus_write_byte(client, DS620_COM_START);
126}
127
128static struct ds620_data *ds620_update_client(struct device *dev)
129{
130 struct i2c_client *client = to_i2c_client(dev);
131 struct ds620_data *data = i2c_get_clientdata(client);
132 struct ds620_data *ret = data;
133
134 mutex_lock(&data->update_lock);
135
136 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
137 || !data->valid) {
138 int i;
139 int res;
140
141 dev_dbg(&client->dev, "Starting ds620 update\n");
142
143 for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
144 res = ds620_read_temp(client,
145 DS620_REG_TEMP[i]);
146 if (res < 0) {
147 ret = ERR_PTR(res);
148 goto abort;
149 }
150
151 data->temp[i] = res;
152 }
153
154 data->last_updated = jiffies;
155 data->valid = 1;
156 }
157abort:
158 mutex_unlock(&data->update_lock);
159
160 return ret;
161}
162
163static ssize_t show_temp(struct device *dev, struct device_attribute *da,
164 char *buf)
165{
166 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
167 struct ds620_data *data = ds620_update_client(dev);
168
169 if (IS_ERR(data))
170 return PTR_ERR(data);
171
172 return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10);
173}
174
175static ssize_t set_temp(struct device *dev, struct device_attribute *da,
176 const char *buf, size_t count)
177{
178 int res;
179 long val;
180
181 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
182 struct i2c_client *client = to_i2c_client(dev);
183 struct ds620_data *data = i2c_get_clientdata(client);
184
185 res = strict_strtol(buf, 10, &val);
186
187 if (res)
188 return res;
189
190 val = (val * 10 / 625) * 8;
191
192 mutex_lock(&data->update_lock);
193 data->temp[attr->index] = val;
194 ds620_write_temp(client, DS620_REG_TEMP[attr->index],
195 data->temp[attr->index]);
196 mutex_unlock(&data->update_lock);
197 return count;
198}
199
200static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
201 char *buf)
202{
203 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
204 struct ds620_data *data = ds620_update_client(dev);
205 struct i2c_client *client = to_i2c_client(dev);
206 u16 conf, new_conf;
207 int res;
208
209 if (IS_ERR(data))
210 return PTR_ERR(data);
211
212 /* reset alarms if necessary */
213 res = i2c_smbus_read_word_data(client, DS620_REG_CONF);
214 if (res < 0)
215 return res;
216
217 conf = swab16(res);
218 new_conf = conf;
219 new_conf &= ~attr->index;
220 if (conf != new_conf) {
221 res = i2c_smbus_write_word_data(client, DS620_REG_CONF,
222 swab16(new_conf));
223 if (res < 0)
224 return res;
225 }
226
227 return sprintf(buf, "%d\n", !!(conf & attr->index));
228}
229
230static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
231static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1);
232static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2);
233static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
234 DS620_REG_CONFIG_TLF);
235static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
236 DS620_REG_CONFIG_THF);
237
238static struct attribute *ds620_attributes[] = {
239 &sensor_dev_attr_temp1_input.dev_attr.attr,
240 &sensor_dev_attr_temp1_min.dev_attr.attr,
241 &sensor_dev_attr_temp1_max.dev_attr.attr,
242 &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
243 &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
244 NULL
245};
246
247static const struct attribute_group ds620_group = {
248 .attrs = ds620_attributes,
249};
250
251static int ds620_probe(struct i2c_client *client,
252 const struct i2c_device_id *id)
253{
254 struct ds620_data *data;
255 int err;
256
257 data = kzalloc(sizeof(struct ds620_data), GFP_KERNEL);
258 if (!data) {
259 err = -ENOMEM;
260 goto exit;
261 }
262
263 i2c_set_clientdata(client, data);
264 mutex_init(&data->update_lock);
265
266 /* Initialize the DS620 chip */
267 ds620_init_client(client);
268
269 /* Register sysfs hooks */
270 err = sysfs_create_group(&client->dev.kobj, &ds620_group);
271 if (err)
272 goto exit_free;
273
274 data->hwmon_dev = hwmon_device_register(&client->dev);
275 if (IS_ERR(data->hwmon_dev)) {
276 err = PTR_ERR(data->hwmon_dev);
277 goto exit_remove_files;
278 }
279
280 dev_info(&client->dev, "temperature sensor found\n");
281
282 return 0;
283
284exit_remove_files:
285 sysfs_remove_group(&client->dev.kobj, &ds620_group);
286exit_free:
287 kfree(data);
288exit:
289 return err;
290}
291
292static int ds620_remove(struct i2c_client *client)
293{
294 struct ds620_data *data = i2c_get_clientdata(client);
295
296 hwmon_device_unregister(data->hwmon_dev);
297 sysfs_remove_group(&client->dev.kobj, &ds620_group);
298
299 kfree(data);
300
301 return 0;
302}
303
304static const struct i2c_device_id ds620_id[] = {
305 {"ds620", 0},
306 {}
307};
308
309MODULE_DEVICE_TABLE(i2c, ds620_id);
310
311/* This is the driver that will be inserted */
312static struct i2c_driver ds620_driver = {
313 .class = I2C_CLASS_HWMON,
314 .driver = {
315 .name = "ds620",
316 },
317 .probe = ds620_probe,
318 .remove = ds620_remove,
319 .id_table = ds620_id,
320};
321
322static int __init ds620_init(void)
323{
324 return i2c_add_driver(&ds620_driver);
325}
326
327static void __exit ds620_exit(void)
328{
329 i2c_del_driver(&ds620_driver);
330}
331
332MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
333MODULE_DESCRIPTION("DS620 driver");
334MODULE_LICENSE("GPL");
335
336module_init(ds620_init);
337module_exit(ds620_exit);