Root/
1 | /* |
2 | * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware |
3 | * monitoring |
4 | * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> |
5 | * |
6 | * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made |
7 | * by Winbond. It reports a single external temperature with a 1 deg |
8 | * resolution and a 3 deg accuracy. Datasheet can be obtained from |
9 | * Winbond's website at: |
10 | * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf |
11 | * |
12 | * Ported to Linux 2.6 by Wolfgang Ziegler <nuppla@gmx.at> and Jean Delvare |
13 | * <khali@linux-fr.org>. |
14 | * |
15 | * Thanks to James Bolt <james@evilpenguin.com> for benchmarking the read |
16 | * error handling mechanism. |
17 | * |
18 | * This program is free software; you can redistribute it and/or modify |
19 | * it under the terms of the GNU General Public License as published by |
20 | * the Free Software Foundation; either version 2 of the License, or |
21 | * (at your option) any later version. |
22 | * |
23 | * This program is distributed in the hope that it will be useful, |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
26 | * GNU General Public License for more details. |
27 | * |
28 | * You should have received a copy of the GNU General Public License |
29 | * along with this program; if not, write to the Free Software |
30 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
31 | */ |
32 | |
33 | #include <linux/module.h> |
34 | #include <linux/delay.h> |
35 | #include <linux/init.h> |
36 | #include <linux/slab.h> |
37 | #include <linux/jiffies.h> |
38 | #include <linux/i2c.h> |
39 | #include <linux/hwmon.h> |
40 | #include <linux/hwmon-sysfs.h> |
41 | #include <linux/err.h> |
42 | #include <linux/mutex.h> |
43 | |
44 | /* How many retries on register read error */ |
45 | #define MAX_RETRIES 5 |
46 | |
47 | /* |
48 | * Address to scan |
49 | * Address is fully defined internally and cannot be changed. |
50 | */ |
51 | |
52 | static const unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; |
53 | |
54 | /* |
55 | * The W83L785TS-S registers |
56 | * Manufacturer ID is 0x5CA3 for Winbond. |
57 | */ |
58 | |
59 | #define W83L785TS_REG_MAN_ID1 0x4D |
60 | #define W83L785TS_REG_MAN_ID2 0x4C |
61 | #define W83L785TS_REG_CHIP_ID 0x4E |
62 | #define W83L785TS_REG_CONFIG 0x40 |
63 | #define W83L785TS_REG_TYPE 0x52 |
64 | #define W83L785TS_REG_TEMP 0x27 |
65 | #define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */ |
66 | |
67 | /* |
68 | * Conversions |
69 | * The W83L785TS-S uses signed 8-bit values. |
70 | */ |
71 | |
72 | #define TEMP_FROM_REG(val) ((val) * 1000) |
73 | |
74 | /* |
75 | * Functions declaration |
76 | */ |
77 | |
78 | static int w83l785ts_probe(struct i2c_client *client, |
79 | const struct i2c_device_id *id); |
80 | static int w83l785ts_detect(struct i2c_client *client, |
81 | struct i2c_board_info *info); |
82 | static int w83l785ts_remove(struct i2c_client *client); |
83 | static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); |
84 | static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); |
85 | |
86 | /* |
87 | * Driver data (common to all clients) |
88 | */ |
89 | |
90 | static const struct i2c_device_id w83l785ts_id[] = { |
91 | { "w83l785ts", 0 }, |
92 | { } |
93 | }; |
94 | MODULE_DEVICE_TABLE(i2c, w83l785ts_id); |
95 | |
96 | static struct i2c_driver w83l785ts_driver = { |
97 | .class = I2C_CLASS_HWMON, |
98 | .driver = { |
99 | .name = "w83l785ts", |
100 | }, |
101 | .probe = w83l785ts_probe, |
102 | .remove = w83l785ts_remove, |
103 | .id_table = w83l785ts_id, |
104 | .detect = w83l785ts_detect, |
105 | .address_list = normal_i2c, |
106 | }; |
107 | |
108 | /* |
109 | * Client data (each client gets its own) |
110 | */ |
111 | |
112 | struct w83l785ts_data { |
113 | struct device *hwmon_dev; |
114 | struct mutex update_lock; |
115 | char valid; /* zero until following fields are valid */ |
116 | unsigned long last_updated; /* in jiffies */ |
117 | |
118 | /* registers values */ |
119 | s8 temp[2]; /* 0: input |
120 | 1: critical limit */ |
121 | }; |
122 | |
123 | /* |
124 | * Sysfs stuff |
125 | */ |
126 | |
127 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, |
128 | char *buf) |
129 | { |
130 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
131 | struct w83l785ts_data *data = w83l785ts_update_device(dev); |
132 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); |
133 | } |
134 | |
135 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); |
136 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1); |
137 | |
138 | /* |
139 | * Real code |
140 | */ |
141 | |
142 | /* Return 0 if detection is successful, -ENODEV otherwise */ |
143 | static int w83l785ts_detect(struct i2c_client *client, |
144 | struct i2c_board_info *info) |
145 | { |
146 | struct i2c_adapter *adapter = client->adapter; |
147 | u16 man_id; |
148 | u8 chip_id; |
149 | |
150 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
151 | return -ENODEV; |
152 | |
153 | /* detection */ |
154 | if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80) |
155 | || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) { |
156 | dev_dbg(&adapter->dev, |
157 | "W83L785TS-S detection failed at 0x%02x\n", |
158 | client->addr); |
159 | return -ENODEV; |
160 | } |
161 | |
162 | /* Identification */ |
163 | man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8) |
164 | + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0); |
165 | chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0); |
166 | |
167 | if (man_id != 0x5CA3 /* Winbond */ |
168 | || chip_id != 0x70) { /* W83L785TS-S */ |
169 | dev_dbg(&adapter->dev, |
170 | "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", |
171 | man_id, chip_id); |
172 | return -ENODEV; |
173 | } |
174 | |
175 | strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE); |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | static int w83l785ts_probe(struct i2c_client *new_client, |
181 | const struct i2c_device_id *id) |
182 | { |
183 | struct w83l785ts_data *data; |
184 | int err = 0; |
185 | |
186 | data = kzalloc(sizeof(struct w83l785ts_data), GFP_KERNEL); |
187 | if (!data) { |
188 | err = -ENOMEM; |
189 | goto exit; |
190 | } |
191 | |
192 | i2c_set_clientdata(new_client, data); |
193 | data->valid = 0; |
194 | mutex_init(&data->update_lock); |
195 | |
196 | /* Default values in case the first read fails (unlikely). */ |
197 | data->temp[1] = data->temp[0] = 0; |
198 | |
199 | /* |
200 | * Initialize the W83L785TS chip |
201 | * Nothing yet, assume it is already started. |
202 | */ |
203 | |
204 | err = device_create_file(&new_client->dev, |
205 | &sensor_dev_attr_temp1_input.dev_attr); |
206 | if (err) |
207 | goto exit_remove; |
208 | |
209 | err = device_create_file(&new_client->dev, |
210 | &sensor_dev_attr_temp1_max.dev_attr); |
211 | if (err) |
212 | goto exit_remove; |
213 | |
214 | /* Register sysfs hooks */ |
215 | data->hwmon_dev = hwmon_device_register(&new_client->dev); |
216 | if (IS_ERR(data->hwmon_dev)) { |
217 | err = PTR_ERR(data->hwmon_dev); |
218 | goto exit_remove; |
219 | } |
220 | |
221 | return 0; |
222 | |
223 | exit_remove: |
224 | device_remove_file(&new_client->dev, |
225 | &sensor_dev_attr_temp1_input.dev_attr); |
226 | device_remove_file(&new_client->dev, |
227 | &sensor_dev_attr_temp1_max.dev_attr); |
228 | kfree(data); |
229 | exit: |
230 | return err; |
231 | } |
232 | |
233 | static int w83l785ts_remove(struct i2c_client *client) |
234 | { |
235 | struct w83l785ts_data *data = i2c_get_clientdata(client); |
236 | |
237 | hwmon_device_unregister(data->hwmon_dev); |
238 | device_remove_file(&client->dev, |
239 | &sensor_dev_attr_temp1_input.dev_attr); |
240 | device_remove_file(&client->dev, |
241 | &sensor_dev_attr_temp1_max.dev_attr); |
242 | |
243 | kfree(data); |
244 | return 0; |
245 | } |
246 | |
247 | static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) |
248 | { |
249 | int value, i; |
250 | struct device *dev; |
251 | const char *prefix; |
252 | |
253 | /* We might be called during detection, at which point the client |
254 | isn't yet fully initialized, so we can't use dev_dbg on it */ |
255 | if (i2c_get_clientdata(client)) { |
256 | dev = &client->dev; |
257 | prefix = ""; |
258 | } else { |
259 | dev = &client->adapter->dev; |
260 | prefix = "w83l785ts: "; |
261 | } |
262 | |
263 | /* Frequent read errors have been reported on Asus boards, so we |
264 | * retry on read errors. If it still fails (unlikely), return the |
265 | * default value requested by the caller. */ |
266 | for (i = 1; i <= MAX_RETRIES; i++) { |
267 | value = i2c_smbus_read_byte_data(client, reg); |
268 | if (value >= 0) { |
269 | dev_dbg(dev, "%sRead 0x%02x from register 0x%02x.\n", |
270 | prefix, value, reg); |
271 | return value; |
272 | } |
273 | dev_dbg(dev, "%sRead failed, will retry in %d.\n", prefix, i); |
274 | msleep(i); |
275 | } |
276 | |
277 | dev_err(dev, "%sCouldn't read value from register 0x%02x.\n", prefix, |
278 | reg); |
279 | return defval; |
280 | } |
281 | |
282 | static struct w83l785ts_data *w83l785ts_update_device(struct device *dev) |
283 | { |
284 | struct i2c_client *client = to_i2c_client(dev); |
285 | struct w83l785ts_data *data = i2c_get_clientdata(client); |
286 | |
287 | mutex_lock(&data->update_lock); |
288 | |
289 | if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) { |
290 | dev_dbg(&client->dev, "Updating w83l785ts data.\n"); |
291 | data->temp[0] = w83l785ts_read_value(client, |
292 | W83L785TS_REG_TEMP, data->temp[0]); |
293 | data->temp[1] = w83l785ts_read_value(client, |
294 | W83L785TS_REG_TEMP_OVER, data->temp[1]); |
295 | |
296 | data->last_updated = jiffies; |
297 | data->valid = 1; |
298 | } |
299 | |
300 | mutex_unlock(&data->update_lock); |
301 | |
302 | return data; |
303 | } |
304 | |
305 | static int __init sensors_w83l785ts_init(void) |
306 | { |
307 | return i2c_add_driver(&w83l785ts_driver); |
308 | } |
309 | |
310 | static void __exit sensors_w83l785ts_exit(void) |
311 | { |
312 | i2c_del_driver(&w83l785ts_driver); |
313 | } |
314 | |
315 | MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); |
316 | MODULE_DESCRIPTION("W83L785TS-S driver"); |
317 | MODULE_LICENSE("GPL"); |
318 | |
319 | module_init(sensors_w83l785ts_init); |
320 | module_exit(sensors_w83l785ts_exit); |
321 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9