temp revert rk change
[firefly-linux-kernel-4.4.55.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 STATUS_RD                       0x02
39 #define CONFIG_RD                       0x03
40
41 #define CONFIG_WR                       0x09
42 #define CONV_RATE_WR                    0x0A
43 #define LOCAL_TEMP_HI_LIMIT_WR          0x0B
44 #define EXT_TEMP_HI_LIMIT_HI_BYTE       0x0D
45 #define OFFSET_WR                       0x11
46 #define EXT_THERM_LIMIT_WR              0x19
47 #define LOCAL_THERM_LIMIT_WR            0x20
48 #define THERM_HYSTERESIS_WR             0x21
49
50 /* Configuration Register Bits */
51 #define EXTENDED_RANGE_BIT              (0x1 << 2)
52 #define THERM2_BIT                      (0x1 << 5)
53 #define STANDBY_BIT                     (0x1 << 6)
54
55 /* Max Temperature Measurements */
56 #define EXTENDED_RANGE_OFFSET           64U
57 #define STANDARD_RANGE_MAX              127U
58 #define EXTENDED_RANGE_MAX              (150U + EXTENDED_RANGE_OFFSET)
59
60 struct nct1008_data {
61         struct work_struct work;
62         struct i2c_client *client;
63         struct mutex mutex;
64         u8 config;
65         void (*alarm_fn)(bool raised);
66 };
67
68 static void nct1008_enable(struct i2c_client *client)
69 {
70         struct nct1008_data *data = i2c_get_clientdata(client);
71
72         i2c_smbus_write_byte_data(client, CONFIG_WR,
73                                   data->config & ~STANDBY_BIT);
74 }
75
76 static void nct1008_disable(struct i2c_client *client)
77 {
78         struct nct1008_data *data = i2c_get_clientdata(client);
79
80         i2c_smbus_write_byte_data(client, CONFIG_WR,
81                                   data->config | STANDBY_BIT);
82 }
83
84
85 static void nct1008_work_func(struct work_struct *work)
86 {
87         struct nct1008_data *data = container_of(work, struct nct1008_data, work);
88         int irq = data->client->irq;
89
90         mutex_lock(&data->mutex);
91
92         if (data->alarm_fn) {
93                 /* Therm2 line is active low */
94                 data->alarm_fn(!gpio_get_value(irq_to_gpio(irq)));
95         }
96
97         mutex_unlock(&data->mutex);
98 }
99
100 static irqreturn_t nct1008_irq(int irq, void *dev_id)
101 {
102         struct nct1008_data *data = dev_id;
103         schedule_work(&data->work);
104
105         return IRQ_HANDLED;
106 }
107
108 static inline u8 value_to_temperature(bool extended, u8 value)
109 {
110         return (extended ? (u8)(value - EXTENDED_RANGE_OFFSET) : value);
111 }
112
113 static inline u8 temperature_to_value(bool extended, u8 temp)
114 {
115         return (extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : temp);
116 }
117
118 static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
119 {
120         struct i2c_client *client           = data->client;
121         struct nct1008_platform_data *pdata = client->dev.platform_data;
122         u8 value;
123         int err;
124
125         if (!pdata || !pdata->supported_hwrev)
126                 return -ENODEV;
127
128         /*
129          * Initial Configuration - device is placed in standby and
130          * ALERT/THERM2 pin is configured as THERM2
131          */
132         data->config = value = pdata->ext_range ?
133                 (STANDBY_BIT | THERM2_BIT | EXTENDED_RANGE_BIT) :
134                 (STANDBY_BIT | THERM2_BIT);
135
136         err = i2c_smbus_write_byte_data(client, CONFIG_WR, value);
137         if (err < 0)
138                 goto error;
139
140         /* Temperature conversion rate */
141         err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate);
142         if (err < 0)
143                 goto error;
144
145         /* External temperature h/w shutdown limit */
146         value = temperature_to_value(pdata->ext_range, pdata->shutdown_ext_limit);
147         err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value);
148         if (err < 0)
149                 goto error;
150
151         /* Local temperature h/w shutdown limit */
152         value = temperature_to_value(pdata->ext_range, pdata->shutdown_local_limit);
153         err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value);
154         if (err < 0)
155                 goto error;
156
157         /* External Temperature Throttling limit */
158         value = temperature_to_value(pdata->ext_range, pdata->throttling_ext_limit);
159         err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE, value);
160         if (err < 0)
161                 goto error;
162
163         /* Local Temperature Throttling limit */
164         value = pdata->ext_range ? EXTENDED_RANGE_MAX : STANDARD_RANGE_MAX;
165         err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR, value);
166         if (err < 0)
167                 goto error;
168
169         /* Remote channel offset */
170         err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset);
171         if (err < 0)
172                 goto error;
173
174         /* THERM hysteresis */
175         err = i2c_smbus_write_byte_data(client, THERM_HYSTERESIS_WR, pdata->hysteresis);
176         if (err < 0)
177                 goto error;
178
179         data->alarm_fn = pdata->alarm_fn;
180         return 0;
181 error:
182         return err;
183 }
184
185 static int __devinit nct1008_configure_irq(struct nct1008_data *data)
186 {
187         INIT_WORK(&data->work, nct1008_work_func);
188
189         return request_irq(data->client->irq, nct1008_irq, IRQF_TRIGGER_RISING |
190                                 IRQF_TRIGGER_FALLING, DRIVER_NAME, data);
191 }
192
193 static int __devinit nct1008_probe(struct i2c_client *client, const struct i2c_device_id *id)
194 {
195         struct nct1008_data *data;
196         int err;
197
198         data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL);
199
200         if (!data)
201                 return -ENOMEM;
202
203         data->client = client;
204         i2c_set_clientdata(client, data);
205         mutex_init(&data->mutex);
206
207         err = nct1008_configure_sensor(data);   /* sensor is in standby */
208         if (err < 0)
209                 goto error;
210
211         err = nct1008_configure_irq(data);
212         if (err < 0)
213                 goto error;
214
215         nct1008_enable(client);         /* sensor is running */
216
217         schedule_work(&data->work);             /* check initial state */
218
219         return 0;
220
221 error:
222         kfree(data);
223         return err;
224 }
225
226 static int __devexit nct1008_remove(struct i2c_client *client)
227 {
228         struct nct1008_data *data = i2c_get_clientdata(client);
229
230         free_irq(data->client->irq, data);
231         cancel_work_sync(&data->work);
232         kfree(data);
233
234         return 0;
235 }
236
237 #ifdef CONFIG_PM
238 static int nct1008_suspend(struct i2c_client *client, pm_message_t state)
239 {
240         disable_irq(client->irq);
241         nct1008_disable(client);
242
243         return 0;
244 }
245
246 static int nct1008_resume(struct i2c_client *client)
247 {
248         struct nct1008_data *data = i2c_get_clientdata(client);
249
250         nct1008_enable(client);
251         enable_irq(client->irq);
252         schedule_work(&data->work);
253
254         return 0;
255 }
256 #endif
257
258 static const struct i2c_device_id nct1008_id[] = {
259         { DRIVER_NAME, 0 },
260         { }
261 };
262 MODULE_DEVICE_TABLE(i2c, nct1008_id);
263
264 static struct i2c_driver nct1008_driver = {
265         .driver = {
266                 .name   = DRIVER_NAME,
267         },
268         .probe          = nct1008_probe,
269         .remove         = __devexit_p(nct1008_remove),
270         .id_table       = nct1008_id,
271 #ifdef CONFIG_PM
272         .suspend        = nct1008_suspend,
273         .resume         = nct1008_resume,
274 #endif
275 };
276
277 static int __init nct1008_init(void)
278 {
279         return i2c_add_driver(&nct1008_driver);
280 }
281
282 static void __exit nct1008_exit(void)
283 {
284         i2c_del_driver(&nct1008_driver);
285 }
286
287 MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008");
288 MODULE_LICENSE("GPL");
289
290 module_init (nct1008_init);
291 module_exit (nct1008_exit);