Root/
1 | /* |
2 | * amc6821.c - Part of lm_sensors, Linux kernel modules for hardware |
3 | * monitoring |
4 | * Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si> |
5 | * |
6 | * Based on max6650.c: |
7 | * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de> |
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 | |
25 | #include <linux/kernel.h> /* Needed for KERN_INFO */ |
26 | #include <linux/module.h> |
27 | #include <linux/init.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/jiffies.h> |
30 | #include <linux/i2c.h> |
31 | #include <linux/hwmon.h> |
32 | #include <linux/hwmon-sysfs.h> |
33 | #include <linux/err.h> |
34 | #include <linux/mutex.h> |
35 | |
36 | |
37 | /* |
38 | * Addresses to scan. |
39 | */ |
40 | |
41 | static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e, |
42 | 0x4c, 0x4d, 0x4e, I2C_CLIENT_END}; |
43 | |
44 | |
45 | |
46 | /* |
47 | * Insmod parameters |
48 | */ |
49 | |
50 | static int pwminv; /*Inverted PWM output. */ |
51 | module_param(pwminv, int, S_IRUGO); |
52 | |
53 | static int init = 1; /*Power-on initialization.*/ |
54 | module_param(init, int, S_IRUGO); |
55 | |
56 | |
57 | enum chips { amc6821 }; |
58 | |
59 | #define AMC6821_REG_DEV_ID 0x3D |
60 | #define AMC6821_REG_COMP_ID 0x3E |
61 | #define AMC6821_REG_CONF1 0x00 |
62 | #define AMC6821_REG_CONF2 0x01 |
63 | #define AMC6821_REG_CONF3 0x3F |
64 | #define AMC6821_REG_CONF4 0x04 |
65 | #define AMC6821_REG_STAT1 0x02 |
66 | #define AMC6821_REG_STAT2 0x03 |
67 | #define AMC6821_REG_TDATA_LOW 0x08 |
68 | #define AMC6821_REG_TDATA_HI 0x09 |
69 | #define AMC6821_REG_LTEMP_HI 0x0A |
70 | #define AMC6821_REG_RTEMP_HI 0x0B |
71 | #define AMC6821_REG_LTEMP_LIMIT_MIN 0x15 |
72 | #define AMC6821_REG_LTEMP_LIMIT_MAX 0x14 |
73 | #define AMC6821_REG_RTEMP_LIMIT_MIN 0x19 |
74 | #define AMC6821_REG_RTEMP_LIMIT_MAX 0x18 |
75 | #define AMC6821_REG_LTEMP_CRIT 0x1B |
76 | #define AMC6821_REG_RTEMP_CRIT 0x1D |
77 | #define AMC6821_REG_PSV_TEMP 0x1C |
78 | #define AMC6821_REG_DCY 0x22 |
79 | #define AMC6821_REG_LTEMP_FAN_CTRL 0x24 |
80 | #define AMC6821_REG_RTEMP_FAN_CTRL 0x25 |
81 | #define AMC6821_REG_DCY_LOW_TEMP 0x21 |
82 | |
83 | #define AMC6821_REG_TACH_LLIMITL 0x10 |
84 | #define AMC6821_REG_TACH_LLIMITH 0x11 |
85 | #define AMC6821_REG_TACH_HLIMITL 0x12 |
86 | #define AMC6821_REG_TACH_HLIMITH 0x13 |
87 | |
88 | #define AMC6821_CONF1_START 0x01 |
89 | #define AMC6821_CONF1_FAN_INT_EN 0x02 |
90 | #define AMC6821_CONF1_FANIE 0x04 |
91 | #define AMC6821_CONF1_PWMINV 0x08 |
92 | #define AMC6821_CONF1_FAN_FAULT_EN 0x10 |
93 | #define AMC6821_CONF1_FDRC0 0x20 |
94 | #define AMC6821_CONF1_FDRC1 0x40 |
95 | #define AMC6821_CONF1_THERMOVIE 0x80 |
96 | |
97 | #define AMC6821_CONF2_PWM_EN 0x01 |
98 | #define AMC6821_CONF2_TACH_MODE 0x02 |
99 | #define AMC6821_CONF2_TACH_EN 0x04 |
100 | #define AMC6821_CONF2_RTFIE 0x08 |
101 | #define AMC6821_CONF2_LTOIE 0x10 |
102 | #define AMC6821_CONF2_RTOIE 0x20 |
103 | #define AMC6821_CONF2_PSVIE 0x40 |
104 | #define AMC6821_CONF2_RST 0x80 |
105 | |
106 | #define AMC6821_CONF3_THERM_FAN_EN 0x80 |
107 | #define AMC6821_CONF3_REV_MASK 0x0F |
108 | |
109 | #define AMC6821_CONF4_OVREN 0x10 |
110 | #define AMC6821_CONF4_TACH_FAST 0x20 |
111 | #define AMC6821_CONF4_PSPR 0x40 |
112 | #define AMC6821_CONF4_MODE 0x80 |
113 | |
114 | #define AMC6821_STAT1_RPM_ALARM 0x01 |
115 | #define AMC6821_STAT1_FANS 0x02 |
116 | #define AMC6821_STAT1_RTH 0x04 |
117 | #define AMC6821_STAT1_RTL 0x08 |
118 | #define AMC6821_STAT1_R_THERM 0x10 |
119 | #define AMC6821_STAT1_RTF 0x20 |
120 | #define AMC6821_STAT1_LTH 0x40 |
121 | #define AMC6821_STAT1_LTL 0x80 |
122 | |
123 | #define AMC6821_STAT2_RTC 0x08 |
124 | #define AMC6821_STAT2_LTC 0x10 |
125 | #define AMC6821_STAT2_LPSV 0x20 |
126 | #define AMC6821_STAT2_L_THERM 0x40 |
127 | #define AMC6821_STAT2_THERM_IN 0x80 |
128 | |
129 | enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, |
130 | IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, |
131 | IDX_TEMP2_MAX, IDX_TEMP2_CRIT, |
132 | TEMP_IDX_LEN, }; |
133 | |
134 | static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI, |
135 | AMC6821_REG_LTEMP_LIMIT_MIN, |
136 | AMC6821_REG_LTEMP_LIMIT_MAX, |
137 | AMC6821_REG_LTEMP_CRIT, |
138 | AMC6821_REG_RTEMP_HI, |
139 | AMC6821_REG_RTEMP_LIMIT_MIN, |
140 | AMC6821_REG_RTEMP_LIMIT_MAX, |
141 | AMC6821_REG_RTEMP_CRIT, }; |
142 | |
143 | enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX, |
144 | FAN1_IDX_LEN, }; |
145 | |
146 | static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, |
147 | AMC6821_REG_TACH_LLIMITL, |
148 | AMC6821_REG_TACH_HLIMITL, }; |
149 | |
150 | |
151 | static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI, |
152 | AMC6821_REG_TACH_LLIMITH, |
153 | AMC6821_REG_TACH_HLIMITH, }; |
154 | |
155 | static int amc6821_probe( |
156 | struct i2c_client *client, |
157 | const struct i2c_device_id *id); |
158 | static int amc6821_detect( |
159 | struct i2c_client *client, |
160 | struct i2c_board_info *info); |
161 | static int amc6821_init_client(struct i2c_client *client); |
162 | static int amc6821_remove(struct i2c_client *client); |
163 | static struct amc6821_data *amc6821_update_device(struct device *dev); |
164 | |
165 | /* |
166 | * Driver data (common to all clients) |
167 | */ |
168 | |
169 | static const struct i2c_device_id amc6821_id[] = { |
170 | { "amc6821", amc6821 }, |
171 | { } |
172 | }; |
173 | |
174 | MODULE_DEVICE_TABLE(i2c, amc6821_id); |
175 | |
176 | static struct i2c_driver amc6821_driver = { |
177 | .class = I2C_CLASS_HWMON, |
178 | .driver = { |
179 | .name = "amc6821", |
180 | }, |
181 | .probe = amc6821_probe, |
182 | .remove = amc6821_remove, |
183 | .id_table = amc6821_id, |
184 | .detect = amc6821_detect, |
185 | .address_list = normal_i2c, |
186 | }; |
187 | |
188 | |
189 | /* |
190 | * Client data (each client gets its own) |
191 | */ |
192 | |
193 | struct amc6821_data { |
194 | struct device *hwmon_dev; |
195 | struct mutex update_lock; |
196 | char valid; /* zero until following fields are valid */ |
197 | unsigned long last_updated; /* in jiffies */ |
198 | |
199 | /* register values */ |
200 | int temp[TEMP_IDX_LEN]; |
201 | |
202 | u16 fan[FAN1_IDX_LEN]; |
203 | u8 fan1_div; |
204 | |
205 | u8 pwm1; |
206 | u8 temp1_auto_point_temp[3]; |
207 | u8 temp2_auto_point_temp[3]; |
208 | u8 pwm1_auto_point_pwm[3]; |
209 | u8 pwm1_enable; |
210 | u8 pwm1_auto_channels_temp; |
211 | |
212 | u8 stat1; |
213 | u8 stat2; |
214 | }; |
215 | |
216 | |
217 | static ssize_t get_temp( |
218 | struct device *dev, |
219 | struct device_attribute *devattr, |
220 | char *buf) |
221 | { |
222 | struct amc6821_data *data = amc6821_update_device(dev); |
223 | int ix = to_sensor_dev_attr(devattr)->index; |
224 | |
225 | return sprintf(buf, "%d\n", data->temp[ix] * 1000); |
226 | } |
227 | |
228 | |
229 | |
230 | static ssize_t set_temp( |
231 | struct device *dev, |
232 | struct device_attribute *attr, |
233 | const char *buf, |
234 | size_t count) |
235 | { |
236 | struct i2c_client *client = to_i2c_client(dev); |
237 | struct amc6821_data *data = i2c_get_clientdata(client); |
238 | int ix = to_sensor_dev_attr(attr)->index; |
239 | long val; |
240 | |
241 | int ret = kstrtol(buf, 10, &val); |
242 | if (ret) |
243 | return ret; |
244 | val = SENSORS_LIMIT(val / 1000, -128, 127); |
245 | |
246 | mutex_lock(&data->update_lock); |
247 | data->temp[ix] = val; |
248 | if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) { |
249 | dev_err(&client->dev, "Register write error, aborting.\n"); |
250 | count = -EIO; |
251 | } |
252 | mutex_unlock(&data->update_lock); |
253 | return count; |
254 | } |
255 | |
256 | |
257 | |
258 | |
259 | static ssize_t get_temp_alarm( |
260 | struct device *dev, |
261 | struct device_attribute *devattr, |
262 | char *buf) |
263 | { |
264 | struct amc6821_data *data = amc6821_update_device(dev); |
265 | int ix = to_sensor_dev_attr(devattr)->index; |
266 | u8 flag; |
267 | |
268 | switch (ix) { |
269 | case IDX_TEMP1_MIN: |
270 | flag = data->stat1 & AMC6821_STAT1_LTL; |
271 | break; |
272 | case IDX_TEMP1_MAX: |
273 | flag = data->stat1 & AMC6821_STAT1_LTH; |
274 | break; |
275 | case IDX_TEMP1_CRIT: |
276 | flag = data->stat2 & AMC6821_STAT2_LTC; |
277 | break; |
278 | case IDX_TEMP2_MIN: |
279 | flag = data->stat1 & AMC6821_STAT1_RTL; |
280 | break; |
281 | case IDX_TEMP2_MAX: |
282 | flag = data->stat1 & AMC6821_STAT1_RTH; |
283 | break; |
284 | case IDX_TEMP2_CRIT: |
285 | flag = data->stat2 & AMC6821_STAT2_RTC; |
286 | break; |
287 | default: |
288 | dev_dbg(dev, "Unknown attr->index (%d).\n", ix); |
289 | return -EINVAL; |
290 | } |
291 | if (flag) |
292 | return sprintf(buf, "1"); |
293 | else |
294 | return sprintf(buf, "0"); |
295 | } |
296 | |
297 | |
298 | |
299 | |
300 | static ssize_t get_temp2_fault( |
301 | struct device *dev, |
302 | struct device_attribute *devattr, |
303 | char *buf) |
304 | { |
305 | struct amc6821_data *data = amc6821_update_device(dev); |
306 | if (data->stat1 & AMC6821_STAT1_RTF) |
307 | return sprintf(buf, "1"); |
308 | else |
309 | return sprintf(buf, "0"); |
310 | } |
311 | |
312 | static ssize_t get_pwm1( |
313 | struct device *dev, |
314 | struct device_attribute *devattr, |
315 | char *buf) |
316 | { |
317 | struct amc6821_data *data = amc6821_update_device(dev); |
318 | return sprintf(buf, "%d\n", data->pwm1); |
319 | } |
320 | |
321 | static ssize_t set_pwm1( |
322 | struct device *dev, |
323 | struct device_attribute *devattr, |
324 | const char *buf, |
325 | size_t count) |
326 | { |
327 | struct i2c_client *client = to_i2c_client(dev); |
328 | struct amc6821_data *data = i2c_get_clientdata(client); |
329 | long val; |
330 | int ret = kstrtol(buf, 10, &val); |
331 | if (ret) |
332 | return ret; |
333 | |
334 | mutex_lock(&data->update_lock); |
335 | data->pwm1 = SENSORS_LIMIT(val , 0, 255); |
336 | i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); |
337 | mutex_unlock(&data->update_lock); |
338 | return count; |
339 | } |
340 | |
341 | static ssize_t get_pwm1_enable( |
342 | struct device *dev, |
343 | struct device_attribute *devattr, |
344 | char *buf) |
345 | { |
346 | struct amc6821_data *data = amc6821_update_device(dev); |
347 | return sprintf(buf, "%d\n", data->pwm1_enable); |
348 | } |
349 | |
350 | static ssize_t set_pwm1_enable( |
351 | struct device *dev, |
352 | struct device_attribute *attr, |
353 | const char *buf, |
354 | size_t count) |
355 | { |
356 | struct i2c_client *client = to_i2c_client(dev); |
357 | struct amc6821_data *data = i2c_get_clientdata(client); |
358 | long val; |
359 | int config = kstrtol(buf, 10, &val); |
360 | if (config) |
361 | return config; |
362 | |
363 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); |
364 | if (config < 0) { |
365 | dev_err(&client->dev, |
366 | "Error reading configuration register, aborting.\n"); |
367 | return -EIO; |
368 | } |
369 | |
370 | switch (val) { |
371 | case 1: |
372 | config &= ~AMC6821_CONF1_FDRC0; |
373 | config &= ~AMC6821_CONF1_FDRC1; |
374 | break; |
375 | case 2: |
376 | config &= ~AMC6821_CONF1_FDRC0; |
377 | config |= AMC6821_CONF1_FDRC1; |
378 | break; |
379 | case 3: |
380 | config |= AMC6821_CONF1_FDRC0; |
381 | config |= AMC6821_CONF1_FDRC1; |
382 | break; |
383 | default: |
384 | return -EINVAL; |
385 | } |
386 | mutex_lock(&data->update_lock); |
387 | if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) { |
388 | dev_err(&client->dev, |
389 | "Configuration register write error, aborting.\n"); |
390 | count = -EIO; |
391 | } |
392 | mutex_unlock(&data->update_lock); |
393 | return count; |
394 | } |
395 | |
396 | |
397 | static ssize_t get_pwm1_auto_channels_temp( |
398 | struct device *dev, |
399 | struct device_attribute *devattr, |
400 | char *buf) |
401 | { |
402 | struct amc6821_data *data = amc6821_update_device(dev); |
403 | return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp); |
404 | } |
405 | |
406 | |
407 | static ssize_t get_temp_auto_point_temp( |
408 | struct device *dev, |
409 | struct device_attribute *devattr, |
410 | char *buf) |
411 | { |
412 | int ix = to_sensor_dev_attr_2(devattr)->index; |
413 | int nr = to_sensor_dev_attr_2(devattr)->nr; |
414 | struct amc6821_data *data = amc6821_update_device(dev); |
415 | switch (nr) { |
416 | case 1: |
417 | return sprintf(buf, "%d\n", |
418 | data->temp1_auto_point_temp[ix] * 1000); |
419 | break; |
420 | case 2: |
421 | return sprintf(buf, "%d\n", |
422 | data->temp2_auto_point_temp[ix] * 1000); |
423 | break; |
424 | default: |
425 | dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); |
426 | return -EINVAL; |
427 | } |
428 | } |
429 | |
430 | |
431 | static ssize_t get_pwm1_auto_point_pwm( |
432 | struct device *dev, |
433 | struct device_attribute *devattr, |
434 | char *buf) |
435 | { |
436 | int ix = to_sensor_dev_attr(devattr)->index; |
437 | struct amc6821_data *data = amc6821_update_device(dev); |
438 | return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]); |
439 | } |
440 | |
441 | |
442 | static inline ssize_t set_slope_register(struct i2c_client *client, |
443 | u8 reg, |
444 | u8 dpwm, |
445 | u8 *ptemp) |
446 | { |
447 | int dt; |
448 | u8 tmp; |
449 | |
450 | dt = ptemp[2]-ptemp[1]; |
451 | for (tmp = 4; tmp > 0; tmp--) { |
452 | if (dt * (0x20 >> tmp) >= dpwm) |
453 | break; |
454 | } |
455 | tmp |= (ptemp[1] & 0x7C) << 1; |
456 | if (i2c_smbus_write_byte_data(client, |
457 | reg, tmp)) { |
458 | dev_err(&client->dev, "Register write error, aborting.\n"); |
459 | return -EIO; |
460 | } |
461 | return 0; |
462 | } |
463 | |
464 | |
465 | |
466 | static ssize_t set_temp_auto_point_temp( |
467 | struct device *dev, |
468 | struct device_attribute *attr, |
469 | const char *buf, |
470 | size_t count) |
471 | { |
472 | struct i2c_client *client = to_i2c_client(dev); |
473 | struct amc6821_data *data = amc6821_update_device(dev); |
474 | int ix = to_sensor_dev_attr_2(attr)->index; |
475 | int nr = to_sensor_dev_attr_2(attr)->nr; |
476 | u8 *ptemp; |
477 | u8 reg; |
478 | int dpwm; |
479 | long val; |
480 | int ret = kstrtol(buf, 10, &val); |
481 | if (ret) |
482 | return ret; |
483 | |
484 | switch (nr) { |
485 | case 1: |
486 | ptemp = data->temp1_auto_point_temp; |
487 | reg = AMC6821_REG_LTEMP_FAN_CTRL; |
488 | break; |
489 | case 2: |
490 | ptemp = data->temp2_auto_point_temp; |
491 | reg = AMC6821_REG_RTEMP_FAN_CTRL; |
492 | break; |
493 | default: |
494 | dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); |
495 | return -EINVAL; |
496 | } |
497 | |
498 | data->valid = 0; |
499 | mutex_lock(&data->update_lock); |
500 | switch (ix) { |
501 | case 0: |
502 | ptemp[0] = SENSORS_LIMIT(val / 1000, 0, |
503 | data->temp1_auto_point_temp[1]); |
504 | ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, |
505 | data->temp2_auto_point_temp[1]); |
506 | ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, 63); |
507 | if (i2c_smbus_write_byte_data( |
508 | client, |
509 | AMC6821_REG_PSV_TEMP, |
510 | ptemp[0])) { |
511 | dev_err(&client->dev, |
512 | "Register write error, aborting.\n"); |
513 | count = -EIO; |
514 | } |
515 | goto EXIT; |
516 | break; |
517 | case 1: |
518 | ptemp[1] = SENSORS_LIMIT( |
519 | val / 1000, |
520 | (ptemp[0] & 0x7C) + 4, |
521 | 124); |
522 | ptemp[1] &= 0x7C; |
523 | ptemp[2] = SENSORS_LIMIT( |
524 | ptemp[2], ptemp[1] + 1, |
525 | 255); |
526 | break; |
527 | case 2: |
528 | ptemp[2] = SENSORS_LIMIT( |
529 | val / 1000, |
530 | ptemp[1]+1, |
531 | 255); |
532 | break; |
533 | default: |
534 | dev_dbg(dev, "Unknown attr->index (%d).\n", ix); |
535 | count = -EINVAL; |
536 | goto EXIT; |
537 | } |
538 | dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; |
539 | if (set_slope_register(client, reg, dpwm, ptemp)) |
540 | count = -EIO; |
541 | |
542 | EXIT: |
543 | mutex_unlock(&data->update_lock); |
544 | return count; |
545 | } |
546 | |
547 | |
548 | |
549 | static ssize_t set_pwm1_auto_point_pwm( |
550 | struct device *dev, |
551 | struct device_attribute *attr, |
552 | const char *buf, |
553 | size_t count) |
554 | { |
555 | struct i2c_client *client = to_i2c_client(dev); |
556 | struct amc6821_data *data = i2c_get_clientdata(client); |
557 | int dpwm; |
558 | long val; |
559 | int ret = kstrtol(buf, 10, &val); |
560 | if (ret) |
561 | return ret; |
562 | |
563 | mutex_lock(&data->update_lock); |
564 | data->pwm1_auto_point_pwm[1] = SENSORS_LIMIT(val, 0, 254); |
565 | if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, |
566 | data->pwm1_auto_point_pwm[1])) { |
567 | dev_err(&client->dev, "Register write error, aborting.\n"); |
568 | count = -EIO; |
569 | goto EXIT; |
570 | } |
571 | dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; |
572 | if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm, |
573 | data->temp1_auto_point_temp)) { |
574 | count = -EIO; |
575 | goto EXIT; |
576 | } |
577 | if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm, |
578 | data->temp2_auto_point_temp)) { |
579 | count = -EIO; |
580 | goto EXIT; |
581 | } |
582 | |
583 | EXIT: |
584 | data->valid = 0; |
585 | mutex_unlock(&data->update_lock); |
586 | return count; |
587 | } |
588 | |
589 | static ssize_t get_fan( |
590 | struct device *dev, |
591 | struct device_attribute *devattr, |
592 | char *buf) |
593 | { |
594 | struct amc6821_data *data = amc6821_update_device(dev); |
595 | int ix = to_sensor_dev_attr(devattr)->index; |
596 | if (0 == data->fan[ix]) |
597 | return sprintf(buf, "0"); |
598 | return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix])); |
599 | } |
600 | |
601 | |
602 | |
603 | static ssize_t get_fan1_fault( |
604 | struct device *dev, |
605 | struct device_attribute *devattr, |
606 | char *buf) |
607 | { |
608 | struct amc6821_data *data = amc6821_update_device(dev); |
609 | if (data->stat1 & AMC6821_STAT1_FANS) |
610 | return sprintf(buf, "1"); |
611 | else |
612 | return sprintf(buf, "0"); |
613 | } |
614 | |
615 | |
616 | |
617 | static ssize_t set_fan( |
618 | struct device *dev, |
619 | struct device_attribute *attr, |
620 | const char *buf, size_t count) |
621 | { |
622 | struct i2c_client *client = to_i2c_client(dev); |
623 | struct amc6821_data *data = i2c_get_clientdata(client); |
624 | long val; |
625 | int ix = to_sensor_dev_attr(attr)->index; |
626 | int ret = kstrtol(buf, 10, &val); |
627 | if (ret) |
628 | return ret; |
629 | val = 1 > val ? 0xFFFF : 6000000/val; |
630 | |
631 | mutex_lock(&data->update_lock); |
632 | data->fan[ix] = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); |
633 | if (i2c_smbus_write_byte_data(client, fan_reg_low[ix], |
634 | data->fan[ix] & 0xFF)) { |
635 | dev_err(&client->dev, "Register write error, aborting.\n"); |
636 | count = -EIO; |
637 | goto EXIT; |
638 | } |
639 | if (i2c_smbus_write_byte_data(client, |
640 | fan_reg_hi[ix], data->fan[ix] >> 8)) { |
641 | dev_err(&client->dev, "Register write error, aborting.\n"); |
642 | count = -EIO; |
643 | } |
644 | EXIT: |
645 | mutex_unlock(&data->update_lock); |
646 | return count; |
647 | } |
648 | |
649 | |
650 | |
651 | static ssize_t get_fan1_div( |
652 | struct device *dev, |
653 | struct device_attribute *devattr, |
654 | char *buf) |
655 | { |
656 | struct amc6821_data *data = amc6821_update_device(dev); |
657 | return sprintf(buf, "%d\n", data->fan1_div); |
658 | } |
659 | |
660 | static ssize_t set_fan1_div( |
661 | struct device *dev, |
662 | struct device_attribute *attr, |
663 | const char *buf, size_t count) |
664 | { |
665 | struct i2c_client *client = to_i2c_client(dev); |
666 | struct amc6821_data *data = i2c_get_clientdata(client); |
667 | long val; |
668 | int config = kstrtol(buf, 10, &val); |
669 | if (config) |
670 | return config; |
671 | |
672 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); |
673 | if (config < 0) { |
674 | dev_err(&client->dev, |
675 | "Error reading configuration register, aborting.\n"); |
676 | return -EIO; |
677 | } |
678 | mutex_lock(&data->update_lock); |
679 | switch (val) { |
680 | case 2: |
681 | config &= ~AMC6821_CONF4_PSPR; |
682 | data->fan1_div = 2; |
683 | break; |
684 | case 4: |
685 | config |= AMC6821_CONF4_PSPR; |
686 | data->fan1_div = 4; |
687 | break; |
688 | default: |
689 | count = -EINVAL; |
690 | goto EXIT; |
691 | } |
692 | if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) { |
693 | dev_err(&client->dev, |
694 | "Configuration register write error, aborting.\n"); |
695 | count = -EIO; |
696 | } |
697 | EXIT: |
698 | mutex_unlock(&data->update_lock); |
699 | return count; |
700 | } |
701 | |
702 | |
703 | |
704 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, |
705 | get_temp, NULL, IDX_TEMP1_INPUT); |
706 | static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp, |
707 | set_temp, IDX_TEMP1_MIN); |
708 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp, |
709 | set_temp, IDX_TEMP1_MAX); |
710 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp, |
711 | set_temp, IDX_TEMP1_CRIT); |
712 | static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, |
713 | get_temp_alarm, NULL, IDX_TEMP1_MIN); |
714 | static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, |
715 | get_temp_alarm, NULL, IDX_TEMP1_MAX); |
716 | static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, |
717 | get_temp_alarm, NULL, IDX_TEMP1_CRIT); |
718 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR, |
719 | get_temp, NULL, IDX_TEMP2_INPUT); |
720 | static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp, |
721 | set_temp, IDX_TEMP2_MIN); |
722 | static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp, |
723 | set_temp, IDX_TEMP2_MAX); |
724 | static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp, |
725 | set_temp, IDX_TEMP2_CRIT); |
726 | static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, |
727 | get_temp2_fault, NULL, 0); |
728 | static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, |
729 | get_temp_alarm, NULL, IDX_TEMP2_MIN); |
730 | static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, |
731 | get_temp_alarm, NULL, IDX_TEMP2_MAX); |
732 | static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, |
733 | get_temp_alarm, NULL, IDX_TEMP2_CRIT); |
734 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, IDX_FAN1_INPUT); |
735 | static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, |
736 | get_fan, set_fan, IDX_FAN1_MIN); |
737 | static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO | S_IWUSR, |
738 | get_fan, set_fan, IDX_FAN1_MAX); |
739 | static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan1_fault, NULL, 0); |
740 | static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, |
741 | get_fan1_div, set_fan1_div, 0); |
742 | |
743 | static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm1, set_pwm1, 0); |
744 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, |
745 | get_pwm1_enable, set_pwm1_enable, 0); |
746 | static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, |
747 | get_pwm1_auto_point_pwm, NULL, 0); |
748 | static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, |
749 | get_pwm1_auto_point_pwm, set_pwm1_auto_point_pwm, 1); |
750 | static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, |
751 | get_pwm1_auto_point_pwm, NULL, 2); |
752 | static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO, |
753 | get_pwm1_auto_channels_temp, NULL, 0); |
754 | static SENSOR_DEVICE_ATTR_2(temp1_auto_point1_temp, S_IRUGO, |
755 | get_temp_auto_point_temp, NULL, 1, 0); |
756 | static SENSOR_DEVICE_ATTR_2(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, |
757 | get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 1); |
758 | static SENSOR_DEVICE_ATTR_2(temp1_auto_point3_temp, S_IWUSR | S_IRUGO, |
759 | get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 2); |
760 | |
761 | static SENSOR_DEVICE_ATTR_2(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, |
762 | get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 0); |
763 | static SENSOR_DEVICE_ATTR_2(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, |
764 | get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 1); |
765 | static SENSOR_DEVICE_ATTR_2(temp2_auto_point3_temp, S_IWUSR | S_IRUGO, |
766 | get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 2); |
767 | |
768 | |
769 | |
770 | static struct attribute *amc6821_attrs[] = { |
771 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
772 | &sensor_dev_attr_temp1_min.dev_attr.attr, |
773 | &sensor_dev_attr_temp1_max.dev_attr.attr, |
774 | &sensor_dev_attr_temp1_crit.dev_attr.attr, |
775 | &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, |
776 | &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, |
777 | &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, |
778 | &sensor_dev_attr_temp2_input.dev_attr.attr, |
779 | &sensor_dev_attr_temp2_min.dev_attr.attr, |
780 | &sensor_dev_attr_temp2_max.dev_attr.attr, |
781 | &sensor_dev_attr_temp2_crit.dev_attr.attr, |
782 | &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, |
783 | &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, |
784 | &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, |
785 | &sensor_dev_attr_temp2_fault.dev_attr.attr, |
786 | &sensor_dev_attr_fan1_input.dev_attr.attr, |
787 | &sensor_dev_attr_fan1_min.dev_attr.attr, |
788 | &sensor_dev_attr_fan1_max.dev_attr.attr, |
789 | &sensor_dev_attr_fan1_fault.dev_attr.attr, |
790 | &sensor_dev_attr_fan1_div.dev_attr.attr, |
791 | &sensor_dev_attr_pwm1.dev_attr.attr, |
792 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, |
793 | &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, |
794 | &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, |
795 | &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, |
796 | &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, |
797 | &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, |
798 | &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, |
799 | &sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr, |
800 | &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, |
801 | &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, |
802 | &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr, |
803 | NULL |
804 | }; |
805 | |
806 | static struct attribute_group amc6821_attr_grp = { |
807 | .attrs = amc6821_attrs, |
808 | }; |
809 | |
810 | |
811 | |
812 | /* Return 0 if detection is successful, -ENODEV otherwise */ |
813 | static int amc6821_detect( |
814 | struct i2c_client *client, |
815 | struct i2c_board_info *info) |
816 | { |
817 | struct i2c_adapter *adapter = client->adapter; |
818 | int address = client->addr; |
819 | int dev_id, comp_id; |
820 | |
821 | dev_dbg(&adapter->dev, "amc6821_detect called.\n"); |
822 | |
823 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { |
824 | dev_dbg(&adapter->dev, |
825 | "amc6821: I2C bus doesn't support byte mode, " |
826 | "skipping.\n"); |
827 | return -ENODEV; |
828 | } |
829 | |
830 | dev_id = i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID); |
831 | comp_id = i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID); |
832 | if (dev_id != 0x21 || comp_id != 0x49) { |
833 | dev_dbg(&adapter->dev, |
834 | "amc6821: detection failed at 0x%02x.\n", |
835 | address); |
836 | return -ENODEV; |
837 | } |
838 | |
839 | /* |
840 | * Bit 7 of the address register is ignored, so we can check the |
841 | * ID registers again |
842 | */ |
843 | dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID); |
844 | comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID); |
845 | if (dev_id != 0x21 || comp_id != 0x49) { |
846 | dev_dbg(&adapter->dev, |
847 | "amc6821: detection failed at 0x%02x.\n", |
848 | address); |
849 | return -ENODEV; |
850 | } |
851 | |
852 | dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address); |
853 | strlcpy(info->type, "amc6821", I2C_NAME_SIZE); |
854 | |
855 | return 0; |
856 | } |
857 | |
858 | static int amc6821_probe( |
859 | struct i2c_client *client, |
860 | const struct i2c_device_id *id) |
861 | { |
862 | struct amc6821_data *data; |
863 | int err; |
864 | |
865 | data = kzalloc(sizeof(struct amc6821_data), GFP_KERNEL); |
866 | if (!data) { |
867 | dev_err(&client->dev, "out of memory.\n"); |
868 | return -ENOMEM; |
869 | } |
870 | |
871 | |
872 | i2c_set_clientdata(client, data); |
873 | mutex_init(&data->update_lock); |
874 | |
875 | /* |
876 | * Initialize the amc6821 chip |
877 | */ |
878 | err = amc6821_init_client(client); |
879 | if (err) |
880 | goto err_free; |
881 | |
882 | err = sysfs_create_group(&client->dev.kobj, &amc6821_attr_grp); |
883 | if (err) |
884 | goto err_free; |
885 | |
886 | data->hwmon_dev = hwmon_device_register(&client->dev); |
887 | if (!IS_ERR(data->hwmon_dev)) |
888 | return 0; |
889 | |
890 | err = PTR_ERR(data->hwmon_dev); |
891 | dev_err(&client->dev, "error registering hwmon device.\n"); |
892 | sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp); |
893 | err_free: |
894 | kfree(data); |
895 | return err; |
896 | } |
897 | |
898 | static int amc6821_remove(struct i2c_client *client) |
899 | { |
900 | struct amc6821_data *data = i2c_get_clientdata(client); |
901 | |
902 | hwmon_device_unregister(data->hwmon_dev); |
903 | sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp); |
904 | |
905 | kfree(data); |
906 | |
907 | return 0; |
908 | } |
909 | |
910 | |
911 | static int amc6821_init_client(struct i2c_client *client) |
912 | { |
913 | int config; |
914 | int err = -EIO; |
915 | |
916 | if (init) { |
917 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); |
918 | |
919 | if (config < 0) { |
920 | dev_err(&client->dev, |
921 | "Error reading configuration register, aborting.\n"); |
922 | return err; |
923 | } |
924 | |
925 | config |= AMC6821_CONF4_MODE; |
926 | |
927 | if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, |
928 | config)) { |
929 | dev_err(&client->dev, |
930 | "Configuration register write error, aborting.\n"); |
931 | return err; |
932 | } |
933 | |
934 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3); |
935 | |
936 | if (config < 0) { |
937 | dev_err(&client->dev, |
938 | "Error reading configuration register, aborting.\n"); |
939 | return err; |
940 | } |
941 | |
942 | dev_info(&client->dev, "Revision %d\n", config & 0x0f); |
943 | |
944 | config &= ~AMC6821_CONF3_THERM_FAN_EN; |
945 | |
946 | if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3, |
947 | config)) { |
948 | dev_err(&client->dev, |
949 | "Configuration register write error, aborting.\n"); |
950 | return err; |
951 | } |
952 | |
953 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2); |
954 | |
955 | if (config < 0) { |
956 | dev_err(&client->dev, |
957 | "Error reading configuration register, aborting.\n"); |
958 | return err; |
959 | } |
960 | |
961 | config &= ~AMC6821_CONF2_RTFIE; |
962 | config &= ~AMC6821_CONF2_LTOIE; |
963 | config &= ~AMC6821_CONF2_RTOIE; |
964 | if (i2c_smbus_write_byte_data(client, |
965 | AMC6821_REG_CONF2, config)) { |
966 | dev_err(&client->dev, |
967 | "Configuration register write error, aborting.\n"); |
968 | return err; |
969 | } |
970 | |
971 | config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); |
972 | |
973 | if (config < 0) { |
974 | dev_err(&client->dev, |
975 | "Error reading configuration register, aborting.\n"); |
976 | return err; |
977 | } |
978 | |
979 | config &= ~AMC6821_CONF1_THERMOVIE; |
980 | config &= ~AMC6821_CONF1_FANIE; |
981 | config |= AMC6821_CONF1_START; |
982 | if (pwminv) |
983 | config |= AMC6821_CONF1_PWMINV; |
984 | else |
985 | config &= ~AMC6821_CONF1_PWMINV; |
986 | |
987 | if (i2c_smbus_write_byte_data( |
988 | client, AMC6821_REG_CONF1, config)) { |
989 | dev_err(&client->dev, |
990 | "Configuration register write error, aborting.\n"); |
991 | return err; |
992 | } |
993 | } |
994 | return 0; |
995 | } |
996 | |
997 | |
998 | static struct amc6821_data *amc6821_update_device(struct device *dev) |
999 | { |
1000 | struct i2c_client *client = to_i2c_client(dev); |
1001 | struct amc6821_data *data = i2c_get_clientdata(client); |
1002 | int timeout = HZ; |
1003 | u8 reg; |
1004 | int i; |
1005 | |
1006 | mutex_lock(&data->update_lock); |
1007 | |
1008 | if (time_after(jiffies, data->last_updated + timeout) || |
1009 | !data->valid) { |
1010 | |
1011 | for (i = 0; i < TEMP_IDX_LEN; i++) |
1012 | data->temp[i] = i2c_smbus_read_byte_data(client, |
1013 | temp_reg[i]); |
1014 | |
1015 | data->stat1 = i2c_smbus_read_byte_data(client, |
1016 | AMC6821_REG_STAT1); |
1017 | data->stat2 = i2c_smbus_read_byte_data(client, |
1018 | AMC6821_REG_STAT2); |
1019 | |
1020 | data->pwm1 = i2c_smbus_read_byte_data(client, |
1021 | AMC6821_REG_DCY); |
1022 | for (i = 0; i < FAN1_IDX_LEN; i++) { |
1023 | data->fan[i] = i2c_smbus_read_byte_data( |
1024 | client, |
1025 | fan_reg_low[i]); |
1026 | data->fan[i] += i2c_smbus_read_byte_data( |
1027 | client, |
1028 | fan_reg_hi[i]) << 8; |
1029 | } |
1030 | data->fan1_div = i2c_smbus_read_byte_data(client, |
1031 | AMC6821_REG_CONF4); |
1032 | data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2; |
1033 | |
1034 | data->pwm1_auto_point_pwm[0] = 0; |
1035 | data->pwm1_auto_point_pwm[2] = 255; |
1036 | data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client, |
1037 | AMC6821_REG_DCY_LOW_TEMP); |
1038 | |
1039 | data->temp1_auto_point_temp[0] = |
1040 | i2c_smbus_read_byte_data(client, |
1041 | AMC6821_REG_PSV_TEMP); |
1042 | data->temp2_auto_point_temp[0] = |
1043 | data->temp1_auto_point_temp[0]; |
1044 | reg = i2c_smbus_read_byte_data(client, |
1045 | AMC6821_REG_LTEMP_FAN_CTRL); |
1046 | data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1; |
1047 | reg &= 0x07; |
1048 | reg = 0x20 >> reg; |
1049 | if (reg > 0) |
1050 | data->temp1_auto_point_temp[2] = |
1051 | data->temp1_auto_point_temp[1] + |
1052 | (data->pwm1_auto_point_pwm[2] - |
1053 | data->pwm1_auto_point_pwm[1]) / reg; |
1054 | else |
1055 | data->temp1_auto_point_temp[2] = 255; |
1056 | |
1057 | reg = i2c_smbus_read_byte_data(client, |
1058 | AMC6821_REG_RTEMP_FAN_CTRL); |
1059 | data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1; |
1060 | reg &= 0x07; |
1061 | reg = 0x20 >> reg; |
1062 | if (reg > 0) |
1063 | data->temp2_auto_point_temp[2] = |
1064 | data->temp2_auto_point_temp[1] + |
1065 | (data->pwm1_auto_point_pwm[2] - |
1066 | data->pwm1_auto_point_pwm[1]) / reg; |
1067 | else |
1068 | data->temp2_auto_point_temp[2] = 255; |
1069 | |
1070 | reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); |
1071 | reg = (reg >> 5) & 0x3; |
1072 | switch (reg) { |
1073 | case 0: /*open loop: software sets pwm1*/ |
1074 | data->pwm1_auto_channels_temp = 0; |
1075 | data->pwm1_enable = 1; |
1076 | break; |
1077 | case 2: /*closed loop: remote T (temp2)*/ |
1078 | data->pwm1_auto_channels_temp = 2; |
1079 | data->pwm1_enable = 2; |
1080 | break; |
1081 | case 3: /*closed loop: local and remote T (temp2)*/ |
1082 | data->pwm1_auto_channels_temp = 3; |
1083 | data->pwm1_enable = 3; |
1084 | break; |
1085 | case 1: /* |
1086 | * semi-open loop: software sets rpm, chip controls |
1087 | * pwm1, currently not implemented |
1088 | */ |
1089 | data->pwm1_auto_channels_temp = 0; |
1090 | data->pwm1_enable = 0; |
1091 | break; |
1092 | } |
1093 | |
1094 | data->last_updated = jiffies; |
1095 | data->valid = 1; |
1096 | } |
1097 | mutex_unlock(&data->update_lock); |
1098 | return data; |
1099 | } |
1100 | |
1101 | module_i2c_driver(amc6821_driver); |
1102 | |
1103 | MODULE_LICENSE("GPL"); |
1104 | MODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>"); |
1105 | MODULE_DESCRIPTION("Texas Instruments amc6821 hwmon driver"); |
1106 |
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