Blame view

drivers/hwmon/ds1621.c 9.19 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /*
      ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
               monitoring
      Christian W. Zuckschwerdt  <zany@triq.net>  2000-11-23
      based on lm75.c by Frodo Looijaard <frodol@dds.nl>
      Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with 
      the help of Jean Delvare <khali@linux-fr.org>
  
      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/jiffies.h>
  #include <linux/i2c.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
29
  #include <linux/hwmon.h>
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
30
  #include <linux/hwmon-sysfs.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
31
  #include <linux/err.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
32
  #include <linux/mutex.h>
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
33
  #include <linux/sysfs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
  #include "lm75.h"
  
  /* Addresses to scan */
25e9c86d5   Mark M. Hoffman   hwmon: normal_i2c...
37
  static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  					0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  
  /* Insmod parameters */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  static int polarity = -1;
  module_param(polarity, int, 0);
  MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
  
  /* Many DS1621 constants specified below */
  /* Config register used for detection         */
  /*  7    6    5    4    3    2    1    0      */
  /* |Done|THF |TLF |NVB | X  | X  |POL |1SHOT| */
  #define DS1621_REG_CONFIG_NVB		0x10
  #define DS1621_REG_CONFIG_POLARITY	0x02
  #define DS1621_REG_CONFIG_1SHOT		0x01
  #define DS1621_REG_CONFIG_DONE		0x80
  
  /* The DS1621 registers */
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
55
56
57
58
59
  static const u8 DS1621_REG_TEMP[3] = {
  	0xAA,		/* input, word, RO */
  	0xA2,		/* min, word, RW */
  	0xA1,		/* max, word, RW */
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
  #define DS1621_REG_CONF			0xAC /* byte, RW */
  #define DS1621_COM_START		0xEE /* no data */
  #define DS1621_COM_STOP			0x22 /* no data */
  
  /* The DS1621 configuration register */
  #define DS1621_ALARM_TEMP_HIGH		0x40
  #define DS1621_ALARM_TEMP_LOW		0x20
75819f01a   Jean Delvare   hwmon/ds1621: Min...
67
  /* Conversions */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
  #define ALARMS_FROM_REG(val) ((val) & \
                                (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
  
  /* Each client has this additional data */
  struct ds1621_data {
1beeffe43   Tony Jones   hwmon: Convert fr...
73
  	struct device *hwmon_dev;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
74
  	struct mutex update_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
  	char valid;			/* !=0 if following fields are valid */
  	unsigned long last_updated;	/* In jiffies */
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
77
  	u16 temp[3];			/* Register values, word */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
  	u8 conf;			/* Register encoding, combined */
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  static void ds1621_init_client(struct i2c_client *client)
  {
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
82
83
84
  	u8 conf, new_conf;
  
  	new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
44bbe87e9   Steven Cole   [PATCH] Spelling ...
85
  	/* switch to continuous conversion mode */
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
86
  	new_conf &= ~DS1621_REG_CONFIG_1SHOT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
  
  	/* setup output polarity */
  	if (polarity == 0)
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
90
  		new_conf &= ~DS1621_REG_CONFIG_POLARITY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	else if (polarity == 1)
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
92
  		new_conf |= DS1621_REG_CONFIG_POLARITY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  	
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
94
95
  	if (conf != new_conf)
  		i2c_smbus_write_byte_data(client, DS1621_REG_CONF, new_conf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
  	
  	/* start conversion */
  	i2c_smbus_write_byte(client, DS1621_COM_START);
  }
9202add67   Jean Delvare   hwmon: (ds1621) R...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  static struct ds1621_data *ds1621_update_client(struct device *dev)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ds1621_data *data = i2c_get_clientdata(client);
  	u8 new_conf;
  
  	mutex_lock(&data->update_lock);
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  	    || !data->valid) {
  		int i;
  
  		dev_dbg(&client->dev, "Starting ds1621 update
  ");
594592dc6   Jean Delvare   hwmon: (ds1621) C...
114
  		data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
9202add67   Jean Delvare   hwmon: (ds1621) R...
115
116
  
  		for (i = 0; i < ARRAY_SIZE(data->temp); i++)
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
117
  			data->temp[i] = i2c_smbus_read_word_swapped(client,
594592dc6   Jean Delvare   hwmon: (ds1621) C...
118
  							 DS1621_REG_TEMP[i]);
9202add67   Jean Delvare   hwmon: (ds1621) R...
119
120
121
122
123
124
125
126
  
  		/* reset alarms if necessary */
  		new_conf = data->conf;
  		if (data->temp[0] > data->temp[1])	/* input > min */
  			new_conf &= ~DS1621_ALARM_TEMP_LOW;
  		if (data->temp[0] < data->temp[2])	/* input < max */
  			new_conf &= ~DS1621_ALARM_TEMP_HIGH;
  		if (data->conf != new_conf)
594592dc6   Jean Delvare   hwmon: (ds1621) C...
127
128
  			i2c_smbus_write_byte_data(client, DS1621_REG_CONF,
  						  new_conf);
9202add67   Jean Delvare   hwmon: (ds1621) R...
129
130
131
132
133
134
135
136
137
  
  		data->last_updated = jiffies;
  		data->valid = 1;
  	}
  
  	mutex_unlock(&data->update_lock);
  
  	return data;
  }
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
138
139
140
141
142
143
144
145
  static ssize_t show_temp(struct device *dev, struct device_attribute *da,
  			 char *buf)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  	struct ds1621_data *data = ds1621_update_client(dev);
  	return sprintf(buf, "%d
  ",
  		       LM75_TEMP_FROM_REG(data->temp[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  }
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
147
148
149
150
151
  static ssize_t set_temp(struct device *dev, struct device_attribute *da,
  			const char *buf, size_t count)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  	struct i2c_client *client = to_i2c_client(dev);
e4879e28a   Jean Delvare   hwmon: (ds1621) A...
152
  	struct ds1621_data *data = i2c_get_clientdata(client);
5bfedac04   Christian Hohnstaedt   hwmon: Allow writ...
153
  	u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154

46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
155
156
  	mutex_lock(&data->update_lock);
  	data->temp[attr->index] = val;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
157
158
  	i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index],
  				     data->temp[attr->index]);
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
159
160
161
  	mutex_unlock(&data->update_lock);
  	return count;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162

46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
163
164
  static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
  			   char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
  {
  	struct ds1621_data *data = ds1621_update_client(dev);
  	return sprintf(buf, "%d
  ", ALARMS_FROM_REG(data->conf));
  }
87f0f31ba   Jean Delvare   hwmon/ds1621: Cre...
170
171
172
173
174
175
176
177
  static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
  			  char *buf)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  	struct ds1621_data *data = ds1621_update_client(dev);
  	return sprintf(buf, "%d
  ", !!(data->conf & attr->index));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
179
180
181
  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
  static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1);
  static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2);
87f0f31ba   Jean Delvare   hwmon/ds1621: Cre...
182
183
184
185
  static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
  		DS1621_ALARM_TEMP_LOW);
  static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
  		DS1621_ALARM_TEMP_HIGH);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186

