Blame view
drivers/hwmon/tmp102.c
8.54 KB
cff37c9e8 hwmon: (tmp102) V... |
1 |
/* Texas Instruments TMP102 SMBus temperature sensor driver |
beb1b6bbf hwmon: Driver for... |
2 |
* |
cff37c9e8 hwmon: (tmp102) V... |
3 |
* Copyright (C) 2010 Steven King <sfking@fdwdc.com> |
beb1b6bbf 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 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 hwmon: (tmp102) V... |
27 |
#include <linux/device.h> |
dcd8f3923 hwmon: Add missin... |
28 |
#include <linux/jiffies.h> |
6a027523f hwmon: tmp102: ex... |
29 30 |
#include <linux/thermal.h> #include <linux/of.h> |
beb1b6bbf hwmon: Driver for... |
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#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 { |
ad9beea43 hwmon: (tmp102) C... |
53 |
struct i2c_client *client; |
beb1b6bbf hwmon: Driver for... |
54 |
struct device *hwmon_dev; |
6a027523f hwmon: tmp102: ex... |
55 |
struct thermal_zone_device *tz; |
beb1b6bbf hwmon: Driver for... |
56 |
struct mutex lock; |
38806bda6 hwmon: (tmp102) D... |
57 |
u16 config_orig; |
beb1b6bbf hwmon: Driver for... |
58 59 |
unsigned long last_update; int temp[3]; |
00917b5c5 hwmon: (tmp102) F... |
60 |
bool first_time; |
beb1b6bbf hwmon: Driver for... |
61 |
}; |
cff37c9e8 hwmon: (tmp102) V... |
62 63 |
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */ static inline int tmp102_reg_to_mC(s16 val) |
beb1b6bbf hwmon: Driver for... |
64 |
{ |
cff37c9e8 hwmon: (tmp102) V... |
65 |
return ((val & ~0x01) * 1000) / 128; |
beb1b6bbf hwmon: Driver for... |
66 |
} |
cff37c9e8 hwmon: (tmp102) V... |
67 68 |
/* convert milliCelsius to left adjusted 13-bit TMP102 register value */ static inline u16 tmp102_mC_to_reg(int val) |
beb1b6bbf hwmon: Driver for... |
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, }; |
ad9beea43 hwmon: (tmp102) C... |
78 |
static struct tmp102 *tmp102_update_device(struct device *dev) |
beb1b6bbf hwmon: Driver for... |
79 |
{ |
ad9beea43 hwmon: (tmp102) C... |
80 81 |
struct tmp102 *tmp102 = dev_get_drvdata(dev); struct i2c_client *client = tmp102->client; |
beb1b6bbf hwmon: Driver for... |
82 83 |
mutex_lock(&tmp102->lock); |
cff37c9e8 hwmon: (tmp102) V... |
84 |
if (time_after(jiffies, tmp102->last_update + HZ / 3)) { |
beb1b6bbf hwmon: Driver for... |
85 86 |
int i; for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) { |
90f4102ce hwmon: Use i2c_sm... |
87 88 |
int status = i2c_smbus_read_word_swapped(client, tmp102_reg[i]); |
beb1b6bbf hwmon: Driver for... |
89 90 91 92 |
if (status > -1) tmp102->temp[i] = tmp102_reg_to_mC(status); } tmp102->last_update = jiffies; |
00917b5c5 hwmon: (tmp102) F... |
93 |
tmp102->first_time = false; |
beb1b6bbf hwmon: Driver for... |
94 95 96 97 |
} mutex_unlock(&tmp102->lock); return tmp102; } |
17e8351a7 thermal: consiste... |
98 |
static int tmp102_read_temp(void *dev, int *temp) |
6a027523f hwmon: tmp102: ex... |
99 |
{ |
ad9beea43 hwmon: (tmp102) C... |
100 |
struct tmp102 *tmp102 = tmp102_update_device(dev); |
6a027523f hwmon: tmp102: ex... |
101 |
|
00917b5c5 hwmon: (tmp102) F... |
102 103 104 105 106 107 |
/* Is it too early even to return a conversion? */ if (tmp102->first_time) { dev_dbg(dev, "%s: Conversion not ready yet.. ", __func__); return -EAGAIN; } |
6a027523f hwmon: tmp102: ex... |
108 109 110 111 |
*temp = tmp102->temp[0]; return 0; } |
beb1b6bbf hwmon: Driver for... |
112 113 114 115 116 |
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); |
ad9beea43 hwmon: (tmp102) C... |
117 |
struct tmp102 *tmp102 = tmp102_update_device(dev); |
beb1b6bbf hwmon: Driver for... |
118 |
|
00917b5c5 hwmon: (tmp102) F... |
119 120 121 |
/* Is it too early even to return a read? */ if (tmp102->first_time) return -EAGAIN; |
beb1b6bbf hwmon: Driver for... |
122 123 124 125 126 127 128 129 130 |
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); |
ad9beea43 hwmon: (tmp102) C... |
131 132 |
struct tmp102 *tmp102 = dev_get_drvdata(dev); struct i2c_client *client = tmp102->client; |
beb1b6bbf hwmon: Driver for... |
133 |
long val; |
cff37c9e8 hwmon: (tmp102) V... |
134 |
int status; |
beb1b6bbf hwmon: Driver for... |
135 |
|
179c4fdb5 hwmon: replaced s... |
136 |
if (kstrtol(buf, 10, &val) < 0) |
beb1b6bbf hwmon: Driver for... |
137 |
return -EINVAL; |
2a844c148 hwmon: Replace SE... |
138 |
val = clamp_val(val, -256000, 255000); |
cff37c9e8 hwmon: (tmp102) V... |
139 |
|
beb1b6bbf hwmon: Driver for... |
140 |
mutex_lock(&tmp102->lock); |
cff37c9e8 hwmon: (tmp102) V... |
141 |
tmp102->temp[sda->index] = val; |
90f4102ce hwmon: Use i2c_sm... |
142 143 |
status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index], tmp102_mC_to_reg(val)); |
beb1b6bbf hwmon: Driver for... |
144 145 146 147 148 149 150 151 152 153 154 |
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); |
ad9beea43 hwmon: (tmp102) C... |
155 |
static struct attribute *tmp102_attrs[] = { |
beb1b6bbf hwmon: Driver for... |
156 157 158 159 160 |
&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 }; |
ad9beea43 hwmon: (tmp102) C... |
161 |
ATTRIBUTE_GROUPS(tmp102); |
beb1b6bbf hwmon: Driver for... |
162 163 164 |
#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) |
2251aef64 thermal: of: impr... |
165 166 167 |
static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { .get_temp = tmp102_read_temp, }; |
6c931ae1c hwmon: remove use... |
168 |
static int tmp102_probe(struct i2c_client *client, |
beb1b6bbf hwmon: Driver for... |
169 170 |
const struct i2c_device_id *id) { |
fbd9af164 hwmon: (tmp102) I... |
171 |
struct device *dev = &client->dev; |
ad9beea43 hwmon: (tmp102) C... |
172 |
struct device *hwmon_dev; |
beb1b6bbf hwmon: Driver for... |
173 174 |
struct tmp102 *tmp102; int status; |
cff37c9e8 hwmon: (tmp102) V... |
175 |
if (!i2c_check_functionality(client->adapter, |
beb1b6bbf hwmon: Driver for... |
176 |
I2C_FUNC_SMBUS_WORD_DATA)) { |
fbd9af164 hwmon: (tmp102) I... |
177 |
dev_err(dev, |
b55f37572 hwmon: Fix checkp... |
178 179 |
"adapter doesn't support SMBus word transactions "); |
beb1b6bbf hwmon: Driver for... |
180 181 |
return -ENODEV; } |
fbd9af164 hwmon: (tmp102) I... |
182 |
tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL); |
f511a21f4 hwmon: (tmp102) C... |
183 |
if (!tmp102) |
beb1b6bbf hwmon: Driver for... |
184 |
return -ENOMEM; |
f511a21f4 hwmon: (tmp102) C... |
185 |
|
beb1b6bbf hwmon: Driver for... |
186 |
i2c_set_clientdata(client, tmp102); |
ad9beea43 hwmon: (tmp102) C... |
187 |
tmp102->client = client; |
beb1b6bbf hwmon: Driver for... |
188 |
|
90f4102ce hwmon: Use i2c_sm... |
189 |
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); |
38806bda6 hwmon: (tmp102) D... |
190 |
if (status < 0) { |
fbd9af164 hwmon: (tmp102) I... |
191 192 |
dev_err(dev, "error reading config register "); |
f511a21f4 hwmon: (tmp102) C... |
193 |
return status; |
38806bda6 hwmon: (tmp102) D... |
194 195 |
} tmp102->config_orig = status; |
90f4102ce hwmon: Use i2c_sm... |
196 197 |
status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, TMP102_CONFIG); |
cff37c9e8 hwmon: (tmp102) V... |
198 |
if (status < 0) { |
fbd9af164 hwmon: (tmp102) I... |
199 200 |
dev_err(dev, "error writing config register "); |
38806bda6 hwmon: (tmp102) D... |
201 |
goto fail_restore_config; |
cff37c9e8 hwmon: (tmp102) V... |
202 |
} |
90f4102ce hwmon: Use i2c_sm... |
203 |
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); |
beb1b6bbf hwmon: Driver for... |
204 |
if (status < 0) { |
fbd9af164 hwmon: (tmp102) I... |
205 206 |
dev_err(dev, "error reading config register "); |
38806bda6 hwmon: (tmp102) D... |
207 |
goto fail_restore_config; |
beb1b6bbf hwmon: Driver for... |
208 209 210 |
} status &= ~TMP102_CONFIG_RD_ONLY; if (status != TMP102_CONFIG) { |
fbd9af164 hwmon: (tmp102) I... |
211 212 |
dev_err(dev, "config settings did not stick "); |
cff37c9e8 hwmon: (tmp102) V... |
213 |
status = -ENODEV; |
38806bda6 hwmon: (tmp102) D... |
214 |
goto fail_restore_config; |
beb1b6bbf hwmon: Driver for... |
215 |
} |
00917b5c5 hwmon: (tmp102) F... |
216 217 218 |
tmp102->last_update = jiffies; /* Mark that we are not ready with data until conversion is complete */ tmp102->first_time = true; |
beb1b6bbf hwmon: Driver for... |
219 |
mutex_init(&tmp102->lock); |
ad9beea43 hwmon: (tmp102) C... |
220 221 222 |
hwmon_dev = hwmon_device_register_with_groups(dev, client->name, tmp102, tmp102_groups); if (IS_ERR(hwmon_dev)) { |
fbd9af164 hwmon: (tmp102) I... |
223 224 |
dev_dbg(dev, "unable to register hwmon device "); |
ad9beea43 hwmon: (tmp102) C... |
225 226 |
status = PTR_ERR(hwmon_dev); goto fail_restore_config; |
beb1b6bbf hwmon: Driver for... |
227 |
} |
ad9beea43 hwmon: (tmp102) C... |
228 229 |
tmp102->hwmon_dev = hwmon_dev; tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, |
2251aef64 thermal: of: impr... |
230 |
&tmp102_of_thermal_ops); |
6a027523f hwmon: tmp102: ex... |
231 232 |
if (IS_ERR(tmp102->tz)) tmp102->tz = NULL; |
fbd9af164 hwmon: (tmp102) I... |
233 234 |
dev_info(dev, "initialized "); |
beb1b6bbf hwmon: Driver for... |
235 236 |
return 0; |
38806bda6 hwmon: (tmp102) D... |
237 |
|
38806bda6 hwmon: (tmp102) D... |
238 |
fail_restore_config: |
90f4102ce hwmon: Use i2c_sm... |
239 240 |
i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, tmp102->config_orig); |
cff37c9e8 hwmon: (tmp102) V... |
241 |
return status; |
beb1b6bbf hwmon: Driver for... |
242 |
} |
281dfd0b6 hwmon: remove use... |
243 |
static int tmp102_remove(struct i2c_client *client) |
beb1b6bbf hwmon: Driver for... |
244 245 |
{ struct tmp102 *tmp102 = i2c_get_clientdata(client); |
ad9beea43 hwmon: (tmp102) C... |
246 |
thermal_zone_of_sensor_unregister(tmp102->hwmon_dev, tmp102->tz); |
beb1b6bbf hwmon: Driver for... |
247 |
hwmon_device_unregister(tmp102->hwmon_dev); |
38806bda6 hwmon: (tmp102) D... |
248 249 250 251 |
/* Stop monitoring if device was stopped originally */ if (tmp102->config_orig & TMP102_CONF_SD) { int config; |
90f4102ce hwmon: Use i2c_sm... |
252 |
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); |
38806bda6 hwmon: (tmp102) D... |
253 |
if (config >= 0) |
90f4102ce hwmon: Use i2c_sm... |
254 255 |
i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config | TMP102_CONF_SD); |
38806bda6 hwmon: (tmp102) D... |
256 |
} |
beb1b6bbf hwmon: Driver for... |
257 258 |
return 0; } |
dd378b1bc hwmon: (tmp102) a... |
259 |
#ifdef CONFIG_PM_SLEEP |
beb1b6bbf hwmon: Driver for... |
260 261 262 |
static int tmp102_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); |
8d4dee98b hwmon: (tmp102) F... |
263 |
int config; |
beb1b6bbf hwmon: Driver for... |
264 |
|
90f4102ce hwmon: Use i2c_sm... |
265 |
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); |
8d4dee98b hwmon: (tmp102) F... |
266 267 |
if (config < 0) return config; |
beb1b6bbf hwmon: Driver for... |
268 |
|
8d4dee98b hwmon: (tmp102) F... |
269 |
config |= TMP102_CONF_SD; |
90f4102ce hwmon: Use i2c_sm... |
270 |
return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); |
beb1b6bbf hwmon: Driver for... |
271 272 273 274 275 |
} static int tmp102_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); |
8d4dee98b hwmon: (tmp102) F... |
276 |
int config; |
beb1b6bbf hwmon: Driver for... |
277 |
|
90f4102ce hwmon: Use i2c_sm... |
278 |
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); |
8d4dee98b hwmon: (tmp102) F... |
279 280 |
if (config < 0) return config; |
beb1b6bbf hwmon: Driver for... |
281 |
|
8d4dee98b hwmon: (tmp102) F... |
282 |
config &= ~TMP102_CONF_SD; |
90f4102ce hwmon: Use i2c_sm... |
283 |
return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); |
beb1b6bbf hwmon: Driver for... |
284 |
} |
beb1b6bbf hwmon: Driver for... |
285 |
#endif /* CONFIG_PM */ |
dd378b1bc hwmon: (tmp102) a... |
286 |
static SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); |
beb1b6bbf hwmon: Driver for... |
287 |
static const struct i2c_device_id tmp102_id[] = { |
cff37c9e8 hwmon: (tmp102) V... |
288 |
{ "tmp102", 0 }, |
beb1b6bbf hwmon: Driver for... |
289 290 |
{ } }; |
cff37c9e8 hwmon: (tmp102) V... |
291 |
MODULE_DEVICE_TABLE(i2c, tmp102_id); |
beb1b6bbf hwmon: Driver for... |
292 293 294 |
static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, |
dd378b1bc hwmon: (tmp102) a... |
295 |
.driver.pm = &tmp102_dev_pm_ops, |
beb1b6bbf hwmon: Driver for... |
296 |
.probe = tmp102_probe, |
9e5e9b7a9 hwmon: remove use... |
297 |
.remove = tmp102_remove, |
beb1b6bbf hwmon: Driver for... |
298 |
.id_table = tmp102_id, |
beb1b6bbf hwmon: Driver for... |
299 |
}; |
f0967eea8 hwmon: convert dr... |
300 |
module_i2c_driver(tmp102_driver); |
beb1b6bbf hwmon: Driver for... |
301 |
|
beb1b6bbf hwmon: Driver for... |
302 303 304 |
MODULE_AUTHOR("Steven King <sfking@fdwdc.com>"); MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver"); MODULE_LICENSE("GPL"); |