diff options
Diffstat (limited to 'drivers/hwmon/tmp401.c')
-rw-r--r-- | drivers/hwmon/tmp401.c | 173 |
1 files changed, 149 insertions, 24 deletions
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 49932de571e6..7b34f2cd08bb 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c | |||
@@ -1,6 +1,9 @@ | |||
1 | /* tmp401.c | 1 | /* tmp401.c |
2 | * | 2 | * |
3 | * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com> | 3 | * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com> |
4 | * Preliminary tmp411 support by: | ||
5 | * Gabriel Konat, Sander Leget, Wouter Willems | ||
6 | * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de> | ||
4 | * | 7 | * |
5 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
@@ -40,8 +43,7 @@ | |||
40 | static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; | 43 | static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; |
41 | 44 | ||
42 | /* Insmod parameters */ | 45 | /* Insmod parameters */ |
43 | I2C_CLIENT_INSMOD_1(tmp401); | 46 | I2C_CLIENT_INSMOD_2(tmp401, tmp411); |
44 | |||
45 | 47 | ||
46 | /* | 48 | /* |
47 | * The TMP401 registers, note some registers have different addresses for | 49 | * The TMP401 registers, note some registers have different addresses for |
@@ -56,6 +58,7 @@ I2C_CLIENT_INSMOD_1(tmp401); | |||
56 | #define TMP401_CONSECUTIVE_ALERT 0x22 | 58 | #define TMP401_CONSECUTIVE_ALERT 0x22 |
57 | #define TMP401_MANUFACTURER_ID_REG 0xFE | 59 | #define TMP401_MANUFACTURER_ID_REG 0xFE |
58 | #define TMP401_DEVICE_ID_REG 0xFF | 60 | #define TMP401_DEVICE_ID_REG 0xFF |
61 | #define TMP411_N_FACTOR_REG 0x18 | ||
59 | 62 | ||
60 | static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 }; | 63 | static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 }; |
61 | static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 }; | 64 | static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 }; |
@@ -68,6 +71,11 @@ static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 }; | |||
68 | /* These are called the THERM limit / hysteresis / mask in the datasheet */ | 71 | /* These are called the THERM limit / hysteresis / mask in the datasheet */ |
69 | static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; | 72 | static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; |
70 | 73 | ||
74 | static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 }; | ||
75 | static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 }; | ||
76 | static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 }; | ||
77 | static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 }; | ||
78 | |||
71 | /* Flags */ | 79 | /* Flags */ |
72 | #define TMP401_CONFIG_RANGE 0x04 | 80 | #define TMP401_CONFIG_RANGE 0x04 |
73 | #define TMP401_CONFIG_SHUTDOWN 0x40 | 81 | #define TMP401_CONFIG_SHUTDOWN 0x40 |
@@ -82,6 +90,7 @@ static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; | |||
82 | /* Manufacturer / Device ID's */ | 90 | /* Manufacturer / Device ID's */ |
83 | #define TMP401_MANUFACTURER_ID 0x55 | 91 | #define TMP401_MANUFACTURER_ID 0x55 |
84 | #define TMP401_DEVICE_ID 0x11 | 92 | #define TMP401_DEVICE_ID 0x11 |
93 | #define TMP411_DEVICE_ID 0x12 | ||
85 | 94 | ||
86 | /* | 95 | /* |
87 | * Functions declarations | 96 | * Functions declarations |
@@ -100,6 +109,7 @@ static struct tmp401_data *tmp401_update_device(struct device *dev); | |||
100 | 109 | ||
101 | static const struct i2c_device_id tmp401_id[] = { | 110 | static const struct i2c_device_id tmp401_id[] = { |
102 | { "tmp401", tmp401 }, | 111 | { "tmp401", tmp401 }, |
112 | { "tmp411", tmp411 }, | ||
103 | { } | 113 | { } |
104 | }; | 114 | }; |
105 | MODULE_DEVICE_TABLE(i2c, tmp401_id); | 115 | MODULE_DEVICE_TABLE(i2c, tmp401_id); |
@@ -125,6 +135,7 @@ struct tmp401_data { | |||
125 | struct mutex update_lock; | 135 | struct mutex update_lock; |
126 | char valid; /* zero until following fields are valid */ | 136 | char valid; /* zero until following fields are valid */ |
127 | unsigned long last_updated; /* in jiffies */ | 137 | unsigned long last_updated; /* in jiffies */ |
138 | int kind; | ||
128 | 139 | ||
129 | /* register values */ | 140 | /* register values */ |
130 | u8 status; | 141 | u8 status; |
@@ -134,6 +145,8 @@ struct tmp401_data { | |||
134 | u16 temp_high[2]; | 145 | u16 temp_high[2]; |
135 | u8 temp_crit[2]; | 146 | u8 temp_crit[2]; |
136 | u8 temp_crit_hyst; | 147 | u8 temp_crit_hyst; |
148 | u16 temp_lowest[2]; | ||
149 | u16 temp_highest[2]; | ||
137 | }; | 150 | }; |
138 | 151 | ||
139 | /* | 152 | /* |
@@ -238,6 +251,28 @@ static ssize_t show_temp_crit_hyst(struct device *dev, | |||
238 | return sprintf(buf, "%d\n", temp); | 251 | return sprintf(buf, "%d\n", temp); |
239 | } | 252 | } |
240 | 253 | ||
254 | static ssize_t show_temp_lowest(struct device *dev, | ||
255 | struct device_attribute *devattr, char *buf) | ||
256 | { | ||
257 | int index = to_sensor_dev_attr(devattr)->index; | ||
258 | struct tmp401_data *data = tmp401_update_device(dev); | ||
259 | |||
260 | return sprintf(buf, "%d\n", | ||
261 | tmp401_register_to_temp(data->temp_lowest[index], | ||
262 | data->config)); | ||
263 | } | ||
264 | |||
265 | static ssize_t show_temp_highest(struct device *dev, | ||
266 | struct device_attribute *devattr, char *buf) | ||
267 | { | ||
268 | int index = to_sensor_dev_attr(devattr)->index; | ||
269 | struct tmp401_data *data = tmp401_update_device(dev); | ||
270 | |||
271 | return sprintf(buf, "%d\n", | ||
272 | tmp401_register_to_temp(data->temp_highest[index], | ||
273 | data->config)); | ||
274 | } | ||
275 | |||
241 | static ssize_t show_status(struct device *dev, | 276 | static ssize_t show_status(struct device *dev, |
242 | struct device_attribute *devattr, char *buf) | 277 | struct device_attribute *devattr, char *buf) |
243 | { | 278 | { |
@@ -361,6 +396,30 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute | |||
361 | return count; | 396 | return count; |
362 | } | 397 | } |
363 | 398 | ||
399 | /* | ||
400 | * Resets the historical measurements of minimum and maximum temperatures. | ||
401 | * This is done by writing any value to any of the minimum/maximum registers | ||
402 | * (0x30-0x37). | ||
403 | */ | ||
404 | static ssize_t reset_temp_history(struct device *dev, | ||
405 | struct device_attribute *devattr, const char *buf, size_t count) | ||
406 | { | ||
407 | long val; | ||
408 | |||
409 | if (strict_strtol(buf, 10, &val)) | ||
410 | return -EINVAL; | ||
411 | |||
412 | if (val != 1) { | ||
413 | dev_err(dev, "temp_reset_history value %ld not" | ||
414 | " supported. Use 1 to reset the history!\n", val); | ||
415 | return -EINVAL; | ||
416 | } | ||
417 | i2c_smbus_write_byte_data(to_i2c_client(dev), | ||
418 | TMP411_TEMP_LOWEST_MSB[0], val); | ||
419 | |||
420 | return count; | ||
421 | } | ||
422 | |||
364 | static struct sensor_device_attribute tmp401_attr[] = { | 423 | static struct sensor_device_attribute tmp401_attr[] = { |
365 | SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), | 424 | SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), |
366 | SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0), | 425 | SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0), |
@@ -390,6 +449,21 @@ static struct sensor_device_attribute tmp401_attr[] = { | |||
390 | }; | 449 | }; |
391 | 450 | ||
392 | /* | 451 | /* |
452 | * Additional features of the TMP411 chip. | ||
453 | * The TMP411 stores the minimum and maximum | ||
454 | * temperature measured since power-on, chip-reset, or | ||
455 | * minimum and maximum register reset for both the local | ||
456 | * and remote channels. | ||
457 | */ | ||
458 | static struct sensor_device_attribute tmp411_attr[] = { | ||
459 | SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0), | ||
460 | SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0), | ||
461 | SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1), | ||
462 | SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1), | ||
463 | SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0), | ||
464 | }; | ||
465 | |||
466 | /* | ||
393 | * Begin non sysfs callback code (aka Real code) | 467 | * Begin non sysfs callback code (aka Real code) |
394 | */ | 468 | */ |
395 | 469 | ||
@@ -432,8 +506,17 @@ static int tmp401_detect(struct i2c_client *client, int kind, | |||
432 | return -ENODEV; | 506 | return -ENODEV; |
433 | 507 | ||
434 | reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); | 508 | reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); |
435 | if (reg != TMP401_DEVICE_ID) | 509 | |
510 | switch (reg) { | ||
511 | case TMP401_DEVICE_ID: | ||
512 | kind = tmp401; | ||
513 | break; | ||
514 | case TMP411_DEVICE_ID: | ||
515 | kind = tmp411; | ||
516 | break; | ||
517 | default: | ||
436 | return -ENODEV; | 518 | return -ENODEV; |
519 | } | ||
437 | 520 | ||
438 | reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); | 521 | reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); |
439 | if (reg & 0x1b) | 522 | if (reg & 0x1b) |
@@ -441,10 +524,11 @@ static int tmp401_detect(struct i2c_client *client, int kind, | |||
441 | 524 | ||
442 | reg = i2c_smbus_read_byte_data(client, | 525 | reg = i2c_smbus_read_byte_data(client, |
443 | TMP401_CONVERSION_RATE_READ); | 526 | TMP401_CONVERSION_RATE_READ); |
527 | /* Datasheet says: 0x1-0x6 */ | ||
444 | if (reg > 15) | 528 | if (reg > 15) |
445 | return -ENODEV; | 529 | return -ENODEV; |
446 | } | 530 | } |
447 | strlcpy(info->type, "tmp401", I2C_NAME_SIZE); | 531 | strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE); |
448 | 532 | ||
449 | return 0; | 533 | return 0; |
450 | } | 534 | } |
@@ -454,6 +538,7 @@ static int tmp401_probe(struct i2c_client *client, | |||
454 | { | 538 | { |
455 | int i, err = 0; | 539 | int i, err = 0; |
456 | struct tmp401_data *data; | 540 | struct tmp401_data *data; |
541 | const char *names[] = { "TMP401", "TMP411" }; | ||
457 | 542 | ||
458 | data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL); | 543 | data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL); |
459 | if (!data) | 544 | if (!data) |
@@ -461,6 +546,7 @@ static int tmp401_probe(struct i2c_client *client, | |||
461 | 546 | ||
462 | i2c_set_clientdata(client, data); | 547 | i2c_set_clientdata(client, data); |
463 | mutex_init(&data->update_lock); | 548 | mutex_init(&data->update_lock); |
549 | data->kind = id->driver_data; | ||
464 | 550 | ||
465 | /* Initialize the TMP401 chip */ | 551 | /* Initialize the TMP401 chip */ |
466 | tmp401_init_client(client); | 552 | tmp401_init_client(client); |
@@ -473,6 +559,16 @@ static int tmp401_probe(struct i2c_client *client, | |||
473 | goto exit_remove; | 559 | goto exit_remove; |
474 | } | 560 | } |
475 | 561 | ||
562 | /* Register aditional tmp411 sysfs hooks */ | ||
563 | if (data->kind == tmp411) { | ||
564 | for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) { | ||
565 | err = device_create_file(&client->dev, | ||
566 | &tmp411_attr[i].dev_attr); | ||
567 | if (err) | ||
568 | goto exit_remove; | ||
569 | } | ||
570 | } | ||
571 | |||
476 | data->hwmon_dev = hwmon_device_register(&client->dev); | 572 | data->hwmon_dev = hwmon_device_register(&client->dev); |
477 | if (IS_ERR(data->hwmon_dev)) { | 573 | if (IS_ERR(data->hwmon_dev)) { |
478 | err = PTR_ERR(data->hwmon_dev); | 574 | err = PTR_ERR(data->hwmon_dev); |
@@ -480,7 +576,8 @@ static int tmp401_probe(struct i2c_client *client, | |||
480 | goto exit_remove; | 576 | goto exit_remove; |
481 | } | 577 | } |
482 | 578 | ||
483 | dev_info(&client->dev, "Detected TI TMP401 chip\n"); | 579 | dev_info(&client->dev, "Detected TI %s chip\n", |
580 | names[data->kind - 1]); | ||
484 | 581 | ||
485 | return 0; | 582 | return 0; |
486 | 583 | ||
@@ -500,15 +597,60 @@ static int tmp401_remove(struct i2c_client *client) | |||
500 | for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) | 597 | for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) |
501 | device_remove_file(&client->dev, &tmp401_attr[i].dev_attr); | 598 | device_remove_file(&client->dev, &tmp401_attr[i].dev_attr); |
502 | 599 | ||
600 | if (data->kind == tmp411) { | ||
601 | for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) | ||
602 | device_remove_file(&client->dev, | ||
603 | &tmp411_attr[i].dev_attr); | ||
604 | } | ||
605 | |||
503 | kfree(data); | 606 | kfree(data); |
504 | return 0; | 607 | return 0; |
505 | } | 608 | } |
506 | 609 | ||
610 | static struct tmp401_data *tmp401_update_device_reg16( | ||
611 | struct i2c_client *client, struct tmp401_data *data) | ||
612 | { | ||
613 | int i; | ||
614 | |||
615 | for (i = 0; i < 2; i++) { | ||
616 | /* | ||
617 | * High byte must be read first immediately followed | ||
618 | * by the low byte | ||
619 | */ | ||
620 | data->temp[i] = i2c_smbus_read_byte_data(client, | ||
621 | TMP401_TEMP_MSB[i]) << 8; | ||
622 | data->temp[i] |= i2c_smbus_read_byte_data(client, | ||
623 | TMP401_TEMP_LSB[i]); | ||
624 | data->temp_low[i] = i2c_smbus_read_byte_data(client, | ||
625 | TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8; | ||
626 | data->temp_low[i] |= i2c_smbus_read_byte_data(client, | ||
627 | TMP401_TEMP_LOW_LIMIT_LSB[i]); | ||
628 | data->temp_high[i] = i2c_smbus_read_byte_data(client, | ||
629 | TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8; | ||
630 | data->temp_high[i] |= i2c_smbus_read_byte_data(client, | ||
631 | TMP401_TEMP_HIGH_LIMIT_LSB[i]); | ||
632 | data->temp_crit[i] = i2c_smbus_read_byte_data(client, | ||
633 | TMP401_TEMP_CRIT_LIMIT[i]); | ||
634 | |||
635 | if (data->kind == tmp411) { | ||
636 | data->temp_lowest[i] = i2c_smbus_read_byte_data(client, | ||
637 | TMP411_TEMP_LOWEST_MSB[i]) << 8; | ||
638 | data->temp_lowest[i] |= i2c_smbus_read_byte_data( | ||
639 | client, TMP411_TEMP_LOWEST_LSB[i]); | ||
640 | |||
641 | data->temp_highest[i] = i2c_smbus_read_byte_data( | ||
642 | client, TMP411_TEMP_HIGHEST_MSB[i]) << 8; | ||
643 | data->temp_highest[i] |= i2c_smbus_read_byte_data( | ||
644 | client, TMP411_TEMP_HIGHEST_LSB[i]); | ||
645 | } | ||
646 | } | ||
647 | return data; | ||
648 | } | ||
649 | |||
507 | static struct tmp401_data *tmp401_update_device(struct device *dev) | 650 | static struct tmp401_data *tmp401_update_device(struct device *dev) |
508 | { | 651 | { |
509 | struct i2c_client *client = to_i2c_client(dev); | 652 | struct i2c_client *client = to_i2c_client(dev); |
510 | struct tmp401_data *data = i2c_get_clientdata(client); | 653 | struct tmp401_data *data = i2c_get_clientdata(client); |
511 | int i; | ||
512 | 654 | ||
513 | mutex_lock(&data->update_lock); | 655 | mutex_lock(&data->update_lock); |
514 | 656 | ||
@@ -516,24 +658,7 @@ static struct tmp401_data *tmp401_update_device(struct device *dev) | |||
516 | data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS); | 658 | data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS); |
517 | data->config = i2c_smbus_read_byte_data(client, | 659 | data->config = i2c_smbus_read_byte_data(client, |
518 | TMP401_CONFIG_READ); | 660 | TMP401_CONFIG_READ); |
519 | for (i = 0; i < 2; i++) { | 661 | tmp401_update_device_reg16(client, data); |
520 | /* High byte must be read first immediately followed | ||
521 | by the low byte */ | ||
522 | data->temp[i] = i2c_smbus_read_byte_data(client, | ||
523 | TMP401_TEMP_MSB[i]) << 8; | ||
524 | data->temp[i] |= i2c_smbus_read_byte_data(client, | ||
525 | TMP401_TEMP_LSB[i]); | ||
526 | data->temp_low[i] = i2c_smbus_read_byte_data(client, | ||
527 | TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8; | ||
528 | data->temp_low[i] |= i2c_smbus_read_byte_data(client, | ||
529 | TMP401_TEMP_LOW_LIMIT_LSB[i]); | ||
530 | data->temp_high[i] = i2c_smbus_read_byte_data(client, | ||
531 | TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8; | ||
532 | data->temp_high[i] |= i2c_smbus_read_byte_data(client, | ||
533 | TMP401_TEMP_HIGH_LIMIT_LSB[i]); | ||
534 | data->temp_crit[i] = i2c_smbus_read_byte_data(client, | ||
535 | TMP401_TEMP_CRIT_LIMIT[i]); | ||
536 | } | ||
537 | 662 | ||
538 | data->temp_crit_hyst = i2c_smbus_read_byte_data(client, | 663 | data->temp_crit_hyst = i2c_smbus_read_byte_data(client, |
539 | TMP401_TEMP_CRIT_HYST); | 664 | TMP401_TEMP_CRIT_HYST); |