Blame view

drivers/hwmon/tmp102.c 8.24 KB
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
1
  /* Texas Instruments TMP102 SMBus temperature sensor driver
beb1b6bbf   Steven King   hwmon: Driver for...
2
   *
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
3
   * Copyright (C) 2010 Steven King <sfking@fdwdc.com>
beb1b6bbf   Steven King   hwmon: Driver for...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   *
   * 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
   */
beb1b6bbf   Steven King   hwmon: Driver for...
19
20
21
22
23
24
25
26
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/i2c.h>
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/err.h>
  #include <linux/mutex.h>
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
27
  #include <linux/device.h>
beb1b6bbf   Steven King   hwmon: Driver for...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  
  #define	DRIVER_NAME "tmp102"
  
  #define	TMP102_TEMP_REG			0x00
  #define	TMP102_CONF_REG			0x01
  /* note: these bit definitions are byte swapped */
  #define		TMP102_CONF_SD		0x0100
  #define		TMP102_CONF_TM		0x0200
  #define		TMP102_CONF_POL		0x0400
  #define		TMP102_CONF_F0		0x0800
  #define		TMP102_CONF_F1		0x1000
  #define		TMP102_CONF_R0		0x2000
  #define		TMP102_CONF_R1		0x4000
  #define		TMP102_CONF_OS		0x8000
  #define		TMP102_CONF_EM		0x0010
  #define		TMP102_CONF_AL		0x0020
  #define		TMP102_CONF_CR0		0x0040
  #define		TMP102_CONF_CR1		0x0080
  #define	TMP102_TLOW_REG			0x02
  #define	TMP102_THIGH_REG		0x03
  
  struct tmp102 {
  	struct device *hwmon_dev;
  	struct mutex lock;
38806bda6   Jean Delvare   hwmon: (tmp102) D...
52
  	u16 config_orig;
beb1b6bbf   Steven King   hwmon: Driver for...
53
54
55
  	unsigned long last_update;
  	int temp[3];
  };
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
56
57
  /* convert left adjusted 13-bit TMP102 register value to milliCelsius */
  static inline int tmp102_reg_to_mC(s16 val)
beb1b6bbf   Steven King   hwmon: Driver for...
58
  {
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
59
  	return ((val & ~0x01) * 1000) / 128;
beb1b6bbf   Steven King   hwmon: Driver for...
60
  }
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
61
62
  /* convert milliCelsius to left adjusted 13-bit TMP102 register value */
  static inline u16 tmp102_mC_to_reg(int val)
beb1b6bbf   Steven King   hwmon: Driver for...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  {
  	return (val * 128) / 1000;
  }
  
  static const u8 tmp102_reg[] = {
  	TMP102_TEMP_REG,
  	TMP102_TLOW_REG,
  	TMP102_THIGH_REG,
  };
  
  static struct tmp102 *tmp102_update_device(struct i2c_client *client)
  {
  	struct tmp102 *tmp102 = i2c_get_clientdata(client);
  
  	mutex_lock(&tmp102->lock);
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
78
  	if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
beb1b6bbf   Steven King   hwmon: Driver for...
79
80
  		int i;
  		for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
81
82
  			int status = i2c_smbus_read_word_swapped(client,
  								 tmp102_reg[i]);
beb1b6bbf   Steven King   hwmon: Driver for...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  			if (status > -1)
  				tmp102->temp[i] = tmp102_reg_to_mC(status);
  		}
  		tmp102->last_update = jiffies;
  	}
  	mutex_unlock(&tmp102->lock);
  	return tmp102;
  }
  
  static ssize_t tmp102_show_temp(struct device *dev,
  				struct device_attribute *attr,
  				char *buf)
  {
  	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
  	struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
  
  	return sprintf(buf, "%d
  ", tmp102->temp[sda->index]);
  }
  
  static ssize_t tmp102_set_temp(struct device *dev,
  			       struct device_attribute *attr,
  			       const char *buf, size_t count)
  {
  	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
  	struct i2c_client *client = to_i2c_client(dev);
  	struct tmp102 *tmp102 = i2c_get_clientdata(client);
  	long val;
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
111
  	int status;
beb1b6bbf   Steven King   hwmon: Driver for...
112

179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
113
  	if (kstrtol(buf, 10, &val) < 0)
beb1b6bbf   Steven King   hwmon: Driver for...
114
  		return -EINVAL;
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
115
  	val = SENSORS_LIMIT(val, -256000, 255000);
beb1b6bbf   Steven King   hwmon: Driver for...
116
  	mutex_lock(&tmp102->lock);
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
117
  	tmp102->temp[sda->index] = val;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
118
119
  	status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index],
  					      tmp102_mC_to_reg(val));
