diff options
Diffstat (limited to 'drivers/thermal/thermal_sys.c')
-rw-r--r-- | drivers/thermal/thermal_sys.c | 142 |
1 files changed, 111 insertions, 31 deletions
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0b1c82ad6805..708f8e92771a 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c | |||
@@ -420,6 +420,29 @@ thermal_cooling_device_trip_point_show(struct device *dev, | |||
420 | 420 | ||
421 | /* hwmon sys I/F */ | 421 | /* hwmon sys I/F */ |
422 | #include <linux/hwmon.h> | 422 | #include <linux/hwmon.h> |
423 | |||
424 | /* thermal zone devices with the same type share one hwmon device */ | ||
425 | struct thermal_hwmon_device { | ||
426 | char type[THERMAL_NAME_LENGTH]; | ||
427 | struct device *device; | ||
428 | int count; | ||
429 | struct list_head tz_list; | ||
430 | struct list_head node; | ||
431 | }; | ||
432 | |||
433 | struct thermal_hwmon_attr { | ||
434 | struct device_attribute attr; | ||
435 | char name[16]; | ||
436 | }; | ||
437 | |||
438 | /* one temperature input for each thermal zone */ | ||
439 | struct thermal_hwmon_temp { | ||
440 | struct list_head hwmon_node; | ||
441 | struct thermal_zone_device *tz; | ||
442 | struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ | ||
443 | struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ | ||
444 | }; | ||
445 | |||
423 | static LIST_HEAD(thermal_hwmon_list); | 446 | static LIST_HEAD(thermal_hwmon_list); |
424 | 447 | ||
425 | static ssize_t | 448 | static ssize_t |
@@ -437,9 +460,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
437 | int ret; | 460 | int ret; |
438 | struct thermal_hwmon_attr *hwmon_attr | 461 | struct thermal_hwmon_attr *hwmon_attr |
439 | = container_of(attr, struct thermal_hwmon_attr, attr); | 462 | = container_of(attr, struct thermal_hwmon_attr, attr); |
440 | struct thermal_zone_device *tz | 463 | struct thermal_hwmon_temp *temp |
441 | = container_of(hwmon_attr, struct thermal_zone_device, | 464 | = container_of(hwmon_attr, struct thermal_hwmon_temp, |
442 | temp_input); | 465 | temp_input); |
466 | struct thermal_zone_device *tz = temp->tz; | ||
443 | 467 | ||
444 | ret = tz->ops->get_temp(tz, &temperature); | 468 | ret = tz->ops->get_temp(tz, &temperature); |
445 | 469 | ||
@@ -455,9 +479,10 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, | |||
455 | { | 479 | { |
456 | struct thermal_hwmon_attr *hwmon_attr | 480 | struct thermal_hwmon_attr *hwmon_attr |
457 | = container_of(attr, struct thermal_hwmon_attr, attr); | 481 | = container_of(attr, struct thermal_hwmon_attr, attr); |
458 | struct thermal_zone_device *tz | 482 | struct thermal_hwmon_temp *temp |
459 | = container_of(hwmon_attr, struct thermal_zone_device, | 483 | = container_of(hwmon_attr, struct thermal_hwmon_temp, |
460 | temp_crit); | 484 | temp_crit); |
485 | struct thermal_zone_device *tz = temp->tz; | ||
461 | long temperature; | 486 | long temperature; |
462 | int ret; | 487 | int ret; |
463 | 488 | ||
@@ -469,22 +494,54 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, | |||
469 | } | 494 | } |
470 | 495 | ||
471 | 496 | ||
472 | static int | 497 | static struct thermal_hwmon_device * |
473 | thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | 498 | thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) |
474 | { | 499 | { |
475 | struct thermal_hwmon_device *hwmon; | 500 | struct thermal_hwmon_device *hwmon; |
476 | int new_hwmon_device = 1; | ||
477 | int result; | ||
478 | 501 | ||
479 | mutex_lock(&thermal_list_lock); | 502 | mutex_lock(&thermal_list_lock); |
480 | list_for_each_entry(hwmon, &thermal_hwmon_list, node) | 503 | list_for_each_entry(hwmon, &thermal_hwmon_list, node) |
481 | if (!strcmp(hwmon->type, tz->type)) { | 504 | if (!strcmp(hwmon->type, tz->type)) { |
482 | new_hwmon_device = 0; | ||
483 | mutex_unlock(&thermal_list_lock); | 505 | mutex_unlock(&thermal_list_lock); |
484 | goto register_sys_interface; | 506 | return hwmon; |
507 | } | ||
508 | mutex_unlock(&thermal_list_lock); | ||
509 | |||
510 | return NULL; | ||
511 | } | ||
512 | |||
513 | /* Find the temperature input matching a given thermal zone */ | ||
514 | static struct thermal_hwmon_temp * | ||
515 | thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, | ||
516 | const struct thermal_zone_device *tz) | ||
517 | { | ||
518 | struct thermal_hwmon_temp *temp; | ||
519 | |||
520 | mutex_lock(&thermal_list_lock); | ||
521 | list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) | ||
522 | if (temp->tz == tz) { | ||
523 | mutex_unlock(&thermal_list_lock); | ||
524 | return temp; | ||
485 | } | 525 | } |
486 | mutex_unlock(&thermal_list_lock); | 526 | mutex_unlock(&thermal_list_lock); |
487 | 527 | ||
528 | return NULL; | ||
529 | } | ||
530 | |||
531 | static int | ||
532 | thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | ||
533 | { | ||
534 | struct thermal_hwmon_device *hwmon; | ||
535 | struct thermal_hwmon_temp *temp; | ||
536 | int new_hwmon_device = 1; | ||
537 | int result; | ||
538 | |||
539 | hwmon = thermal_hwmon_lookup_by_type(tz); | ||
540 | if (hwmon) { | ||
541 | new_hwmon_device = 0; | ||
542 | goto register_sys_interface; | ||
543 | } | ||
544 | |||
488 | hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL); | 545 | hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL); |
489 | if (!hwmon) | 546 | if (!hwmon) |
490 | return -ENOMEM; | 547 | return -ENOMEM; |
@@ -502,30 +559,36 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | |||
502 | goto free_mem; | 559 | goto free_mem; |
503 | 560 | ||
504 | register_sys_interface: | 561 | register_sys_interface: |
505 | tz->hwmon = hwmon; | 562 | temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL); |
563 | if (!temp) { | ||
564 | result = -ENOMEM; | ||
565 | goto unregister_name; | ||
566 | } | ||
567 | |||
568 | temp->tz = tz; | ||
506 | hwmon->count++; | 569 | hwmon->count++; |
507 | 570 | ||
508 | snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH, | 571 | snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH, |
509 | "temp%d_input", hwmon->count); | 572 | "temp%d_input", hwmon->count); |
510 | tz->temp_input.attr.attr.name = tz->temp_input.name; | 573 | temp->temp_input.attr.attr.name = temp->temp_input.name; |
511 | tz->temp_input.attr.attr.mode = 0444; | 574 | temp->temp_input.attr.attr.mode = 0444; |
512 | tz->temp_input.attr.show = temp_input_show; | 575 | temp->temp_input.attr.show = temp_input_show; |
513 | sysfs_attr_init(&tz->temp_input.attr.attr); | 576 | sysfs_attr_init(&temp->temp_input.attr.attr); |
514 | result = device_create_file(hwmon->device, &tz->temp_input.attr); | 577 | result = device_create_file(hwmon->device, &temp->temp_input.attr); |
515 | if (result) | 578 | if (result) |
516 | goto unregister_name; | 579 | goto free_temp_mem; |
517 | 580 | ||
518 | if (tz->ops->get_crit_temp) { | 581 | if (tz->ops->get_crit_temp) { |
519 | unsigned long temperature; | 582 | unsigned long temperature; |
520 | if (!tz->ops->get_crit_temp(tz, &temperature)) { | 583 | if (!tz->ops->get_crit_temp(tz, &temperature)) { |
521 | snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH, | 584 | snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH, |
522 | "temp%d_crit", hwmon->count); | 585 | "temp%d_crit", hwmon->count); |
523 | tz->temp_crit.attr.attr.name = tz->temp_crit.name; | 586 | temp->temp_crit.attr.attr.name = temp->temp_crit.name; |
524 | tz->temp_crit.attr.attr.mode = 0444; | 587 | temp->temp_crit.attr.attr.mode = 0444; |
525 | tz->temp_crit.attr.show = temp_crit_show; | 588 | temp->temp_crit.attr.show = temp_crit_show; |
526 | sysfs_attr_init(&tz->temp_crit.attr.attr); | 589 | sysfs_attr_init(&temp->temp_crit.attr.attr); |
527 | result = device_create_file(hwmon->device, | 590 | result = device_create_file(hwmon->device, |
528 | &tz->temp_crit.attr); | 591 | &temp->temp_crit.attr); |
529 | if (result) | 592 | if (result) |
530 | goto unregister_input; | 593 | goto unregister_input; |
531 | } | 594 | } |
@@ -534,13 +597,15 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | |||
534 | mutex_lock(&thermal_list_lock); | 597 | mutex_lock(&thermal_list_lock); |
535 | if (new_hwmon_device) | 598 | if (new_hwmon_device) |
536 | list_add_tail(&hwmon->node, &thermal_hwmon_list); | 599 | list_add_tail(&hwmon->node, &thermal_hwmon_list); |
537 | list_add_tail(&tz->hwmon_node, &hwmon->tz_list); | 600 | list_add_tail(&temp->hwmon_node, &hwmon->tz_list); |
538 | mutex_unlock(&thermal_list_lock); | 601 | mutex_unlock(&thermal_list_lock); |
539 | 602 | ||
540 | return 0; | 603 | return 0; |
541 | 604 | ||
542 | unregister_input: | 605 | unregister_input: |
543 | device_remove_file(hwmon->device, &tz->temp_input.attr); | 606 | device_remove_file(hwmon->device, &temp->temp_input.attr); |
607 | free_temp_mem: | ||
608 | kfree(temp); | ||
544 | unregister_name: | 609 | unregister_name: |
545 | if (new_hwmon_device) { | 610 | if (new_hwmon_device) { |
546 | device_remove_file(hwmon->device, &dev_attr_name); | 611 | device_remove_file(hwmon->device, &dev_attr_name); |
@@ -556,15 +621,30 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | |||
556 | static void | 621 | static void |
557 | thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) | 622 | thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) |
558 | { | 623 | { |
559 | struct thermal_hwmon_device *hwmon = tz->hwmon; | 624 | struct thermal_hwmon_device *hwmon; |
625 | struct thermal_hwmon_temp *temp; | ||
626 | |||
627 | hwmon = thermal_hwmon_lookup_by_type(tz); | ||
628 | if (unlikely(!hwmon)) { | ||
629 | /* Should never happen... */ | ||
630 | dev_dbg(&tz->device, "hwmon device lookup failed!\n"); | ||
631 | return; | ||
632 | } | ||
633 | |||
634 | temp = thermal_hwmon_lookup_temp(hwmon, tz); | ||
635 | if (unlikely(!temp)) { | ||
636 | /* Should never happen... */ | ||
637 | dev_dbg(&tz->device, "temperature input lookup failed!\n"); | ||
638 | return; | ||
639 | } | ||
560 | 640 | ||
561 | tz->hwmon = NULL; | 641 | device_remove_file(hwmon->device, &temp->temp_input.attr); |
562 | device_remove_file(hwmon->device, &tz->temp_input.attr); | ||
563 | if (tz->ops->get_crit_temp) | 642 | if (tz->ops->get_crit_temp) |
564 | device_remove_file(hwmon->device, &tz->temp_crit.attr); | 643 | device_remove_file(hwmon->device, &temp->temp_crit.attr); |
565 | 644 | ||
566 | mutex_lock(&thermal_list_lock); | 645 | mutex_lock(&thermal_list_lock); |
567 | list_del(&tz->hwmon_node); | 646 | list_del(&temp->hwmon_node); |
647 | kfree(temp); | ||
568 | if (!list_empty(&hwmon->tz_list)) { | 648 | if (!list_empty(&hwmon->tz_list)) { |
569 | mutex_unlock(&thermal_list_lock); | 649 | mutex_unlock(&thermal_list_lock); |
570 | return; | 650 | return; |