xref: /linux/drivers/hwmon/adt7410.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
1 /*
2  * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
3  *	 monitoring
4  * This driver handles the ADT7410 and compatible digital temperature sensors.
5  * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
6  * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
7  * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/slab.h>
27 #include <linux/jiffies.h>
28 #include <linux/i2c.h>
29 #include <linux/hwmon.h>
30 #include <linux/hwmon-sysfs.h>
31 #include <linux/err.h>
32 #include <linux/mutex.h>
33 #include <linux/delay.h>
34 
35 /*
36  * ADT7410 registers definition
37  */
38 
39 #define ADT7410_TEMPERATURE		0
40 #define ADT7410_STATUS			2
41 #define ADT7410_CONFIG			3
42 #define ADT7410_T_ALARM_HIGH		4
43 #define ADT7410_T_ALARM_LOW		6
44 #define ADT7410_T_CRIT			8
45 #define ADT7410_T_HYST			0xA
46 
47 /*
48  * ADT7410 status
49  */
50 #define ADT7410_STAT_T_LOW		(1 << 4)
51 #define ADT7410_STAT_T_HIGH		(1 << 5)
52 #define ADT7410_STAT_T_CRIT		(1 << 6)
53 #define ADT7410_STAT_NOT_RDY		(1 << 7)
54 
55 /*
56  * ADT7410 config
57  */
58 #define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
59 #define ADT7410_CT_POLARITY		(1 << 2)
60 #define ADT7410_INT_POLARITY		(1 << 3)
61 #define ADT7410_EVENT_MODE		(1 << 4)
62 #define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
63 #define ADT7410_FULL			(0 << 5 | 0 << 6)
64 #define ADT7410_PD			(1 << 5 | 1 << 6)
65 #define ADT7410_RESOLUTION		(1 << 7)
66 
67 /*
68  * ADT7410 masks
69  */
70 #define ADT7410_T13_VALUE_MASK			0xFFF8
71 #define ADT7410_T_HYST_MASK			0xF
72 
73 /* straight from the datasheet */
74 #define ADT7410_TEMP_MIN (-55000)
75 #define ADT7410_TEMP_MAX 150000
76 
77 enum adt7410_type {		/* keep sorted in alphabetical order */
78 	adt7410,
79 };
80 
81 /* Addresses scanned */
82 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
83 					I2C_CLIENT_END };
84 
85 static const u8 ADT7410_REG_TEMP[4] = {
86 	ADT7410_TEMPERATURE,		/* input */
87 	ADT7410_T_ALARM_HIGH,		/* high */
88 	ADT7410_T_ALARM_LOW,		/* low */
89 	ADT7410_T_CRIT,			/* critical */
90 };
91 
92 /* Each client has this additional data */
93 struct adt7410_data {
94 	struct device		*hwmon_dev;
95 	struct mutex		update_lock;
96 	u8			config;
97 	u8			oldconfig;
98 	bool			valid;		/* true if registers valid */
99 	unsigned long		last_updated;	/* In jiffies */
100 	s16			temp[4];	/* Register values,
101 						   0 = input
102 						   1 = high
103 						   2 = low
104 						   3 = critical */
105 	u8			hyst;		/* hysteresis offset */
106 };
107 
108 /*
109  * adt7410 register access by I2C
110  */
111 static int adt7410_temp_ready(struct i2c_client *client)
112 {
113 	int i, status;
114 
115 	for (i = 0; i < 6; i++) {
116 		status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
117 		if (status < 0)
118 			return status;
119 		if (!(status & ADT7410_STAT_NOT_RDY))
120 			return 0;
121 		msleep(60);
122 	}
123 	return -ETIMEDOUT;
124 }
125 
126 static struct adt7410_data *adt7410_update_device(struct device *dev)
127 {
128 	struct i2c_client *client = to_i2c_client(dev);
129 	struct adt7410_data *data = i2c_get_clientdata(client);
130 	struct adt7410_data *ret = data;
131 	mutex_lock(&data->update_lock);
132 
133 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
134 	    || !data->valid) {
135 		int i, status;
136 
137 		dev_dbg(&client->dev, "Starting update\n");
138 
139 		status = adt7410_temp_ready(client); /* check for new value */
140 		if (unlikely(status)) {
141 			ret = ERR_PTR(status);
142 			goto abort;
143 		}
144 		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
145 			status = i2c_smbus_read_word_swapped(client,
146 							ADT7410_REG_TEMP[i]);
147 			if (unlikely(status < 0)) {
148 				dev_dbg(dev,
149 					"Failed to read value: reg %d, error %d\n",
150 					ADT7410_REG_TEMP[i], status);
151 				ret = ERR_PTR(status);
152 				goto abort;
153 			}
154 			data->temp[i] = status;
155 		}
156 		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
157 		if (unlikely(status < 0)) {
158 			dev_dbg(dev,
159 				"Failed to read value: reg %d, error %d\n",
160 				ADT7410_T_HYST, status);
161 			ret = ERR_PTR(status);
162 			goto abort;
163 		}
164 		data->hyst = status;
165 		data->last_updated = jiffies;
166 		data->valid = true;
167 	}
168 
169 abort:
170 	mutex_unlock(&data->update_lock);
171 	return ret;
172 }
173 
174 static s16 ADT7410_TEMP_TO_REG(long temp)
175 {
176 	return DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, ADT7410_TEMP_MIN,
177 					       ADT7410_TEMP_MAX) * 128, 1000);
178 }
179 
180 static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
181 {
182 	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
183 	if (!(data->config & ADT7410_RESOLUTION))
184 		reg &= ADT7410_T13_VALUE_MASK;
185 	/*
186 	 * temperature is stored in twos complement format, in steps of
187 	 * 1/128°C
188 	 */
189 	return DIV_ROUND_CLOSEST(reg * 1000, 128);
190 }
191 
192 /*-----------------------------------------------------------------------*/
193 
194 /* sysfs attributes for hwmon */
195 
196 static ssize_t adt7410_show_temp(struct device *dev,
197 				 struct device_attribute *da, char *buf)
198 {
199 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
200 	struct adt7410_data *data = adt7410_update_device(dev);
201 
202 	if (IS_ERR(data))
203 		return PTR_ERR(data);
204 
205 	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
206 		       data->temp[attr->index]));
207 }
208 
209 static ssize_t adt7410_set_temp(struct device *dev,
210 				struct device_attribute *da,
211 				const char *buf, size_t count)
212 {
213 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
214 	struct i2c_client *client = to_i2c_client(dev);
215 	struct adt7410_data *data = i2c_get_clientdata(client);
216 	int nr = attr->index;
217 	long temp;
218 	int ret;
219 
220 	ret = kstrtol(buf, 10, &temp);
221 	if (ret)
222 		return ret;
223 
224 	mutex_lock(&data->update_lock);
225 	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
226 	ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
227 					   data->temp[nr]);
228 	if (ret)
229 		count = ret;
230 	mutex_unlock(&data->update_lock);
231 	return count;
232 }
233 
234 static ssize_t adt7410_show_t_hyst(struct device *dev,
235 				   struct device_attribute *da,
236 				   char *buf)
237 {
238 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
239 	struct adt7410_data *data;
240 	int nr = attr->index;
241 	int hyst;
242 
243 	data = adt7410_update_device(dev);
244 	if (IS_ERR(data))
245 		return PTR_ERR(data);
246 	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
247 
248 	/*
249 	 * hysteresis is stored as a 4 bit offset in the device, convert it
250 	 * to an absolute value
251 	 */
252 	if (nr == 2)	/* min has positive offset, others have negative */
253 		hyst = -hyst;
254 	return sprintf(buf, "%d\n",
255 		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
256 }
257 
258 static ssize_t adt7410_set_t_hyst(struct device *dev,
259 				  struct device_attribute *da,
260 				  const char *buf, size_t count)
261 {
262 	struct i2c_client *client = to_i2c_client(dev);
263 	struct adt7410_data *data = i2c_get_clientdata(client);
264 	int limit, ret;
265 	long hyst;
266 
267 	ret = kstrtol(buf, 10, &hyst);
268 	if (ret)
269 		return ret;
270 	/* convert absolute hysteresis value to a 4 bit delta value */
271 	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
272 	hyst = SENSORS_LIMIT(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
273 	data->hyst = SENSORS_LIMIT(DIV_ROUND_CLOSEST(limit - hyst, 1000),
274 				   0, ADT7410_T_HYST_MASK);
275 	ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
276 	if (ret)
277 		return ret;
278 
279 	return count;
280 }
281 
282 static ssize_t adt7410_show_alarm(struct device *dev,
283 				  struct device_attribute *da,
284 				  char *buf)
285 {
286 	struct i2c_client *client = to_i2c_client(dev);
287 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
288 	int ret;
289 
290 	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
291 	if (ret < 0)
292 		return ret;
293 
294 	return sprintf(buf, "%d\n", !!(ret & attr->index));
295 }
296 
297 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
298 static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
299 			  adt7410_show_temp, adt7410_set_temp, 1);
300 static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
301 			  adt7410_show_temp, adt7410_set_temp, 2);
302 static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
303 			  adt7410_show_temp, adt7410_set_temp, 3);
304 static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
305 			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
306 static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
307 			  adt7410_show_t_hyst, NULL, 2);
308 static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
309 			  adt7410_show_t_hyst, NULL, 3);
310 static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
311 			  NULL, ADT7410_STAT_T_LOW);
312 static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
313 			  NULL, ADT7410_STAT_T_HIGH);
314 static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
315 			  NULL, ADT7410_STAT_T_CRIT);
316 
317 static struct attribute *adt7410_attributes[] = {
318 	&sensor_dev_attr_temp1_input.dev_attr.attr,
319 	&sensor_dev_attr_temp1_max.dev_attr.attr,
320 	&sensor_dev_attr_temp1_min.dev_attr.attr,
321 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
322 	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
323 	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
324 	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
325 	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
326 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
327 	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
328 	NULL
329 };
330 
331 static const struct attribute_group adt7410_group = {
332 	.attrs = adt7410_attributes,
333 };
334 
335 /*-----------------------------------------------------------------------*/
336 
337 /* device probe and removal */
338 
339 static int adt7410_probe(struct i2c_client *client,
340 			 const struct i2c_device_id *id)
341 {
342 	struct adt7410_data *data;
343 	int ret;
344 
345 	if (!i2c_check_functionality(client->adapter,
346 			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
347 		return -ENODEV;
348 
349 	data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
350 			    GFP_KERNEL);
351 	if (!data)
352 		return -ENOMEM;
353 
354 	i2c_set_clientdata(client, data);
355 	mutex_init(&data->update_lock);
356 
357 	/* configure as specified */
358 	ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
359 	if (ret < 0) {
360 		dev_dbg(&client->dev, "Can't read config? %d\n", ret);
361 		return ret;
362 	}
363 	data->oldconfig = ret;
364 	/*
365 	 * Set to 16 bit resolution, continous conversion and comparator mode.
366 	 */
367 	data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
368 			ADT7410_EVENT_MODE;
369 	if (data->config != data->oldconfig) {
370 		ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
371 						data->config);
372 		if (ret)
373 			return ret;
374 	}
375 	dev_dbg(&client->dev, "Config %02x\n", data->config);
376 
377 	/* Register sysfs hooks */
378 	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
379 	if (ret)
380 		goto exit_restore;
381 
382 	data->hwmon_dev = hwmon_device_register(&client->dev);
383 	if (IS_ERR(data->hwmon_dev)) {
384 		ret = PTR_ERR(data->hwmon_dev);
385 		goto exit_remove;
386 	}
387 
388 	dev_info(&client->dev, "sensor '%s'\n", client->name);
389 
390 	return 0;
391 
392 exit_remove:
393 	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
394 exit_restore:
395 	i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
396 	return ret;
397 }
398 
399 static int adt7410_remove(struct i2c_client *client)
400 {
401 	struct adt7410_data *data = i2c_get_clientdata(client);
402 
403 	hwmon_device_unregister(data->hwmon_dev);
404 	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
405 	if (data->oldconfig != data->config)
406 		i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
407 					  data->oldconfig);
408 	return 0;
409 }
410 
411 static const struct i2c_device_id adt7410_ids[] = {
412 	{ "adt7410", adt7410, },
413 	{ /* LIST END */ }
414 };
415 MODULE_DEVICE_TABLE(i2c, adt7410_ids);
416 
417 #ifdef CONFIG_PM
418 static int adt7410_suspend(struct device *dev)
419 {
420 	int ret;
421 	struct i2c_client *client = to_i2c_client(dev);
422 	struct adt7410_data *data = i2c_get_clientdata(client);
423 
424 	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
425 					data->config | ADT7410_PD);
426 	return ret;
427 }
428 
429 static int adt7410_resume(struct device *dev)
430 {
431 	int ret;
432 	struct i2c_client *client = to_i2c_client(dev);
433 	struct adt7410_data *data = i2c_get_clientdata(client);
434 
435 	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
436 	return ret;
437 }
438 
439 static const struct dev_pm_ops adt7410_dev_pm_ops = {
440 	.suspend	= adt7410_suspend,
441 	.resume		= adt7410_resume,
442 };
443 #define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
444 #else
445 #define ADT7410_DEV_PM_OPS NULL
446 #endif /* CONFIG_PM */
447 
448 static struct i2c_driver adt7410_driver = {
449 	.class		= I2C_CLASS_HWMON,
450 	.driver = {
451 		.name	= "adt7410",
452 		.pm	= ADT7410_DEV_PM_OPS,
453 	},
454 	.probe		= adt7410_probe,
455 	.remove		= adt7410_remove,
456 	.id_table	= adt7410_ids,
457 	.address_list	= normal_i2c,
458 };
459 
460 module_i2c_driver(adt7410_driver);
461 
462 MODULE_AUTHOR("Hartmut Knaack");
463 MODULE_DESCRIPTION("ADT7410 driver");
464 MODULE_LICENSE("GPL");
465