beb1b6bbf   Steven King   hwmon: Driver for...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	mutex_unlock(&tmp102->lock);
  	return status ? : count;
  }
  
  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0);
  
  static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
  			  tmp102_set_temp, 1);
  
  static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
  			  tmp102_set_temp, 2);
  
  static struct attribute *tmp102_attributes[] = {
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
  	NULL
  };
  
  static const struct attribute_group tmp102_attr_group = {
  	.attrs = tmp102_attributes,
  };
  
  #define TMP102_CONFIG  (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
  #define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
  
  static int __devinit tmp102_probe(struct i2c_client *client,
  				  const struct i2c_device_id *id)
  {
  	struct tmp102 *tmp102;
  	int status;
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
151
  	if (!i2c_check_functionality(client->adapter,
beb1b6bbf   Steven King   hwmon: Driver for...
152
  				     I2C_FUNC_SMBUS_WORD_DATA)) {
25985edce   Lucas De Marchi   Fix common misspe...
153
  		dev_err(&client->dev, "adapter doesn't support SMBus word "
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
154
155
  			"transactions
  ");
beb1b6bbf   Steven King   hwmon: Driver for...
156
157
158
159
160
161
162
163
164
165
  		return -ENODEV;
  	}
  
  	tmp102 = kzalloc(sizeof(*tmp102), GFP_KERNEL);
  	if (!tmp102) {
  		dev_dbg(&client->dev, "kzalloc failed
  ");
  		return -ENOMEM;
  	}
  	i2c_set_clientdata(client, tmp102);
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
166
  	status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
167
168
169
170
171
172
  	if (status < 0) {
  		dev_err(&client->dev, "error reading config register
  ");
  		goto fail_free;
  	}
  	tmp102->config_orig = status;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
173
174
  	status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
  					      TMP102_CONFIG);
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
175
176
177
  	if (status < 0) {
  		dev_err(&client->dev, "error writing config register
  ");
38806bda6   Jean Delvare   hwmon: (tmp102) D...
178
  		goto fail_restore_config;
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
179
  	}
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
180
  	status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
beb1b6bbf   Steven King   hwmon: Driver for...
181
  	if (status < 0) {
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
182
183
  		dev_err(&client->dev, "error reading config register
  ");
38806bda6   Jean Delvare   hwmon: (tmp102) D...
184
  		goto fail_restore_config;
beb1b6bbf   Steven King   hwmon: Driver for...
185
186
187
  	}
  	status &= ~TMP102_CONFIG_RD_ONLY;
  	if (status != TMP102_CONFIG) {
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
188
189
190
  		dev_err(&client->dev, "config settings did not stick
  ");
  		status = -ENODEV;
38806bda6   Jean Delvare   hwmon: (tmp102) D...
191
  		goto fail_restore_config;
beb1b6bbf   Steven King   hwmon: Driver for...
192
193
194
195
196
197
198
199
  	}
  	tmp102->last_update = jiffies - HZ;
  	mutex_init(&tmp102->lock);
  
  	status = sysfs_create_group(&client->dev.kobj, &tmp102_attr_group);
  	if (status) {
  		dev_dbg(&client->dev, "could not create sysfs files
  ");
38806bda6   Jean Delvare   hwmon: (tmp102) D...
200
  		goto fail_restore_config;
beb1b6bbf   Steven King   hwmon: Driver for...
201
202
203
204
205
206
  	}
  	tmp102->hwmon_dev = hwmon_device_register(&client->dev);
  	if (IS_ERR(tmp102->hwmon_dev)) {
  		dev_dbg(&client->dev, "unable to register hwmon device
  ");
  		status = PTR_ERR(tmp102->hwmon_dev);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
207
  		goto fail_remove_sysfs;
beb1b6bbf   Steven King   hwmon: Driver for...
208
209
210
211
212
213
  	}
  
  	dev_info(&client->dev, "initialized
  ");
  
  	return 0;
38806bda6   Jean Delvare   hwmon: (tmp102) D...
214
215
  
  fail_remove_sysfs:
beb1b6bbf   Steven King   hwmon: Driver for...
216
  	sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
217
  fail_restore_config:
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
218
219
  	i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
  				     tmp102->config_orig);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
220
  fail_free:
beb1b6bbf   Steven King   hwmon: Driver for...
221
  	kfree(tmp102);
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
222
  	return status;
beb1b6bbf   Steven King   hwmon: Driver for...
223
224
225
226
227
  }
  
  static int __devexit tmp102_remove(struct i2c_client *client)
  {
  	struct tmp102 *tmp102 = i2c_get_clientdata(client);
beb1b6bbf   Steven King   hwmon: Driver for...
228
229
  	hwmon_device_unregister(tmp102->hwmon_dev);
  	sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
230
231
232
233
  
  	/* Stop monitoring if device was stopped originally */
  	if (tmp102->config_orig & TMP102_CONF_SD) {
  		int config;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
234
  		config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
235
  		if (config >= 0)
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
236
237
  			i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
  						     config | TMP102_CONF_SD);
38806bda6   Jean Delvare   hwmon: (tmp102) D...
238
  	}
beb1b6bbf   Steven King   hwmon: Driver for...
239
240
241
242
243
244
245
246
247
  	kfree(tmp102);
  
  	return 0;
  }
  
  #ifdef CONFIG_PM
  static int tmp102_suspend(struct device *dev)
  {
  	struct i2c_client *client = to_i2c_client(dev);
8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
248
  	int config;
beb1b6bbf   Steven King   hwmon: Driver for...
249

90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
250
  	config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
251
252
  	if (config < 0)
  		return config;
beb1b6bbf   Steven King   hwmon: Driver for...
253

8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
254
  	config |= TMP102_CONF_SD;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
255
  	return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
beb1b6bbf   Steven King   hwmon: Driver for...
256
257
258
259
260
  }
  
  static int tmp102_resume(struct device *dev)
  {
  	struct i2c_client *client = to_i2c_client(dev);
8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
261
  	int config;
beb1b6bbf   Steven King   hwmon: Driver for...
262

90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
263
  	config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
264
265
  	if (config < 0)
  		return config;
beb1b6bbf   Steven King   hwmon: Driver for...
266

8d4dee98b   Jean Delvare   hwmon: (tmp102) F...
267
  	config &= ~TMP102_CONF_SD;
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
268
  	return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
beb1b6bbf   Steven King   hwmon: Driver for...
269
270
271
272
273
274
275
276
277
278
279
  }
  
  static const struct dev_pm_ops tmp102_dev_pm_ops = {
  	.suspend	= tmp102_suspend,
  	.resume		= tmp102_resume,
  };
  
  #define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops)
  #else
  #define	TMP102_DEV_PM_OPS NULL
  #endif /* CONFIG_PM */
beb1b6bbf   Steven King   hwmon: Driver for...
280
  static const struct i2c_device_id tmp102_id[] = {
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
281
  	{ "tmp102", 0 },
beb1b6bbf   Steven King   hwmon: Driver for...
282
283
  	{ }
  };
cff37c9e8   Jean Delvare   hwmon: (tmp102) V...
284
  MODULE_DEVICE_TABLE(i2c, tmp102_id);
beb1b6bbf   Steven King   hwmon: Driver for...
285
286
287
288
  
  static struct i2c_driver tmp102_driver = {
  	.driver.name	= DRIVER_NAME,
  	.driver.pm	= TMP102_DEV_PM_OPS,
beb1b6bbf   Steven King   hwmon: Driver for...
289
290
291
  	.probe		= tmp102_probe,
  	.remove		= __devexit_p(tmp102_remove),
  	.id_table	= tmp102_id,
beb1b6bbf   Steven King   hwmon: Driver for...
292
293
294
295
296
297
298
299
300
301
302
303
304
  };
  
  static int __init tmp102_init(void)
  {
  	return i2c_add_driver(&tmp102_driver);
  }
  module_init(tmp102_init);
  
  static void __exit tmp102_exit(void)
  {
  	i2c_del_driver(&tmp102_driver);
  }
  module_exit(tmp102_exit);
beb1b6bbf   Steven King   hwmon: Driver for...
305
306
307
  MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
  MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver");
  MODULE_LICENSE("GPL");