aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorAndre Prendel <andre.prendel@gmx.de>2009-09-15 11:18:11 -0400
committerJean Delvare <khali@linux-fr.org>2009-09-15 11:18:11 -0400
commit9410700b881f867a50dd8dc3204372fd9dccd8f8 (patch)
treed41a31183e3716bb2ac8e548843653bbcf54105d /drivers/hwmon
parent5bed13f5809927be10facccb63add834b712df51 (diff)
hwmon: Add driver for Texas Instruments TMP421/422/423 sensor chips
Add support for Texas Instruments TMP421/422/423 temperature sensor IC. TI's TMP421/422/423 are I2C temperature sensor chips. These chips are similar to TI's TMP401/411 chips, but with reduced functionality (only temperature measurement). The chips have one local sensor and up to three (TMP423) remote sensors. Signed-off-by: Andre Prendel <andre.prendel@gmx.de> Acked-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/tmp421.c347
3 files changed, 358 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2e25b7a827d3..ee57a4a2cd72 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -814,6 +814,16 @@ config SENSORS_TMP401
814 This driver can also be built as a module. If so, the module 814 This driver can also be built as a module. If so, the module
815 will be called tmp401. 815 will be called tmp401.
816 816
817config SENSORS_TMP421
818 tristate "Texas Instruments TMP421 and compatible"
819 depends on I2C && EXPERIMENTAL
820 help
821 If you say yes here you get support for Texas Instruments TMP421,
822 TMP422 and TMP423 temperature sensor chips.
823
824 This driver can also be built as a module. If so, the module
825 will be called tmp421.
826
817config SENSORS_VIA686A 827config SENSORS_VIA686A
818 tristate "VIA686A" 828 tristate "VIA686A"
819 depends on PCI 829 depends on PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 7f239a247c33..b577b4997207 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
84obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o 84obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
85obj-$(CONFIG_SENSORS_THMC50) += thmc50.o 85obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
86obj-$(CONFIG_SENSORS_TMP401) += tmp401.o 86obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
87obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
87obj-$(CONFIG_SENSORS_VIA686A) += via686a.o 88obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
88obj-$(CONFIG_SENSORS_VT1211) += vt1211.o 89obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
89obj-$(CONFIG_SENSORS_VT8231) += vt8231.o 90obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
new file mode 100644
index 000000000000..20924343431b
--- /dev/null
+++ b/drivers/hwmon/tmp421.c
@@ -0,0 +1,347 @@
1/* tmp421.c
2 *
3 * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
4 * Preliminary support by:
5 * Melvin Rook, Raymond Ng
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22/*
23 * Driver for the Texas Instruments TMP421 SMBus temperature sensor IC.
24 * Supported models: TMP421, TMP422, TMP423
25 */
26
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/slab.h>
30#include <linux/jiffies.h>
31#include <linux/i2c.h>
32#include <linux/hwmon.h>
33#include <linux/hwmon-sysfs.h>
34#include <linux/err.h>
35#include <linux/mutex.h>
36#include <linux/sysfs.h>
37
38/* Addresses to scan */
39static unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
40 I2C_CLIENT_END };
41
42/* Insmod parameters */
43I2C_CLIENT_INSMOD_3(tmp421, tmp422, tmp423);
44
45/* The TMP421 registers */
46#define TMP421_CONFIG_REG_1 0x09
47#define TMP421_CONVERSION_RATE_REG 0x0B
48#define TMP421_MANUFACTURER_ID_REG 0xFE
49#define TMP421_DEVICE_ID_REG 0xFF
50
51static const u8 TMP421_TEMP_MSB[4] = { 0x00, 0x01, 0x02, 0x03 };
52static const u8 TMP421_TEMP_LSB[4] = { 0x10, 0x11, 0x12, 0x13 };
53
54/* Flags */
55#define TMP421_CONFIG_SHUTDOWN 0x40
56#define TMP421_CONFIG_RANGE 0x04
57
58/* Manufacturer / Device ID's */
59#define TMP421_MANUFACTURER_ID 0x55
60#define TMP421_DEVICE_ID 0x21
61#define TMP422_DEVICE_ID 0x22
62#define TMP423_DEVICE_ID 0x23
63
64static const struct i2c_device_id tmp421_id[] = {
65 { "tmp421", tmp421 },
66 { "tmp422", tmp422 },
67 { "tmp423", tmp423 },
68 { }
69};
70MODULE_DEVICE_TABLE(i2c, tmp421_id);
71
72struct tmp421_data {
73 struct device *hwmon_dev;
74 struct mutex update_lock;
75 char valid;
76 unsigned long last_updated;
77 int kind;
78 u8 config;
79 s16 temp[4];
80};
81
82static int temp_from_s16(s16 reg)
83{
84 int temp = reg;
85
86 return (temp * 1000 + 128) / 256;
87}
88
89static int temp_from_u16(u16 reg)
90{
91 int temp = reg;
92
93 /* Add offset for extended temperature range. */
94 temp -= 64 * 256;
95
96 return (temp * 1000 + 128) / 256;
97}
98
99static struct tmp421_data *tmp421_update_device(struct device *dev)
100{
101 struct i2c_client *client = to_i2c_client(dev);
102 struct tmp421_data *data = i2c_get_clientdata(client);
103 int i;
104
105 mutex_lock(&data->update_lock);
106
107 if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
108 data->config = i2c_smbus_read_byte_data(client,
109 TMP421_CONFIG_REG_1);
110
111 for (i = 0; i <= data->kind; i++) {
112 data->temp[i] = i2c_smbus_read_byte_data(client,
113 TMP421_TEMP_MSB[i]) << 8;
114 data->temp[i] |= i2c_smbus_read_byte_data(client,
115 TMP421_TEMP_LSB[i]);
116 }
117 data->last_updated = jiffies;
118 data->valid = 1;
119 }
120
121 mutex_unlock(&data->update_lock);
122
123 return data;
124}
125
126static ssize_t show_temp_value(struct device *dev,
127 struct device_attribute *devattr, char *buf)
128{
129 int index = to_sensor_dev_attr(devattr)->index;
130 struct tmp421_data *data = tmp421_update_device(dev);
131 int temp;
132
133 mutex_lock(&data->update_lock);
134 if (data->config & TMP421_CONFIG_RANGE)
135 temp = temp_from_u16(data->temp[index]);
136 else
137 temp = temp_from_s16(data->temp[index]);
138 mutex_unlock(&data->update_lock);
139
140 return sprintf(buf, "%d\n", temp);
141}
142
143static ssize_t show_fault(struct device *dev,
144 struct device_attribute *devattr, char *buf)
145{
146 int index = to_sensor_dev_attr(devattr)->index;
147 struct tmp421_data *data = tmp421_update_device(dev);
148
149 /*
150 * The OPEN bit signals a fault. This is bit 0 of the temperature
151 * register (low byte).
152 */
153 if (data->temp[index] & 0x01)
154 return sprintf(buf, "1\n");
155 else
156 return sprintf(buf, "0\n");
157}
158
159static mode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a,
160 int n)
161{
162 struct device *dev = container_of(kobj, struct device, kobj);
163 struct tmp421_data *data = dev_get_drvdata(dev);
164 struct device_attribute *devattr;
165 unsigned int index;
166
167 devattr = container_of(a, struct device_attribute, attr);
168 index = to_sensor_dev_attr(devattr)->index;
169
170 if (data->kind > index)
171 return a->mode;
172
173 return 0;
174}
175
176static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0);
177static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1);
178static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
179static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2);
180static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
181static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3);
182static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
183
184static struct attribute *tmp421_attr[] = {
185 &sensor_dev_attr_temp1_input.dev_attr.attr,
186 &sensor_dev_attr_temp2_input.dev_attr.attr,
187 &sensor_dev_attr_temp2_fault.dev_attr.attr,
188 &sensor_dev_attr_temp3_input.dev_attr.attr,
189 &sensor_dev_attr_temp3_fault.dev_attr.attr,
190 &sensor_dev_attr_temp4_input.dev_attr.attr,
191 &sensor_dev_attr_temp4_fault.dev_attr.attr,
192 NULL
193};
194
195static const struct attribute_group tmp421_group = {
196 .attrs = tmp421_attr,
197 .is_visible = tmp421_is_visible,
198};
199
200static int tmp421_init_client(struct i2c_client *client)
201{
202 int config, config_orig;
203
204 /* Set the conversion rate to 2 Hz */
205 i2c_smbus_write_byte_data(client, TMP421_CONVERSION_RATE_REG, 0x05);
206
207 /* Start conversions (disable shutdown if necessary) */
208 config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
209 if (config < 0) {
210 dev_err(&client->dev, "Could not read configuration"
211 " register (%d)\n", config);
212 return -ENODEV;
213 }
214
215 config_orig = config;
216 config &= ~TMP421_CONFIG_SHUTDOWN;
217
218 if (config != config_orig) {
219 dev_info(&client->dev, "Enable monitoring chip\n");
220 i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_1, config);
221 }
222
223 return 0;
224}
225
226static int tmp421_detect(struct i2c_client *client, int kind,
227 struct i2c_board_info *info)
228{
229 struct i2c_adapter *adapter = client->adapter;
230 const char *names[] = { "TMP421", "TMP422", "TMP423" };
231
232 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
233 return -ENODEV;
234
235 if (kind <= 0) {
236 u8 reg;
237
238 reg = i2c_smbus_read_byte_data(client,
239 TMP421_MANUFACTURER_ID_REG);
240 if (reg != TMP421_MANUFACTURER_ID)
241 return -ENODEV;
242
243 reg = i2c_smbus_read_byte_data(client,
244 TMP421_DEVICE_ID_REG);
245 switch (reg) {
246 case TMP421_DEVICE_ID:
247 kind = tmp421;
248 break;
249 case TMP422_DEVICE_ID:
250 kind = tmp422;
251 break;
252 case TMP423_DEVICE_ID:
253 kind = tmp423;
254 break;
255 default:
256 return -ENODEV;
257 }
258 }
259 strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE);
260 dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n",
261 names[kind - 1], client->addr);
262
263 return 0;
264}
265
266static int tmp421_probe(struct i2c_client *client,
267 const struct i2c_device_id *id)
268{
269 struct tmp421_data *data;
270 int err;
271
272 data = kzalloc(sizeof(struct tmp421_data), GFP_KERNEL);
273 if (!data)
274 return -ENOMEM;
275
276 i2c_set_clientdata(client, data);
277 mutex_init(&data->update_lock);
278 data->kind = id->driver_data;
279
280 err = tmp421_init_client(client);
281 if (err)
282 goto exit_free;
283
284 err = sysfs_create_group(&client->dev.kobj, &tmp421_group);
285 if (err)
286 goto exit_free;
287
288 data->hwmon_dev = hwmon_device_register(&client->dev);
289 if (IS_ERR(data->hwmon_dev)) {
290 err = PTR_ERR(data->hwmon_dev);
291 data->hwmon_dev = NULL;
292 goto exit_remove;
293 }
294 return 0;
295
296exit_remove:
297 sysfs_remove_group(&client->dev.kobj, &tmp421_group);
298
299exit_free:
300 i2c_set_clientdata(client, NULL);
301 kfree(data);
302
303 return err;
304}
305
306static int tmp421_remove(struct i2c_client *client)
307{
308 struct tmp421_data *data = i2c_get_clientdata(client);
309
310 hwmon_device_unregister(data->hwmon_dev);
311 sysfs_remove_group(&client->dev.kobj, &tmp421_group);
312
313 i2c_set_clientdata(client, NULL);
314 kfree(data);
315
316 return 0;
317}
318
319static struct i2c_driver tmp421_driver = {
320 .class = I2C_CLASS_HWMON,
321 .driver = {
322 .name = "tmp421",
323 },
324 .probe = tmp421_probe,
325 .remove = tmp421_remove,
326 .id_table = tmp421_id,
327 .detect = tmp421_detect,
328 .address_data = &addr_data,
329};
330
331static int __init tmp421_init(void)
332{
333 return i2c_add_driver(&tmp421_driver);
334}
335
336static void __exit tmp421_exit(void)
337{
338 i2c_del_driver(&tmp421_driver);
339}
340
341MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>");
342MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor"
343 " driver");
344MODULE_LICENSE("GPL");
345
346module_init(tmp421_init);
347module_exit(tmp421_exit);