a5ebe668a   Jean Delvare   hwmon: Fix unchec...
187
  static struct attribute *ds1621_attributes[] = {
46a2e71ce   Jean Delvare   hwmon/ds1621: Use...
188
189
190
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	&sensor_dev_attr_temp1_min.dev_attr.attr,
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
87f0f31ba   Jean Delvare   hwmon/ds1621: Cre...
191
192
  	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
193
194
195
196
197
198
199
  	&dev_attr_alarms.attr,
  	NULL
  };
  
  static const struct attribute_group ds1621_group = {
  	.attrs = ds1621_attributes,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200

70313eabf   Jean Delvare   hwmon: (ds1621) C...
201
  /* Return 0 if detection is successful, -ENODEV otherwise */
310ec7921   Jean Delvare   i2c: Drop the kin...
202
  static int ds1621_detect(struct i2c_client *client,
70313eabf   Jean Delvare   hwmon: (ds1621) C...
203
  			 struct i2c_board_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
  {
70313eabf   Jean Delvare   hwmon: (ds1621) C...
205
  	struct i2c_adapter *adapter = client->adapter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	int conf, temp;
70313eabf   Jean Delvare   hwmon: (ds1621) C...
207
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA 
  				     | I2C_FUNC_SMBUS_WORD_DATA 
  				     | I2C_FUNC_SMBUS_WRITE_BYTE))
70313eabf   Jean Delvare   hwmon: (ds1621) C...
212
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
  
  	/* Now, we do the remaining detection. It is lousy. */
52df6440a   Jean Delvare   hwmon: Clean up d...
215
216
217
218
219
220
221
222
223
  	/* The NVB bit should be low if no EEPROM write has been  requested
  	   during the latest 10ms, which is highly improbable in our case. */
  	conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
  	if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
  		return -ENODEV;
  	/* The 7 lowest bits of a temperature should always be 0. */
  	for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
  		temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]);
  		if (temp < 0 || (temp & 0x7f00))
