nct1008: add support for external sensor readings
[linux-2.6.git] / drivers / misc / nct1008.c
1 /*
2  * drivers/misc/nct1008.c
3  *
4  * Driver for NCT1008, temperature monitoring device from ON Semiconductors
5  *
6  * Copyright (c) 2010, NVIDIA Corporation.
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, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22
23
24 #include <linux/interrupt.h>
25 #include <linux/mutex.h>
26 #include <linux/module.h>
27 #include <linux/i2c.h>
28 #include <linux/slab.h>
29 #include <linux/err.h>
30 #include <linux/gpio.h>
31
32 #include <linux/nct1008.h>
33
34 #define DRIVER_NAME "nct1008"
35
36 /* Register Addresses */
37 #define LOCAL_TEMP_RD                   0x00
38 #define EXT_HI_TEMP_RD                  0x01
39 #define EXT_LO_TEMP_RD                  0x10
40 #define STATUS_RD                       0x02
41 #define CONFIG_RD                       0x03
42
43 #define CONFIG_WR                       0x09
44 #define CONV_RATE_WR                    0x0A
45 #define LOCAL_TEMP_HI_LIMIT_WR          0x0B
46 #define EXT_TEMP_HI_LIMIT_HI_BYTE       0x0D
47 #define OFFSET_WR                       0x11
48 #define EXT_THERM_LIMIT_WR              0x19
49 #define LOCAL_THERM_LIMIT_WR            0x20
50 #define THERM_HYSTERESIS_WR             0x21
51
52 /* Configuration Register Bits */
53 #define EXTENDED_RANGE_BIT              (0x1 << 2)
54 #define THERM2_BIT                      (0x1 << 5)
55 #define STANDBY_BIT                     (0x1 << 6)
56
57 /* Max Temperature Measurements */
58 #define EXTENDED_RANGE_OFFSET           64U
59 #define STANDARD_RANGE_MAX              127U
60 #define EXTENDED_RANGE_MAX              (150U + EXTENDED_RANGE_OFFSET)
61
62 struct nct1008_data {
63         struct work_struct work;
64         struct i2c_client *client;
65         struct mutex mutex;
66         u8 config;
67         void (*alarm_fn)(bool raised);
68 };
69
70 static ssize_t nct1008_show_temp(struct device *dev,
71         struct device_attribute *attr, char *buf)
72 {
73         struct i2c_client *client = to_i2c_client(dev);
74         signed int temp_value = 0;
75         u8 data = 0;
76
77         if (!dev || !buf || !attr)
78                 return -EINVAL;
79
80         data = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
81         if (data < 0) {
82                 dev_err(&client->dev, "%s: failed to read "
83                         "temperature\n", __func__);
84                 return -EINVAL;
85         }
86
87         temp_value = (signed int)data;
88         return sprintf(buf, "%d\n", temp_value);
89 }
90
91 static ssize_t nct1008_show_ext_temp(struct device *dev,
92         struct device_attribute *attr, char *buf)
93 {
94         struct i2c_client *client = to_i2c_client(dev);
95         signed int temp_value = 0;
96         u8 data = 0;
97
98         if (!dev || !buf || !attr)
99                 return -EINVAL;
100
101         data = i2c_smbus_read_byte_data(client, EXT_HI_TEMP_RD);
102         if (data < 0) {
103                 dev_err(&client->dev, "%s: failed to read "
104                         "ext_temperature\n", __func__);
105                 return -EINVAL;
106         }
107
108         temp_value = (signed int)data;
109
110         data = i2c_smbus_read_byte_data(client, EXT_LO_TEMP_RD);
111
112         return sprintf(buf, "%d.%d\n", temp_value, (25 * (data >> 6)));
113 }
114
115 static DEVICE_ATTR(temperature, S_IRUGO, nct1008_show_temp, NULL);
116 static DEVICE_ATTR(ext_temperature, S_IRUGO, nct1008_show_ext_temp, NULL);
117
118 static struct attribute *nct1008_attributes[] = {
119         &dev_attr_temperature.attr,
120         &dev_attr_ext_temperature.attr,
121         NULL
122 };
123
124 static const struct attribute_group nct1008_attr_group = {
125         .attrs = nct1008_attributes,
126 };
127
128 static void nct1008_enable(struct i2c_client *client)
129 {
130         struct nct1008_data *data = i2c_get_clientdata(client);
131
132         i2c_smbus_write_byte_data(client, CONFIG_WR,
133                                   data->config & ~STANDBY_BIT);
134 }
135
136 static void nct1008_disable(struct i2c_client *client)
137 {
138         struct nct1008_data *data = i2c_get_clientdata(client);
139
140         i2c_smbus_write_byte_data(client, CONFIG_WR,
141                                   data->config | STANDBY_BIT);
142 }
143
144
145 static void nct1008_work_func(struct work_struct *work)
146 {
147         struct nct1008_data *data = container_of(work, struct nct1008_data, work);
148         int irq = data->client->irq;
149
150         mutex_lock(&data->mutex);
151
152         if (data->alarm_fn) {
153                 /* Therm2 line is active low */
154                 data->alarm_fn(!gpio_get_value(irq_to_gpio(irq)));
155         }
156
157         mutex_unlock(&data->mutex);
158 }
159
160 static irqreturn_t nct1008_irq(int irq, void *dev_id)
161 {
162         struct nct1008_data *data = dev_id;
163         schedule_work(&data->work);
164
165         return IRQ_HANDLED;
166 }
167
168 static inline u8 value_to_temperature(bool extended, u8 value)
169 {
170         return (extended ? (u8)(value - EXTENDED_RANGE_OFFSET) : value);
171 }
172
173 static inline u8 temperature_to_value(bool extended, u8 temp)
174 {
175         return (extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : temp);
176 }
177
178 static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
179 {
180         struct i2c_client *client           = data->client;
181         struct nct1008_platform_data *pdata = client->dev.platform_data;
182         u8 value;
183         int err;
184
185         if (!pdata || !pdata->supported_hwrev)
186                 return -ENODEV;
187
188         /*
189          * Initial Configuration - device is placed in standby and
190          * ALERT/THERM2 pin is configured as THERM2
191          */
192         data->config = value = pdata->ext_range ?
193                 (STANDBY_BIT | THERM2_BIT | EXTENDED_RANGE_BIT) :
194                 (STANDBY_BIT | THERM2_BIT);
195
196         err = i2c_smbus_write_byte_data(client, CONFIG_WR, value);
197         if (err < 0)
198                 goto error;
199
200         /* Temperature conversion rate */
201         err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate);
202         if (err < 0)
203                 goto error;
204
205         /* External temperature h/w shutdown limit */
206         value = temperature_to_value(pdata->ext_range, pdata->shutdown_ext_limit);
207         err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value);
208         if (err < 0)
209                 goto error;
210
211         /* Local temperature h/w shutdown limit */
212         value = temperature_to_value(pdata->ext_range, pdata->shutdown_local_limit);
213         err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value);
214         if (err < 0)
215                 goto error;
216
217         /* External Temperature Throttling limit */
218         value = temperature_to_value(pdata->ext_range, pdata->throttling_ext_limit);
219         err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE, value);
220         if (err < 0)
221                 goto error;
222
223         /* Local Temperature Throttling limit */
224         value = pdata->ext_range ? EXTENDED_RANGE_MAX : STANDARD_RANGE_MAX;
225         err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR, value);
226         if (err < 0)
227                 goto error;
228
229         /* Remote channel offset */
230         err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset);
231         if (err < 0)
232                 goto error;
233
234         /* THERM hysteresis */
235         err = i2c_smbus_write_byte_data(client, THERM_HYSTERESIS_WR, pdata->hysteresis);
236         if (err < 0)
237                 goto error;
238
239         data->alarm_fn = pdata->alarm_fn;
240         return 0;
241 error:
242         return err;
243 }
244
245 static int __devinit nct1008_configure_irq(struct nct1008_data *data)
246 {
247         INIT_WORK(&data->work, nct1008_work_func);
248
249         return request_irq(data->client->irq, nct1008_irq, IRQF_TRIGGER_RISING |
250                                 IRQF_TRIGGER_FALLING, DRIVER_NAME, data);
251 }
252
253 static int __devinit nct1008_probe(struct i2c_client *client, const struct i2c_device_id *id)
254 {
255         struct nct1008_data *data;
256         int err;
257
258         data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL);
259
260         if (!data)
261                 return -ENOMEM;
262
263         data->client = client;
264         i2c_set_clientdata(client, data);
265         mutex_init(&data->mutex);
266
267         err = nct1008_configure_sensor(data);   /* sensor is in standby */
268         if (err < 0)
269                 goto error;
270
271         err = nct1008_configure_irq(data);
272         if (err < 0)
273                 goto error;
274
275         /* register sysfs hooks */
276         err = sysfs_create_group(&client->dev.kobj, &nct1008_attr_group);
277         if (err < 0)
278                 goto error;
279
280         dev_info(&client->dev, "%s: initialized\n", __func__);
281
282         nct1008_enable(client);         /* sensor is running */
283
284         schedule_work(&data->work);             /* check initial state */
285
286         return 0;
287
288 error:
289         kfree(data);
290         return err;
291 }
292
293 static int __devexit nct1008_remove(struct i2c_client *client)
294 {
295         struct nct1008_data *data = i2c_get_clientdata(client);
296
297         free_irq(data->client->irq, data);
298         cancel_work_sync(&data->work);
299         sysfs_remove_group(&client->dev.kobj, &nct1008_attr_group);
300         kfree(data);
301
302         return 0;
303 }
304
305 #ifdef CONFIG_PM
306 static int nct1008_suspend(struct i2c_client *client, pm_message_t state)
307 {
308         disable_irq(client->irq);
309         nct1008_disable(client);
310
311         return 0;
312 }
313
314 static int nct1008_resume(struct i2c_client *client)
315 {
316         struct nct1008_data *data = i2c_get_clientdata(client);
317
318         nct1008_enable(client);
319         enable_irq(client->irq);
320         schedule_work(&data->work);
321
322         return 0;
323 }
324 #endif
325
326 static const struct i2c_device_id nct1008_id[] = {
327         { DRIVER_NAME, 0 },
328         { }
329 };
330 MODULE_DEVICE_TABLE(i2c, nct1008_id);
331
332 static struct i2c_driver nct1008_driver = {
333         .driver = {
334                 .name   = DRIVER_NAME,
335         },
336         .probe          = nct1008_probe,
337         .remove         = __devexit_p(nct1008_remove),
338         .id_table       = nct1008_id,
339 #ifdef CONFIG_PM
340         .suspend        = nct1008_suspend,
341         .resume         = nct1008_resume,
342 #endif
343 };
344
345 static int __init nct1008_init(void)
346 {
347         return i2c_add_driver(&nct1008_driver);
348 }
349
350 static void __exit nct1008_exit(void)
351 {
352         i2c_del_driver(&nct1008_driver);
353 }
354
355 MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008");
356 MODULE_LICENSE("GPL");
357
358 module_init (nct1008_init);
359 module_exit (nct1008_exit);