70313eabf   Jean Delvare   hwmon: (ds1621) C...
224
  			return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  	}
70313eabf   Jean Delvare   hwmon: (ds1621) C...
226
  	strlcpy(info->type, "ds1621", I2C_NAME_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227

70313eabf   Jean Delvare   hwmon: (ds1621) C...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  	return 0;
  }
  
  static int ds1621_probe(struct i2c_client *client,
  			const struct i2c_device_id *id)
  {
  	struct ds1621_data *data;
  	int err;
  
  	data = kzalloc(sizeof(struct ds1621_data), GFP_KERNEL);
  	if (!data) {
  		err = -ENOMEM;
  		goto exit;
  	}
  
  	i2c_set_clientdata(client, data);
  	mutex_init(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
  
  	/* Initialize the DS1621 chip */
75819f01a   Jean Delvare   hwmon/ds1621: Min...
247
  	ds1621_init_client(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  
  	/* Register sysfs hooks */
75819f01a   Jean Delvare   hwmon/ds1621: Min...
250
  	if ((err = sysfs_create_group(&client->dev.kobj, &ds1621_group)))
70313eabf   Jean Delvare   hwmon: (ds1621) C...
251
  		goto exit_free;
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
252

1beeffe43   Tony Jones   hwmon: Convert fr...
253
254
255
  	data->hwmon_dev = hwmon_device_register(&client->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
256
  		goto exit_remove_files;
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
257
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	return 0;
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
259
        exit_remove_files:
75819f01a   Jean Delvare   hwmon/ds1621: Min...
260
  	sysfs_remove_group(&client->dev.kobj, &ds1621_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
        exit_free:
  	kfree(data);
        exit:
  	return err;
  }
70313eabf   Jean Delvare   hwmon: (ds1621) C...
266
  static int ds1621_remove(struct i2c_client *client)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  {
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
268
  	struct ds1621_data *data = i2c_get_clientdata(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269

1beeffe43   Tony Jones   hwmon: Convert fr...
270
  	hwmon_device_unregister(data->hwmon_dev);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
271
  	sysfs_remove_group(&client->dev.kobj, &ds1621_group);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
272

943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
273
  	kfree(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
  
  	return 0;
  }
9202add67   Jean Delvare   hwmon: (ds1621) R...
277
  static const struct i2c_device_id ds1621_id[] = {
1f86df49d   Jean Delvare   i2c: Drop I2C_CLI...
278
279
  	{ "ds1621", 0 },
  	{ "ds1625", 0 },
9202add67   Jean Delvare   hwmon: (ds1621) R...
280
281
282
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ds1621_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

9202add67   Jean Delvare   hwmon: (ds1621) R...
284
285
286
287
288
289
290
291
292
293
  /* This is the driver that will be inserted */
  static struct i2c_driver ds1621_driver = {
  	.class		= I2C_CLASS_HWMON,
  	.driver = {
  		.name	= "ds1621",
  	},
  	.probe		= ds1621_probe,
  	.remove		= ds1621_remove,
  	.id_table	= ds1621_id,
  	.detect		= ds1621_detect,
c3813d6af   Jean Delvare   i2c: Get rid of s...
294
  	.address_list	= normal_i2c,
9202add67   Jean Delvare   hwmon: (ds1621) R...
295
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  
  static int __init ds1621_init(void)
  {
  	return i2c_add_driver(&ds1621_driver);
  }
  
  static void __exit ds1621_exit(void)
  {
  	i2c_del_driver(&ds1621_driver);
  }
  
  
  MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
  MODULE_DESCRIPTION("DS1621 driver");
  MODULE_LICENSE("GPL");
  
  module_init(ds1621_init);
  module_exit(ds1621_exit);