Pull thermal control updates from Rafael Wysocki: "These are thermal core changes, including the addition of support for temperature thresholds that can be set from user space, fixes related to thermal zone initialization, suspend/resume and exit, locking rework and rearrangement of the code handling thermal zone temperature updates. Specifics: - Add support for thermal thresholds that can be added and removed from user space via netlink along with a related library update (Daniel Lezcano) - Fix thermal zone initialization, suspend/resume and exit synchronization issues (Rafael Wysocki) - Rearrange locking in the thermal core to use guards (Rafael Wysocki) - Make the code handling thermal zone temperature updates use sorted lists of trip points to reduce the number of trip points table walks in the thermal core (Rafael Wysocki) - Fix and clean up the thermal testing facility code (Rafael Wysocki) - Fix a Power Allocator thermal governor issue (ZhengShaobo)" * tag 'thermal-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (45 commits) thermal: testing: Initialize some variables annoteded with _free() thermal: testing: Use DEFINE_FREE() and __free() to simplify code thermal: testing: Simplify tt_get_tt_zone() thermal: gov_power_allocator: Granted power set to max when nobody request power thermal: core: Relocate thermal zone initialization routine thermal: core: Use trip lists for trip crossing detection thermal: core: Eliminate thermal_zone_trip_down() thermal: core: Relocate functions that update trip points thermal: core: Move some trip processing to thermal_trip_crossed() thermal: core: Pass trip descriptor to thermal_trip_crossed() thermal: core: Rearrange __thermal_zone_device_update() thermal: core: Prepare for moving trips between sorted lists thermal: core: Rename trip list node in struct thermal_trip_desc thermal: core: Build sorted lists instead of sorting them later thermal/lib: Fix memory leak on error in thermal_genl_auto() thermal: thresholds: Fix thermal lock annotation issue tools/thermal/thermal-engine: Take into account the thresholds API tools/lib/thermal: Add the threshold netlink ABI tools/lib/thermal: Make more generic the command encoding function thermal: netlink: Add the commands and the events for the thresholds ...
Unified · Split
| @@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src) · 5 unmodified lines | |||
| 6 | obj-$(CONFIG_THERMAL) += thermal_sys.o | 6 | obj-$(CONFIG_THERMAL) += thermal_sys.o |
| 7 | thermal_sys-y += thermal_core.o thermal_sysfs.o | 7 | thermal_sys-y += thermal_core.o thermal_sysfs.o |
| 8 | thermal_sys-y += thermal_trip.o thermal_helpers.o | 8 | thermal_sys-y += thermal_trip.o thermal_helpers.o |
| 9 | thermal_sys-y += thermal_thresholds.o | ||
| 9 | 10 | ||
| 10 | # netlink interface to manage the thermal framework | 11 | # netlink interface to manage the thermal framework |
| 11 | thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o | 12 | thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o |
| @@ -30,9 +30,7 @@ static void bang_bang_set_instance_target(struct thermal_instance *instance, · 29 unmodified lines | |||
| 30 | 30 | ||
| 31 | dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); | 31 | dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); |
| 32 | 32 | ||
| 33 | mutex_lock(&instance->cdev->lock); | 33 | thermal_cdev_update_nocheck(instance->cdev); |
| 34 | __thermal_cdev_update(instance->cdev); | ||
| 35 | mutex_unlock(&instance->cdev->lock); | ||
| 36 | } | 34 | } |
| 37 | 35 | ||
| 38 | /** | 36 | /** |
| @@ -67,6 +65,7 @@ static void bang_bang_control(struct thermal_zone_device *tz, · 28 unmodified lines | |||
| 67 | const struct thermal_trip *trip, | 65 | const struct thermal_trip *trip, |
| 68 | bool crossed_up) | 66 | bool crossed_up) |
| 69 | { | 67 | { |
| 68 | const struct thermal_trip_desc *td = trip_to_trip_desc(trip); | ||
| 70 | struct thermal_instance *instance; | 69 | struct thermal_instance *instance; |
| 71 | 70 | ||
| 72 | lockdep_assert_held(&tz->lock); | 71 | lockdep_assert_held(&tz->lock); |
| @@ -75,10 +74,8 @@ static void bang_bang_control(struct thermal_zone_device *tz, · 2 unmodified lines | |||
| 75 | thermal_zone_trip_id(tz, trip), trip->temperature, | 74 | thermal_zone_trip_id(tz, trip), trip->temperature, |
| 76 | tz->temperature, trip->hysteresis); | 75 | tz->temperature, trip->hysteresis); |
| 77 | 76 | ||
| 78 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 77 | list_for_each_entry(instance, &td->thermal_instances, trip_node) |
| 79 | if (instance->trip == trip) | 78 | bang_bang_set_instance_target(instance, crossed_up); |
| 80 | bang_bang_set_instance_target(instance, crossed_up); | ||
| 81 | } | ||
| 82 | } | 79 | } |
| 83 | 80 | ||
| 84 | static void bang_bang_manage(struct thermal_zone_device *tz) | 81 | static void bang_bang_manage(struct thermal_zone_device *tz) |
| @@ -104,8 +101,8 @@ static void bang_bang_manage(struct thermal_zone_device *tz) · 19 unmodified lines | |||
| 104 | * to the thermal zone temperature and the trip point threshold. | 101 | * to the thermal zone temperature and the trip point threshold. |
| 105 | */ | 102 | */ |
| 106 | turn_on = tz->temperature >= td->threshold; | 103 | turn_on = tz->temperature >= td->threshold; |
| 107 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 104 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 108 | if (!instance->initialized && instance->trip == trip) | 105 | if (!instance->initialized) |
| 109 | bang_bang_set_instance_target(instance, turn_on); | 106 | bang_bang_set_instance_target(instance, turn_on); |
| 110 | } | 107 | } |
| 111 | } | 108 | } |
| @@ -44,7 +44,7 @@ static int get_trip_level(struct thermal_zone_device *tz) · 43 unmodified lines | |||
| 44 | /** | 44 | /** |
| 45 | * fair_share_throttle - throttles devices associated with the given zone | 45 | * fair_share_throttle - throttles devices associated with the given zone |
| 46 | * @tz: thermal_zone_device | 46 | * @tz: thermal_zone_device |
| 47 | * @trip: trip point | 47 | * @td: trip point descriptor |
| 48 | * @trip_level: number of trips crossed by the zone temperature | 48 | * @trip_level: number of trips crossed by the zone temperature |
| 49 | * | 49 | * |
| 50 | * Throttling Logic: This uses three parameters to calculate the new | 50 | * Throttling Logic: This uses three parameters to calculate the new |
| @@ -61,29 +61,23 @@ static int get_trip_level(struct thermal_zone_device *tz) · 10 unmodified lines | |||
| 61 | * new_state of cooling device = P3 * P2 * P1 | 61 | * new_state of cooling device = P3 * P2 * P1 |
| 62 | */ | 62 | */ |
| 63 | static void fair_share_throttle(struct thermal_zone_device *tz, | 63 | static void fair_share_throttle(struct thermal_zone_device *tz, |
| 64 | const struct thermal_trip *trip, | 64 | const struct thermal_trip_desc *td, |
| 65 | int trip_level) | 65 | int trip_level) |
| 66 | { | 66 | { |
| 67 | struct thermal_instance *instance; | 67 | struct thermal_instance *instance; |
| 68 | int total_weight = 0; | 68 | int total_weight = 0; |
| 69 | int nr_instances = 0; | 69 | int nr_instances = 0; |
| 70 | 70 | ||
| 71 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 71 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 72 | if (instance->trip != trip) | ||
| 73 | continue; | ||
| 74 | |||
| 75 | total_weight += instance->weight; | 72 | total_weight += instance->weight; |
| 76 | nr_instances++; | 73 | nr_instances++; |
| 77 | } | 74 | } |
| 78 | 75 | ||
| 79 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 76 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 80 | struct thermal_cooling_device *cdev = instance->cdev; | 77 | struct thermal_cooling_device *cdev = instance->cdev; |
| 81 | u64 dividend; | 78 | u64 dividend; |
| 82 | u32 divisor; | 79 | u32 divisor; |
| 83 | 80 | ||
| 84 | if (instance->trip != trip) | ||
| 85 | continue; | ||
| 86 | |||
| 87 | dividend = trip_level; | 81 | dividend = trip_level; |
| 88 | dividend *= cdev->max_state; | 82 | dividend *= cdev->max_state; |
| 89 | divisor = tz->num_trips; | 83 | divisor = tz->num_trips; |
| @@ -95,9 +89,7 @@ static void fair_share_throttle(struct thermal_zone_device *tz, · 5 unmodified lines | |||
| 95 | } | 89 | } |
| 96 | instance->target = div_u64(dividend, divisor); | 90 | instance->target = div_u64(dividend, divisor); |
| 97 | 91 | ||
| 98 | mutex_lock(&cdev->lock); | 92 | thermal_cdev_update_nocheck(cdev); |
| 99 | __thermal_cdev_update(cdev); | ||
| 100 | mutex_unlock(&cdev->lock); | ||
| 101 | } | 93 | } |
| 102 | } | 94 | } |
| 103 | 95 | ||
| @@ -116,7 +108,7 @@ static void fair_share_manage(struct thermal_zone_device *tz) · 12 unmodified lines | |||
| 116 | trip->type == THERMAL_TRIP_HOT) | 108 | trip->type == THERMAL_TRIP_HOT) |
| 117 | continue; | 109 | continue; |
| 118 | 110 | ||
| 119 | fair_share_throttle(tz, trip, trip_level); | 111 | fair_share_throttle(tz, td, trip_level); |
| 120 | } | 112 | } |
| 121 | } | 113 | } |
| 122 | 114 | ||
| @@ -97,11 +97,9 @@ struct power_allocator_params { · 96 unmodified lines | |||
| 97 | struct power_actor *power; | 97 | struct power_actor *power; |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
| 100 | static bool power_actor_is_valid(struct power_allocator_params *params, | 100 | static bool power_actor_is_valid(struct thermal_instance *instance) |
| 101 | struct thermal_instance *instance) | ||
| 102 | { | 101 | { |
| 103 | return (instance->trip == params->trip_max && | 102 | return cdev_is_power_actor(instance->cdev); |
| 104 | cdev_is_power_actor(instance->cdev)); | ||
| 105 | } | 103 | } |
| 106 | 104 | ||
| 107 | /** | 105 | /** |
| @@ -118,13 +116,14 @@ static bool power_actor_is_valid(struct power_allocator_params *params, · 10 unmodified lines | |||
| 118 | static u32 estimate_sustainable_power(struct thermal_zone_device *tz) | 116 | static u32 estimate_sustainable_power(struct thermal_zone_device *tz) |
| 119 | { | 117 | { |
| 120 | struct power_allocator_params *params = tz->governor_data; | 118 | struct power_allocator_params *params = tz->governor_data; |
| 119 | const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | ||
| 121 | struct thermal_cooling_device *cdev; | 120 | struct thermal_cooling_device *cdev; |
| 122 | struct thermal_instance *instance; | 121 | struct thermal_instance *instance; |
| 123 | u32 sustainable_power = 0; | 122 | u32 sustainable_power = 0; |
| 124 | u32 min_power; | 123 | u32 min_power; |
| 125 | 124 | ||
| 126 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 125 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 127 | if (!power_actor_is_valid(params, instance)) | 126 | if (!power_actor_is_valid(instance)) |
| 128 | continue; | 127 | continue; |
| 129 | 128 | ||
| 130 | cdev = instance->cdev; | 129 | cdev = instance->cdev; |
| @@ -323,9 +322,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev, · 192 unmodified lines | |||
| 323 | return ret; | 322 | return ret; |
| 324 | 323 | ||
| 325 | instance->target = clamp_val(state, instance->lower, instance->upper); | 324 | instance->target = clamp_val(state, instance->lower, instance->upper); |
| 326 | mutex_lock(&cdev->lock); | 325 | |
| 327 | __thermal_cdev_update(cdev); | 326 | thermal_cdev_update_nocheck(cdev); |
| 328 | mutex_unlock(&cdev->lock); | ||
| 329 | 327 | ||
| 330 | return 0; | 328 | return 0; |
| 331 | } | 329 | } |
| @@ -356,11 +354,19 @@ static void divvy_up_power(struct power_actor *power, int num_actors, · 24 unmodified lines | |||
| 356 | u32 extra_power = 0; | 354 | u32 extra_power = 0; |
| 357 | int i; | 355 | int i; |
| 358 | 356 | ||
| 359 | /* | 357 | if (!total_req_power) { |
| 360 | * Prevent division by 0 if none of the actors request power. | 358 | /* |
| 361 | */ | 359 | * Nobody requested anything, just give everybody |
| 362 | if (!total_req_power) | 360 | * the maximum power |
| 363 | total_req_power = 1; | 361 | */ |
| 362 | for (i = 0; i < num_actors; i++) { | ||
| 363 | struct power_actor *pa = &power[i]; | ||
| 364 | |||
| 365 | pa->granted_power = pa->max_power; | ||
| 366 | } | ||
| 367 | |||
| 368 | return; | ||
| 369 | } | ||
| 364 | 370 | ||
| 365 | for (i = 0; i < num_actors; i++) { | 371 | for (i = 0; i < num_actors; i++) { |
| 366 | struct power_actor *pa = &power[i]; | 372 | struct power_actor *pa = &power[i]; |
| @@ -400,6 +406,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, · 33 unmodified lines | |||
| 400 | static void allocate_power(struct thermal_zone_device *tz, int control_temp) | 406 | static void allocate_power(struct thermal_zone_device *tz, int control_temp) |
| 401 | { | 407 | { |
| 402 | struct power_allocator_params *params = tz->governor_data; | 408 | struct power_allocator_params *params = tz->governor_data; |
| 409 | const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | ||
| 403 | unsigned int num_actors = params->num_actors; | 410 | unsigned int num_actors = params->num_actors; |
| 404 | struct power_actor *power = params->power; | 411 | struct power_actor *power = params->power; |
| 405 | struct thermal_cooling_device *cdev; | 412 | struct thermal_cooling_device *cdev; |
| @@ -417,10 +424,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) · 11 unmodified lines | |||
| 417 | /* Clean all buffers for new power estimations */ | 424 | /* Clean all buffers for new power estimations */ |
| 418 | memset(power, 0, params->buffer_size); | 425 | memset(power, 0, params->buffer_size); |
| 419 | 426 | ||
| 420 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 427 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 421 | struct power_actor *pa = &power[i]; | 428 | struct power_actor *pa = &power[i]; |
| 422 | 429 | ||
| 423 | if (!power_actor_is_valid(params, instance)) | 430 | if (!power_actor_is_valid(instance)) |
| 424 | continue; | 431 | continue; |
| 425 | 432 | ||
| 426 | cdev = instance->cdev; | 433 | cdev = instance->cdev; |
| @@ -454,10 +461,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) · 27 unmodified lines | |||
| 454 | power_range); | 461 | power_range); |
| 455 | 462 | ||
| 456 | i = 0; | 463 | i = 0; |
| 457 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 464 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 458 | struct power_actor *pa = &power[i]; | 465 | struct power_actor *pa = &power[i]; |
| 459 | 466 | ||
| 460 | if (!power_actor_is_valid(params, instance)) | 467 | if (!power_actor_is_valid(instance)) |
| 461 | continue; | 468 | continue; |
| 462 | 469 | ||
| 463 | power_actor_set_power(instance->cdev, instance, | 470 | power_actor_set_power(instance->cdev, instance, |
| @@ -538,29 +545,29 @@ static void reset_pid_controller(struct power_allocator_params *params) · 74 unmodified lines | |||
| 538 | static void allow_maximum_power(struct thermal_zone_device *tz) | 545 | static void allow_maximum_power(struct thermal_zone_device *tz) |
| 539 | { | 546 | { |
| 540 | struct power_allocator_params *params = tz->governor_data; | 547 | struct power_allocator_params *params = tz->governor_data; |
| 548 | const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | ||
| 541 | struct thermal_cooling_device *cdev; | 549 | struct thermal_cooling_device *cdev; |
| 542 | struct thermal_instance *instance; | 550 | struct thermal_instance *instance; |
| 543 | u32 req_power; | 551 | u32 req_power; |
| 544 | 552 | ||
| 545 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 553 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 546 | if (!power_actor_is_valid(params, instance)) | 554 | if (!power_actor_is_valid(instance)) |
| 547 | continue; | 555 | continue; |
| 548 | 556 | ||
| 549 | cdev = instance->cdev; | 557 | cdev = instance->cdev; |
| 550 | 558 | ||
| 551 | instance->target = 0; | 559 | instance->target = 0; |
| 552 | mutex_lock(&cdev->lock); | 560 | scoped_guard(cooling_dev, cdev) { |
| 553 | /* | 561 | /* |
| 554 | * Call for updating the cooling devices local stats and avoid | 562 | * Call for updating the cooling devices local stats and |
| 555 | * periods of dozen of seconds when those have not been | 563 | * avoid periods of dozen of seconds when those have not |
| 556 | * maintained. | 564 | * been maintained. |
| 557 | */ | 565 | */ |
| 558 | cdev->ops->get_requested_power(cdev, &req_power); | 566 | cdev->ops->get_requested_power(cdev, &req_power); |
| 559 | 567 | ||
| 560 | if (params->update_cdevs) | 568 | if (params->update_cdevs) |
| 561 | __thermal_cdev_update(cdev); | 569 | __thermal_cdev_update(cdev); |
| 562 | 570 | } | |
| 563 | mutex_unlock(&cdev->lock); | ||
| 564 | } | 571 | } |
| 565 | } | 572 | } |
| 566 | 573 | ||
| @@ -581,13 +588,11 @@ static void allow_maximum_power(struct thermal_zone_device *tz) · 14 unmodified lines | |||
| 581 | static int check_power_actors(struct thermal_zone_device *tz, | 588 | static int check_power_actors(struct thermal_zone_device *tz, |
| 582 | struct power_allocator_params *params) | 589 | struct power_allocator_params *params) |
| 583 | { | 590 | { |
| 591 | const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | ||
| 584 | struct thermal_instance *instance; | 592 | struct thermal_instance *instance; |
| 585 | int ret = 0; | 593 | int ret = 0; |
| 586 | 594 | ||
| 587 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 595 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 588 | if (instance->trip != params->trip_max) | ||
| 589 | continue; | ||
| 590 | |||
| 591 | if (!cdev_is_power_actor(instance->cdev)) { | 596 | if (!cdev_is_power_actor(instance->cdev)) { |
| 592 | dev_warn(&tz->device, "power_allocator: %s is not a power actor\n", | 597 | dev_warn(&tz->device, "power_allocator: %s is not a power actor\n", |
| 593 | instance->cdev->type); | 598 | instance->cdev->type); |
| @@ -635,14 +640,15 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, · 41 unmodified lines | |||
| 635 | enum thermal_notify_event reason) | 640 | enum thermal_notify_event reason) |
| 636 | { | 641 | { |
| 637 | struct power_allocator_params *params = tz->governor_data; | 642 | struct power_allocator_params *params = tz->governor_data; |
| 643 | const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | ||
| 638 | struct thermal_instance *instance; | 644 | struct thermal_instance *instance; |
| 639 | int num_actors = 0; | 645 | int num_actors = 0; |
| 640 | 646 | ||
| 641 | switch (reason) { | 647 | switch (reason) { |
| 642 | case THERMAL_TZ_BIND_CDEV: | 648 | case THERMAL_TZ_BIND_CDEV: |
| 643 | case THERMAL_TZ_UNBIND_CDEV: | 649 | case THERMAL_TZ_UNBIND_CDEV: |
| 644 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) | 650 | list_for_each_entry(instance, &td->thermal_instances, trip_node) |
| 645 | if (power_actor_is_valid(params, instance)) | 651 | if (power_actor_is_valid(instance)) |
| 646 | num_actors++; | 652 | num_actors++; |
| 647 | 653 | ||
| 648 | if (num_actors == params->num_actors) | 654 | if (num_actors == params->num_actors) |
| @@ -652,8 +658,8 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, · 3 unmodified lines | |||
| 652 | break; | 658 | break; |
| 653 | case THERMAL_INSTANCE_WEIGHT_CHANGED: | 659 | case THERMAL_INSTANCE_WEIGHT_CHANGED: |
| 654 | params->total_weight = 0; | 660 | params->total_weight = 0; |
| 655 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) | 661 | list_for_each_entry(instance, &td->thermal_instances, trip_node) |
| 656 | if (power_actor_is_valid(params, instance)) | 662 | if (power_actor_is_valid(instance)) |
| 657 | params->total_weight += instance->weight; | 663 | params->total_weight += instance->weight; |
| 658 | break; | 664 | break; |
| 659 | default: | 665 | default: |
| @@ -66,9 +66,10 @@ static unsigned long get_target_state(struct thermal_instance *instance, · 65 unmodified lines | |||
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | static void thermal_zone_trip_update(struct thermal_zone_device *tz, | 68 | static void thermal_zone_trip_update(struct thermal_zone_device *tz, |
| 69 | const struct thermal_trip *trip, | 69 | const struct thermal_trip_desc *td, |
| 70 | int trip_threshold) | 70 | int trip_threshold) |
| 71 | { | 71 | { |
| 72 | const struct thermal_trip *trip = &td->trip; | ||
| 72 | enum thermal_trend trend = get_tz_trend(tz, trip); | 73 | enum thermal_trend trend = get_tz_trend(tz, trip); |
| 73 | int trip_id = thermal_zone_trip_id(tz, trip); | 74 | int trip_id = thermal_zone_trip_id(tz, trip); |
| 74 | struct thermal_instance *instance; | 75 | struct thermal_instance *instance; |
| @@ -82,12 +83,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, · 7 unmodified lines | |||
| 82 | dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", | 83 | dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", |
| 83 | trip_id, trip->type, trip_threshold, trend, throttle); | 84 | trip_id, trip->type, trip_threshold, trend, throttle); |
| 84 | 85 | ||
| 85 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | 86 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { |
| 86 | int old_target; | 87 | int old_target; |
| 87 | 88 | ||
| 88 | if (instance->trip != trip) | ||
| 89 | continue; | ||
| 90 | |||
| 91 | old_target = instance->target; | 89 | old_target = instance->target; |
| 92 | instance->target = get_target_state(instance, trend, throttle); | 90 | instance->target = get_target_state(instance, trend, throttle); |
| 93 | 91 | ||
| @@ -99,9 +97,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, · 5 unmodified lines | |||
| 99 | 97 | ||
| 100 | instance->initialized = true; | 98 | instance->initialized = true; |
| 101 | 99 | ||
| 102 | mutex_lock(&instance->cdev->lock); | 100 | scoped_guard(cooling_dev, instance->cdev) { |
| 103 | instance->cdev->updated = false; /* cdev needs update */ | 101 | instance->cdev->updated = false; /* cdev needs update */ |
| 104 | mutex_unlock(&instance->cdev->lock); | 102 | } |
| 105 | } | 103 | } |
| 106 | } | 104 | } |
| 107 | 105 | ||
| @@ -127,11 +125,13 @@ static void step_wise_manage(struct thermal_zone_device *tz) · 19 unmodified lines | |||
| 127 | trip->type == THERMAL_TRIP_HOT) | 125 | trip->type == THERMAL_TRIP_HOT) |
| 128 | continue; | 126 | continue; |
| 129 | 127 | ||
| 130 | thermal_zone_trip_update(tz, trip, td->threshold); | 128 | thermal_zone_trip_update(tz, td, td->threshold); |
| 131 | } | 129 | } |
| 132 | 130 | ||
| 133 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) | 131 | for_each_trip_desc(tz, td) { |
| 134 | thermal_cdev_update(instance->cdev); | 132 | list_for_each_entry(instance, &td->thermal_instances, trip_node) |
| 133 | thermal_cdev_update(instance->cdev); | ||
| 134 | } | ||
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | static struct thermal_governor thermal_gov_step_wise = { | 137 | static struct thermal_governor thermal_gov_step_wise = { |
| @@ -185,7 +185,7 @@ static void tt_add_tz_work_fn(struct work_struct *work) · 184 unmodified lines | |||
| 185 | int tt_add_tz(void) | 185 | int tt_add_tz(void) |
| 186 | { | 186 | { |
| 187 | struct tt_thermal_zone *tt_zone __free(kfree); | 187 | struct tt_thermal_zone *tt_zone __free(kfree); |
| 188 | struct tt_work *tt_work __free(kfree); | 188 | struct tt_work *tt_work __free(kfree) = NULL; |
| 189 | int ret; | 189 | int ret; |
| 190 | 190 | ||
| 191 | tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); | 191 | tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); |
| @@ -237,7 +237,7 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) · 45 unmodified lines | |||
| 237 | 237 | ||
| 238 | int tt_del_tz(const char *arg) | 238 | int tt_del_tz(const char *arg) |
| 239 | { | 239 | { |
| 240 | struct tt_work *tt_work __free(kfree); | 240 | struct tt_work *tt_work __free(kfree) = NULL; |
| 241 | struct tt_thermal_zone *tt_zone, *aux; | 241 | struct tt_thermal_zone *tt_zone, *aux; |
| 242 | int ret; | 242 | int ret; |
| 243 | int id; | 243 | int id; |
| @@ -288,19 +288,14 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) · 44 unmodified lines | |||
| 288 | 288 | ||
| 289 | guard(mutex)(&tt_thermal_zones_lock); | 289 | guard(mutex)(&tt_thermal_zones_lock); |
| 290 | 290 | ||
| 291 | ret = -EINVAL; | ||
| 292 | list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { | 291 | list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { |
| 293 | if (tt_zone->id == id) { | 292 | if (tt_zone->id == id) { |
| 294 | tt_zone->refcount++; | 293 | tt_zone->refcount++; |
| 295 | ret = 0; | 294 | return tt_zone; |
| 296 | break; | ||
| 297 | } | 295 | } |
| 298 | } | 296 | } |
| 299 | 297 | ||
| 300 | if (ret) | 298 | return ERR_PTR(-EINVAL); |
| 301 | return ERR_PTR(ret); | ||
| 302 | |||
| 303 | return tt_zone; | ||
| 304 | } | 299 | } |
| 305 | 300 | ||
| 306 | static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) | 301 | static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) |
| @@ -310,6 +305,9 @@ static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) · 3 unmodified lines | |||
| 310 | tt_zone->refcount--; | 305 | tt_zone->refcount--; |
| 311 | } | 306 | } |
| 312 | 307 | ||
| 308 | DEFINE_FREE(put_tt_zone, struct tt_thermal_zone *, | ||
| 309 | if (!IS_ERR_OR_NULL(_T)) tt_put_tt_zone(_T)) | ||
| 310 | |||
| 313 | static void tt_zone_add_trip_work_fn(struct work_struct *work) | 311 | static void tt_zone_add_trip_work_fn(struct work_struct *work) |
| 314 | { | 312 | { |
| 315 | struct tt_work *tt_work = tt_work_of_work(work); | 313 | struct tt_work *tt_work = tt_work_of_work(work); |
| @@ -332,9 +330,9 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) · 16 unmodified lines | |||
| 332 | 330 | ||
| 333 | int tt_zone_add_trip(const char *arg) | 331 | int tt_zone_add_trip(const char *arg) |
| 334 | { | 332 | { |
| 333 | struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; | ||
| 334 | struct tt_trip *tt_trip __free(kfree) = NULL; | ||
| 335 | struct tt_work *tt_work __free(kfree); | 335 | struct tt_work *tt_work __free(kfree); |
| 336 | struct tt_trip *tt_trip __free(kfree); | ||
| 337 | struct tt_thermal_zone *tt_zone; | ||
| 338 | int id; | 336 | int id; |
| 339 | 337 | ||
| 340 | tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); | 338 | tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); |
| @@ -350,10 +348,8 @@ int tt_zone_add_trip(const char *arg) · 9 unmodified lines | |||
| 350 | return PTR_ERR(tt_zone); | 348 | return PTR_ERR(tt_zone); |
| 351 | 349 | ||
| 352 | id = ida_alloc(&tt_zone->ida, GFP_KERNEL); | 350 | id = ida_alloc(&tt_zone->ida, GFP_KERNEL); |
| 353 | if (id < 0) { | 351 | if (id < 0) |
| 354 | tt_put_tt_zone(tt_zone); | ||
| 355 | return id; | 352 | return id; |
| 356 | } | ||
| 357 | 353 | ||
| 358 | tt_trip->trip.type = THERMAL_TRIP_ACTIVE; | 354 | tt_trip->trip.type = THERMAL_TRIP_ACTIVE; |
| 359 | tt_trip->trip.temperature = THERMAL_TEMP_INVALID; | 355 | tt_trip->trip.temperature = THERMAL_TEMP_INVALID; |
| @@ -366,7 +362,7 @@ int tt_zone_add_trip(const char *arg) · 6 unmodified lines | |||
| 366 | tt_zone->num_trips++; | 362 | tt_zone->num_trips++; |
| 367 | 363 | ||
| 368 | INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); | 364 | INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); |
| 369 | tt_work->tt_zone = tt_zone; | 365 | tt_work->tt_zone = no_free_ptr(tt_zone); |
| 370 | tt_work->tt_trip = no_free_ptr(tt_trip); | 366 | tt_work->tt_trip = no_free_ptr(tt_trip); |
| 371 | schedule_work(&(no_free_ptr(tt_work)->work)); | 367 | schedule_work(&(no_free_ptr(tt_work)->work)); |
| 372 | 368 | ||
| @@ -391,7 +387,7 @@ static struct thermal_zone_device_ops tt_zone_ops = { · 18 unmodified lines | |||
| 391 | 387 | ||
| 392 | static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) | 388 | static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) |
| 393 | { | 389 | { |
| 394 | struct thermal_trip *trips __free(kfree); | 390 | struct thermal_trip *trips __free(kfree) = NULL; |
| 395 | struct thermal_zone_device *tz; | 391 | struct thermal_zone_device *tz; |
| 396 | struct tt_trip *tt_trip; | 392 | struct tt_trip *tt_trip; |
| 397 | int i; | 393 | int i; |
| @@ -425,23 +421,18 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) · 27 unmodified lines | |||
| 425 | 421 | ||
| 426 | int tt_zone_reg(const char *arg) | 422 | int tt_zone_reg(const char *arg) |
| 427 | { | 423 | { |
| 428 | struct tt_thermal_zone *tt_zone; | 424 | struct tt_thermal_zone *tt_zone __free(put_tt_zone); |
| 429 | int ret; | ||
| 430 | 425 | ||
| 431 | tt_zone = tt_get_tt_zone(arg); | 426 | tt_zone = tt_get_tt_zone(arg); |
| 432 | if (IS_ERR(tt_zone)) | 427 | if (IS_ERR(tt_zone)) |
| 433 | return PTR_ERR(tt_zone); | 428 | return PTR_ERR(tt_zone); |
| 434 | 429 | ||
| 435 | ret = tt_zone_register_tz(tt_zone); | 430 | return tt_zone_register_tz(tt_zone); |
| 436 | |||
| 437 | tt_put_tt_zone(tt_zone); | ||
| 438 | |||
| 439 | return ret; | ||
| 440 | } | 431 | } |
| 441 | 432 | ||
| 442 | int tt_zone_unreg(const char *arg) | 433 | int tt_zone_unreg(const char *arg) |
| 443 | { | 434 | { |
| 444 | struct tt_thermal_zone *tt_zone; | 435 | struct tt_thermal_zone *tt_zone __free(put_tt_zone); |
| 445 | 436 | ||
| 446 | tt_zone = tt_get_tt_zone(arg); | 437 | tt_zone = tt_get_tt_zone(arg); |
| 447 | if (IS_ERR(tt_zone)) | 438 | if (IS_ERR(tt_zone)) |
| @@ -449,8 +440,6 @@ int tt_zone_unreg(const char *arg) · 1 unmodified line | |||
| 449 | 440 | ||
| 450 | tt_zone_unregister_tz(tt_zone); | 441 | tt_zone_unregister_tz(tt_zone); |
| 451 | 442 | ||
| 452 | tt_put_tt_zone(tt_zone); | ||
| 453 | |||
| 454 | return 0; | 443 | return 0; |
| 455 | } | 444 | } |
| 456 | 445 | ||
| @@ -15,7 +15,6 @@ · 14 unmodified lines | |||
| 15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
| 16 | #include <linux/kdev_t.h> | 16 | #include <linux/kdev_t.h> |
| 17 | #include <linux/idr.h> | 17 | #include <linux/idr.h> |
| 18 | #include <linux/list_sort.h> | ||
| 19 | #include <linux/thermal.h> | 18 | #include <linux/thermal.h> |
| 20 | #include <linux/reboot.h> | 19 | #include <linux/reboot.h> |
| 21 | #include <linux/string.h> | 20 | #include <linux/string.h> |
| @@ -40,6 +39,8 @@ static DEFINE_MUTEX(thermal_governor_lock); · 18 unmodified lines | |||
| 40 | 39 | ||
| 41 | static struct thermal_governor *def_governor; | 40 | static struct thermal_governor *def_governor; |
| 42 | 41 | ||
| 42 | static bool thermal_pm_suspended; | ||
| 43 | |||
| 43 | /* | 44 | /* |
| 44 | * Governor section: set of functions to handle thermal governors | 45 | * Governor section: set of functions to handle thermal governors |
| 45 | * | 46 | * |
| @@ -122,7 +123,7 @@ int thermal_register_governor(struct thermal_governor *governor) · 76 unmodified lines | |||
| 122 | if (!governor) | 123 | if (!governor) |
| 123 | return -EINVAL; | 124 | return -EINVAL; |
| 124 | 125 | ||
| 125 | mutex_lock(&thermal_governor_lock); | 126 | guard(mutex)(&thermal_governor_lock); |
| 126 | 127 | ||
| 127 | err = -EBUSY; | 128 | err = -EBUSY; |
| 128 | if (!__find_governor(governor->name)) { | 129 | if (!__find_governor(governor->name)) { |
| @@ -138,7 +139,7 @@ int thermal_register_governor(struct thermal_governor *governor) · 9 unmodified lines | |||
| 138 | def_governor = governor; | 139 | def_governor = governor; |
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | mutex_lock(&thermal_list_lock); | 142 | guard(mutex)(&thermal_list_lock); |
| 142 | 143 | ||
| 143 | list_for_each_entry(pos, &thermal_tz_list, node) { | 144 | list_for_each_entry(pos, &thermal_tz_list, node) { |
| 144 | /* | 145 | /* |
| @@ -161,9 +162,6 @@ int thermal_register_governor(struct thermal_governor *governor) · 16 unmodified lines | |||
| 161 | } | 162 | } |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 164 | mutex_unlock(&thermal_list_lock); | ||
| 165 | mutex_unlock(&thermal_governor_lock); | ||
| 166 | |||
| 167 | return err; | 165 | return err; |
| 168 | } | 166 | } |
| 169 | 167 | ||
| @@ -174,23 +172,20 @@ void thermal_unregister_governor(struct thermal_governor *governor) · 4 unmodified lines | |||
| 174 | if (!governor) | 172 | if (!governor) |
| 175 | return; | 173 | return; |
| 176 | 174 | ||
| 177 | mutex_lock(&thermal_governor_lock); | 175 | guard(mutex)(&thermal_governor_lock); |
| 178 | 176 | ||
| 179 | if (!__find_governor(governor->name)) | 177 | if (!__find_governor(governor->name)) |
| 180 | goto exit; | 178 | return; |
| 181 | 179 | ||
| 182 | mutex_lock(&thermal_list_lock); | 180 | list_del(&governor->governor_list); |
| 181 | |||
| 182 | guard(mutex)(&thermal_list_lock); | ||
| 183 | 183 | ||
| 184 | list_for_each_entry(pos, &thermal_tz_list, node) { | 184 | list_for_each_entry(pos, &thermal_tz_list, node) { |
| 185 | if (!strncasecmp(pos->governor->name, governor->name, | 185 | if (!strncasecmp(pos->governor->name, governor->name, |
| 186 | THERMAL_NAME_LENGTH)) | 186 | THERMAL_NAME_LENGTH)) |
| 187 | thermal_set_governor(pos, NULL); | 187 | thermal_set_governor(pos, NULL); |
| 188 | } | 188 | } |
| 189 | |||
| 190 | mutex_unlock(&thermal_list_lock); | ||
| 191 | list_del(&governor->governor_list); | ||
| 192 | exit: | ||
| 193 | mutex_unlock(&thermal_governor_lock); | ||
| 194 | } | 189 | } |
| 195 | 190 | ||
| 196 | int thermal_zone_device_set_policy(struct thermal_zone_device *tz, | 191 | int thermal_zone_device_set_policy(struct thermal_zone_device *tz, |
| @@ -199,18 +194,12 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz, · 2 unmodified lines | |||
| 199 | struct thermal_governor *gov; | 194 | struct thermal_governor *gov; |
| 200 | int ret = -EINVAL; | 195 | int ret = -EINVAL; |
| 201 | 196 | ||
| 202 | mutex_lock(&thermal_governor_lock); | 197 | guard(mutex)(&thermal_governor_lock); |
| 203 | mutex_lock(&tz->lock); | 198 | guard(thermal_zone)(tz); |
| 204 | 199 | ||
| 205 | gov = __find_governor(strim(policy)); | 200 | gov = __find_governor(strim(policy)); |
| 206 | if (!gov) | 201 | if (gov) |
| 207 | goto exit; | 202 | ret = thermal_set_governor(tz, gov); |
| 208 | |||
| 209 | ret = thermal_set_governor(tz, gov); | ||
| 210 | |||
| 211 | exit: | ||
| 212 | mutex_unlock(&tz->lock); | ||
| 213 | mutex_unlock(&thermal_governor_lock); | ||
| 214 | 203 | ||
| 215 | thermal_notify_tz_gov_change(tz, policy); | 204 | thermal_notify_tz_gov_change(tz, policy); |
| 216 | 205 | ||
| @@ -222,15 +211,13 @@ int thermal_build_list_of_policies(char *buf) · 5 unmodified lines | |||
| 222 | struct thermal_governor *pos; | 211 | struct thermal_governor *pos; |
| 223 | ssize_t count = 0; | 212 | ssize_t count = 0; |
| 224 | 213 | ||
| 225 | mutex_lock(&thermal_governor_lock); | 214 | guard(mutex)(&thermal_governor_lock); |
| 226 | 215 | ||
| 227 | list_for_each_entry(pos, &thermal_governor_list, governor_list) { | 216 | list_for_each_entry(pos, &thermal_governor_list, governor_list) { |
| 228 | count += sysfs_emit_at(buf, count, "%s ", pos->name); | 217 | count += sysfs_emit_at(buf, count, "%s ", pos->name); |
| 229 | } | 218 | } |
| 230 | count += sysfs_emit_at(buf, count, "\n"); | 219 | count += sysfs_emit_at(buf, count, "\n"); |
| 231 | 220 | ||
| 232 | mutex_unlock(&thermal_governor_lock); | ||
| 233 | |||
| 234 | return count; | 221 | return count; |
| 235 | } | 222 | } |
| 236 | 223 | ||
| @@ -421,83 +408,46 @@ static void handle_critical_trips(struct thermal_zone_device *tz, · 184 unmodified lines | |||
| 421 | tz->ops.hot(tz); | 408 | tz->ops.hot(tz); |
| 422 | } | 409 | } |
| 423 | 410 | ||
| 424 | static void handle_thermal_trip(struct thermal_zone_device *tz, | 411 | static void move_trip_to_sorted_list(struct thermal_trip_desc *td, |
| 425 | struct thermal_trip_desc *td, | 412 | struct list_head *list) |
| 426 | struct list_head *way_up_list, | ||
| 427 | struct list_head *way_down_list) | ||
| 428 | { | 413 | { |
| 429 | const struct thermal_trip *trip = &td->trip; | 414 | struct thermal_trip_desc *entry; |
| 430 | int old_threshold; | ||
| 431 | |||
| 432 | if (trip->temperature == THERMAL_TEMP_INVALID) | ||
| 433 | return; | ||
| 434 | 415 | ||
| 435 | /* | 416 | /* |
| 436 | * If the trip temperature or hysteresis has been updated recently, | 417 | * Delete upfront and then add to make relocation within the same list |
| 437 | * the threshold needs to be computed again using the new values. | 418 | * work. |
| 438 | * However, its initial value still reflects the old ones and that | ||
| 439 | * is what needs to be compared with the previous zone temperature | ||
| 440 | * to decide which action to take. | ||
| 441 | */ | 419 | */ |
| 442 | old_threshold = td->threshold; | 420 | list_del(&td->list_node); |
| 443 | td->threshold = trip->temperature; | ||
| 444 | 421 | ||
| 445 | if (tz->last_temperature >= old_threshold && | 422 | /* Assume that the new entry is likely to be the last one. */ |
| 446 | tz->last_temperature != THERMAL_TEMP_INIT) { | 423 | list_for_each_entry_reverse(entry, list, list_node) { |
| 447 | /* | 424 | if (entry->threshold <= td->threshold) { |
| 448 | * Mitigation is under way, so it needs to stop if the zone | 425 | list_add(&td->list_node, &entry->list_node); |
| 449 | * temperature falls below the low temperature of the trip. | 426 | return; |
| 450 | * In that case, the trip temperature becomes the new threshold. | ||
| 451 | */ | ||
| 452 | if (tz->temperature < trip->temperature - trip->hysteresis) { | ||
| 453 | list_add(&td->notify_list_node, way_down_list); | ||
| 454 | td->notify_temp = trip->temperature - trip->hysteresis; | ||
| 455 | |||
| 456 | if (trip->type == THERMAL_TRIP_PASSIVE) { | ||
| 457 | tz->passive--; | ||
| 458 | WARN_ON(tz->passive < 0); | ||
| 459 | } | ||
| 460 | } else { | ||
| 461 | td->threshold -= trip->hysteresis; | ||
| 462 | } | 427 | } |
| 463 | } else if (tz->temperature >= trip->temperature) { | ||
| 464 | /* | ||
| 465 | * There is no mitigation under way, so it needs to be started | ||
| 466 | * if the zone temperature exceeds the trip one. The new | ||
| 467 | * threshold is then set to the low temperature of the trip. | ||
| 468 | */ | ||
| 469 | list_add_tail(&td->notify_list_node, way_up_list); | ||
| 470 | td->notify_temp = trip->temperature; | ||
| 471 | td->threshold -= trip->hysteresis; | ||
| 472 | |||
| 473 | if (trip->type == THERMAL_TRIP_PASSIVE) | ||
| 474 | tz->passive++; | ||
| 475 | else if (trip->type == THERMAL_TRIP_CRITICAL || | ||
| 476 | trip->type == THERMAL_TRIP_HOT) | ||
| 477 | handle_critical_trips(tz, trip); | ||
| 478 | } | 428 | } |
| 429 | list_add(&td->list_node, list); | ||
| 479 | } | 430 | } |
| 480 | 431 | ||
| 481 | static void thermal_zone_device_check(struct work_struct *work) | 432 | static void move_to_trips_high(struct thermal_zone_device *tz, |
| 433 | struct thermal_trip_desc *td) | ||
| 482 | { | 434 | { |
| 483 | struct thermal_zone_device *tz = container_of(work, struct | 435 | td->threshold = td->trip.temperature; |
| 484 | thermal_zone_device, | 436 | move_trip_to_sorted_list(td, &tz->trips_high); |
| 485 | poll_queue.work); | ||
| 486 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||
| 487 | } | 437 | } |
| 488 | 438 | ||
| 489 | static void thermal_zone_device_init(struct thermal_zone_device *tz) | 439 | static void move_to_trips_reached(struct thermal_zone_device *tz, |
| 440 | struct thermal_trip_desc *td) | ||
| 490 | { | 441 | { |
| 491 | struct thermal_instance *pos; | 442 | td->threshold = td->trip.temperature - td->trip.hysteresis; |
| 492 | 443 | move_trip_to_sorted_list(td, &tz->trips_reached); | |
| 493 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | 444 | } |
| 494 | 445 | ||
| 495 | tz->temperature = THERMAL_TEMP_INIT; | 446 | static void move_to_trips_invalid(struct thermal_zone_device *tz, |
| 496 | tz->passive = 0; | 447 | struct thermal_trip_desc *td) |
| 497 | tz->prev_low_trip = -INT_MAX; | 448 | { |
| 498 | tz->prev_high_trip = INT_MAX; | 449 | td->threshold = INT_MAX; |
| 499 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) | 450 | list_move(&td->list_node, &tz->trips_invalid); |
| 500 | pos->initialized = false; | ||
| 501 | } | 451 | } |
| 502 | 452 | ||
| 503 | static void thermal_governor_trip_crossed(struct thermal_governor *governor, | 453 | static void thermal_governor_trip_crossed(struct thermal_governor *governor, |
| @@ -513,41 +463,154 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, · 9 unmodified lines | |||
| 513 | } | 463 | } |
| 514 | 464 | ||
| 515 | static void thermal_trip_crossed(struct thermal_zone_device *tz, | 465 | static void thermal_trip_crossed(struct thermal_zone_device *tz, |
| 516 | const struct thermal_trip *trip, | 466 | struct thermal_trip_desc *td, |
| 517 | struct thermal_governor *governor, | 467 | struct thermal_governor *governor, |
| 518 | bool crossed_up) | 468 | bool crossed_up) |
| 519 | { | 469 | { |
| 470 | const struct thermal_trip *trip = &td->trip; | ||
| 471 | |||
| 520 | if (crossed_up) { | 472 | if (crossed_up) { |
| 473 | if (trip->type == THERMAL_TRIP_PASSIVE) | ||
| 474 | tz->passive++; | ||
| 475 | else if (trip->type == THERMAL_TRIP_CRITICAL || | ||
| 476 | trip->type == THERMAL_TRIP_HOT) | ||
| 477 | handle_critical_trips(tz, trip); | ||
| 478 | |||
| 521 | thermal_notify_tz_trip_up(tz, trip); | 479 | thermal_notify_tz_trip_up(tz, trip); |
| 522 | thermal_debug_tz_trip_up(tz, trip); | 480 | thermal_debug_tz_trip_up(tz, trip); |
| 523 | } else { | 481 | } else { |
| 482 | if (trip->type == THERMAL_TRIP_PASSIVE) { | ||
| 483 | tz->passive--; | ||
| 484 | WARN_ON(tz->passive < 0); | ||
| 485 | } | ||
| 524 | thermal_notify_tz_trip_down(tz, trip); | 486 | thermal_notify_tz_trip_down(tz, trip); |
| 525 | thermal_debug_tz_trip_down(tz, trip); | 487 | thermal_debug_tz_trip_down(tz, trip); |
| 526 | } | 488 | } |
| 527 | thermal_governor_trip_crossed(governor, tz, trip, crossed_up); | 489 | thermal_governor_trip_crossed(governor, tz, trip, crossed_up); |
| 528 | } | 490 | } |
| 529 | 491 | ||
| 530 | static int thermal_trip_notify_cmp(void *not_used, const struct list_head *a, | 492 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, |
| 531 | const struct list_head *b) | 493 | struct thermal_trip *trip, int hyst) |
| 532 | { | 494 | { |
| 533 | struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, | 495 | struct thermal_trip_desc *td = trip_to_trip_desc(trip); |
| 534 | notify_list_node); | 496 | |
| 535 | struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, | 497 | WRITE_ONCE(trip->hysteresis, hyst); |
| 536 | notify_list_node); | 498 | thermal_notify_tz_trip_change(tz, trip); |
| 537 | return tda->notify_temp - tdb->notify_temp; | 499 | /* |
| 500 | * If the zone temperature is above or at the trip tmperature, the trip | ||
| 501 | * is in the trips_reached list and its threshold is equal to its low | ||
| 502 | * temperature. It needs to stay in that list, but its threshold needs | ||
| 503 | * to be updated and the list ordering may need to be restored. | ||
| 504 | */ | ||
| 505 | if (tz->temperature >= td->threshold) | ||
| 506 | move_to_trips_reached(tz, td); | ||
| 507 | } | ||
| 508 | |||
| 509 | void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, | ||
| 510 | struct thermal_trip *trip, int temp) | ||
| 511 | { | ||
| 512 | struct thermal_trip_desc *td = trip_to_trip_desc(trip); | ||
| 513 | int old_temp = trip->temperature; | ||
| 514 | |||
| 515 | if (old_temp == temp) | ||
| 516 | return; | ||
| 517 | |||
| 518 | WRITE_ONCE(trip->temperature, temp); | ||
| 519 | thermal_notify_tz_trip_change(tz, trip); | ||
| 520 | |||
| 521 | if (old_temp == THERMAL_TEMP_INVALID) { | ||
| 522 | /* | ||
| 523 | * The trip was invalid before the change, so move it to the | ||
| 524 | * trips_high list regardless of the new temperature value | ||
| 525 | * because there is no mitigation under way for it. If a | ||
| 526 | * mitigation needs to be started, the trip will be moved to the | ||
| 527 | * trips_reached list later. | ||
| 528 | */ | ||
| 529 | move_to_trips_high(tz, td); | ||
| 530 | return; | ||
| 531 | } | ||
| 532 | |||
| 533 | if (temp == THERMAL_TEMP_INVALID) { | ||
| 534 | /* | ||
| 535 | * If the trip is in the trips_reached list, mitigation is under | ||
| 536 | * way for it and it needs to be stopped because the trip is | ||
| 537 | * effectively going away. | ||
| 538 | */ | ||
| 539 | if (tz->temperature >= td->threshold) | ||
| 540 | thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); | ||
| 541 | |||
| 542 | move_to_trips_invalid(tz, td); | ||
| 543 | return; | ||
| 544 | } | ||
| 545 | |||
| 546 | /* | ||
| 547 | * The trip stays on its current list, but its threshold needs to be | ||
| 548 | * updated due to the temperature change and the list ordering may need | ||
| 549 | * to be restored. | ||
| 550 | */ | ||
| 551 | if (tz->temperature >= td->threshold) | ||
| 552 | move_to_trips_reached(tz, td); | ||
| 553 | else | ||
| 554 | move_to_trips_high(tz, td); | ||
| 555 | } | ||
| 556 | EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); | ||
| 557 | |||
| 558 | static void thermal_zone_handle_trips(struct thermal_zone_device *tz, | ||
| 559 | struct thermal_governor *governor, | ||
| 560 | int *low, int *high) | ||
| 561 | { | ||
| 562 | struct thermal_trip_desc *td, *next; | ||
| 563 | LIST_HEAD(way_down_list); | ||
| 564 | |||
| 565 | /* Check the trips that were below or at the zone temperature. */ | ||
| 566 | list_for_each_entry_safe_reverse(td, next, &tz->trips_reached, list_node) { | ||
| 567 | if (td->threshold <= tz->temperature) | ||
| 568 | break; | ||
| 569 | |||
| 570 | thermal_trip_crossed(tz, td, governor, false); | ||
| 571 | /* | ||
| 572 | * The current trips_high list needs to be processed before | ||
| 573 | * adding new entries to it, so put them on a temporary list. | ||
| 574 | */ | ||
| 575 | list_move(&td->list_node, &way_down_list); | ||
| 576 | } | ||
| 577 | /* Check the trips that were previously above the zone temperature. */ | ||
| 578 | list_for_each_entry_safe(td, next, &tz->trips_high, list_node) { | ||
| 579 | if (td->threshold > tz->temperature) | ||
| 580 | break; | ||
| 581 | |||
| 582 | thermal_trip_crossed(tz, td, governor, true); | ||
| 583 | move_to_trips_reached(tz, td); | ||
| 584 | } | ||
| 585 | /* Move all of the trips from the temporary list to trips_high. */ | ||
| 586 | list_for_each_entry_safe(td, next, &way_down_list, list_node) | ||
| 587 | move_to_trips_high(tz, td); | ||
| 588 | |||
| 589 | if (!list_empty(&tz->trips_reached)) { | ||
| 590 | td = list_last_entry(&tz->trips_reached, | ||
| 591 | struct thermal_trip_desc, list_node); | ||
| 592 | /* | ||
| 593 | * Set the "low" value below the current trip threshold in case | ||
| 594 | * the zone temperature is at that threshold and stays there, | ||
| 595 | * which would trigger a new interrupt immediately in vain. | ||
| 596 | */ | ||
| 597 | *low = td->threshold - 1; | ||
| 598 | } | ||
| 599 | if (!list_empty(&tz->trips_high)) { | ||
| 600 | td = list_first_entry(&tz->trips_high, | ||
| 601 | struct thermal_trip_desc, list_node); | ||
| 602 | *high = td->threshold; | ||
| 603 | } | ||
| 538 | } | 604 | } |
| 539 | 605 | ||
| 540 | void __thermal_zone_device_update(struct thermal_zone_device *tz, | 606 | void __thermal_zone_device_update(struct thermal_zone_device *tz, |
| 541 | enum thermal_notify_event event) | 607 | enum thermal_notify_event event) |
| 542 | { | 608 | { |
| 543 | struct thermal_governor *governor = thermal_get_tz_governor(tz); | 609 | struct thermal_governor *governor = thermal_get_tz_governor(tz); |
| 544 | struct thermal_trip_desc *td; | ||
| 545 | LIST_HEAD(way_down_list); | ||
| 546 | LIST_HEAD(way_up_list); | ||
| 547 | int low = -INT_MAX, high = INT_MAX; | 610 | int low = -INT_MAX, high = INT_MAX; |
| 548 | int temp, ret; | 611 | int temp, ret; |
| 549 | 612 | ||
| 550 | if (tz->suspended || tz->mode != THERMAL_DEVICE_ENABLED) | 613 | if (tz->state != TZ_STATE_READY || tz->mode != THERMAL_DEVICE_ENABLED) |
| 551 | return; | 614 | return; |
| 552 | 615 | ||
| 553 | ret = __thermal_zone_get_temp(tz, &temp); | 616 | ret = __thermal_zone_get_temp(tz, &temp); |
| @@ -575,26 +638,12 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, · 21 unmodified lines | |||
| 575 | 638 | ||
| 576 | tz->notify_event = event; | 639 | tz->notify_event = event; |
| 577 | 640 | ||
| 578 | for_each_trip_desc(tz, td) { | 641 | thermal_zone_handle_trips(tz, governor, &low, &high); |
| 579 | handle_thermal_trip(tz, td, &way_up_list, &way_down_list); | ||
| 580 | 642 | ||
| 581 | if (td->threshold <= tz->temperature && td->threshold > low) | 643 | thermal_thresholds_handle(tz, &low, &high); |
| 582 | low = td->threshold; | ||
| 583 | |||
| 584 | if (td->threshold >= tz->temperature && td->threshold < high) | ||
| 585 | high = td->threshold; | ||
| 586 | } | ||
| 587 | 644 | ||
| 588 | thermal_zone_set_trips(tz, low, high); | 645 | thermal_zone_set_trips(tz, low, high); |
| 589 | 646 | ||
| 590 | list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); | ||
| 591 | list_for_each_entry(td, &way_up_list, notify_list_node) | ||
| 592 | thermal_trip_crossed(tz, &td->trip, governor, true); | ||
| 593 | |||
| 594 | list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); | ||
| 595 | list_for_each_entry_reverse(td, &way_down_list, notify_list_node) | ||
| 596 | thermal_trip_crossed(tz, &td->trip, governor, false); | ||
| 597 | |||
| 598 | if (governor->manage) | 647 | if (governor->manage) |
| 599 | governor->manage(tz); | 648 | governor->manage(tz); |
| 600 | 649 | ||
| @@ -609,26 +658,18 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, · 8 unmodified lines | |||
| 609 | { | 658 | { |
| 610 | int ret; | 659 | int ret; |
| 611 | 660 | ||
| 612 | mutex_lock(&tz->lock); | 661 | guard(thermal_zone)(tz); |
| 613 | 662 | ||
| 614 | /* do nothing if mode isn't changing */ | 663 | /* do nothing if mode isn't changing */ |
| 615 | if (mode == tz->mode) { | 664 | if (mode == tz->mode) |
| 616 | mutex_unlock(&tz->lock); | ||
| 617 | |||
| 618 | return 0; | 665 | return 0; |
| 619 | } | ||
| 620 | 666 | ||
| 621 | ret = __thermal_zone_device_set_mode(tz, mode); | 667 | ret = __thermal_zone_device_set_mode(tz, mode); |
| 622 | if (ret) { | 668 | if (ret) |
| 623 | mutex_unlock(&tz->lock); | ||
| 624 | |||
| 625 | return ret; | 669 | return ret; |
| 626 | } | ||
| 627 | 670 | ||
| 628 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | 671 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
| 629 | 672 | ||
| 630 | mutex_unlock(&tz->lock); | ||
| 631 | |||
| 632 | if (mode == THERMAL_DEVICE_ENABLED) | 673 | if (mode == THERMAL_DEVICE_ENABLED) |
| 633 | thermal_notify_tz_enable(tz); | 674 | thermal_notify_tz_enable(tz); |
| 634 | else | 675 | else |
| @@ -657,85 +698,81 @@ static bool thermal_zone_is_present(struct thermal_zone_device *tz) · 22 unmodified lines | |||
| 657 | void thermal_zone_device_update(struct thermal_zone_device *tz, | 698 | void thermal_zone_device_update(struct thermal_zone_device *tz, |
| 658 | enum thermal_notify_event event) | 699 | enum thermal_notify_event event) |
| 659 | { | 700 | { |
| 660 | mutex_lock(&tz->lock); | 701 | guard(thermal_zone)(tz); |
| 702 | |||
| 661 | if (thermal_zone_is_present(tz)) | 703 | if (thermal_zone_is_present(tz)) |
| 662 | __thermal_zone_device_update(tz, event); | 704 | __thermal_zone_device_update(tz, event); |
| 663 | mutex_unlock(&tz->lock); | ||
| 664 | } | 705 | } |
| 665 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); | 706 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); |
| 666 | 707 | ||
| 667 | void thermal_zone_trip_down(struct thermal_zone_device *tz, | ||
| 668 | const struct thermal_trip *trip) | ||
| 669 | { | ||
| 670 | thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); | ||
| 671 | } | ||
| 672 | |||
| 673 | int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), | 708 | int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), |
| 674 | void *data) | 709 | void *data) |
| 675 | { | 710 | { |
| 676 | struct thermal_governor *gov; | 711 | struct thermal_governor *gov; |
| 677 | int ret = 0; | ||
| 678 | 712 | ||
| 679 | mutex_lock(&thermal_governor_lock); | 713 | guard(mutex)(&thermal_governor_lock); |
| 714 | |||
| 680 | list_for_each_entry(gov, &thermal_governor_list, governor_list) { | 715 | list_for_each_entry(gov, &thermal_governor_list, governor_list) { |
| 716 | int ret; | ||
| 717 | |||
| 681 | ret = cb(gov, data); | 718 | ret = cb(gov, data); |
| 682 | if (ret) | 719 | if (ret) |
| 683 | break; | 720 | return ret; |
| 684 | } | 721 | } |
| 685 | mutex_unlock(&thermal_governor_lock); | ||
| 686 | 722 | ||
| 687 | return ret; | 723 | return 0; |
| 688 | } | 724 | } |
| 689 | 725 | ||
| 690 | int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, | 726 | int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, |
| 691 | void *), void *data) | 727 | void *), void *data) |
| 692 | { | 728 | { |
| 693 | struct thermal_cooling_device *cdev; | 729 | struct thermal_cooling_device *cdev; |
| 694 | int ret = 0; | ||
| 695 | 730 | ||
| 696 | mutex_lock(&thermal_list_lock); | 731 | guard(mutex)(&thermal_list_lock); |
| 732 | |||
| 697 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | 733 | list_for_each_entry(cdev, &thermal_cdev_list, node) { |
| 734 | int ret; | ||
| 735 | |||
| 698 | ret = cb(cdev, data); | 736 | ret = cb(cdev, data); |
| 699 | if (ret) | 737 | if (ret) |
| 700 | break; | 738 | return ret; |
| 701 | } | 739 | } |
| 702 | mutex_unlock(&thermal_list_lock); | ||
| 703 | 740 | ||
| 704 | return ret; | 741 | return 0; |
| 705 | } | 742 | } |
| 706 | 743 | ||
| 707 | int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), | 744 | int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), |
| 708 | void *data) | 745 | void *data) |
| 709 | { | 746 | { |
| 710 | struct thermal_zone_device *tz; | 747 | struct thermal_zone_device *tz; |
| 711 | int ret = 0; | ||
| 712 | 748 | ||
| 713 | mutex_lock(&thermal_list_lock); | 749 | guard(mutex)(&thermal_list_lock); |
| 750 | |||
| 714 | list_for_each_entry(tz, &thermal_tz_list, node) { | 751 | list_for_each_entry(tz, &thermal_tz_list, node) { |
| 752 | int ret; | ||
| 753 | |||
| 715 | ret = cb(tz, data); | 754 | ret = cb(tz, data); |
| 716 | if (ret) | 755 | if (ret) |
| 717 | break; | 756 | return ret; |
| 718 | } | 757 | } |
| 719 | mutex_unlock(&thermal_list_lock); | ||
| 720 | 758 | ||
| 721 | return ret; | 759 | return 0; |
| 722 | } | 760 | } |
| 723 | 761 | ||
| 724 | struct thermal_zone_device *thermal_zone_get_by_id(int id) | 762 | struct thermal_zone_device *thermal_zone_get_by_id(int id) |
| 725 | { | 763 | { |
| 726 | struct thermal_zone_device *tz, *match = NULL; | 764 | struct thermal_zone_device *tz; |
| 765 | |||
| 766 | guard(mutex)(&thermal_list_lock); | ||
| 727 | 767 | ||
| 728 | mutex_lock(&thermal_list_lock); | ||
| 729 | list_for_each_entry(tz, &thermal_tz_list, node) { | 768 | list_for_each_entry(tz, &thermal_tz_list, node) { |
| 730 | if (tz->id == id) { | 769 | if (tz->id == id) { |
| 731 | get_device(&tz->device); | 770 | get_device(&tz->device); |
| 732 | match = tz; | 771 | return tz; |
| 733 | break; | ||
| 734 | } | 772 | } |
| 735 | } | 773 | } |
| 736 | mutex_unlock(&thermal_list_lock); | ||
| 737 | 774 | ||
| 738 | return match; | 775 | return NULL; |
| 739 | } | 776 | } |
| 740 | 777 | ||
| 741 | /* | 778 | /* |
| @@ -748,12 +785,32 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) · 6 unmodified lines | |||
| 748 | * binding, and unbinding. | 785 | * binding, and unbinding. |
| 749 | */ | 786 | */ |
| 750 | 787 | ||
| 788 | static int thermal_instance_add(struct thermal_instance *new_instance, | ||
| 789 | struct thermal_cooling_device *cdev, | ||
| 790 | struct thermal_trip_desc *td) | ||
| 791 | { | ||
| 792 | struct thermal_instance *instance; | ||
| 793 | |||
| 794 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { | ||
| 795 | if (instance->cdev == cdev) | ||
| 796 | return -EEXIST; | ||
| 797 | } | ||
| 798 | |||
| 799 | list_add_tail(&new_instance->trip_node, &td->thermal_instances); | ||
| 800 | |||
| 801 | guard(cooling_dev)(cdev); | ||
| 802 | |||
| 803 | list_add_tail(&new_instance->cdev_node, &cdev->thermal_instances); | ||
| 804 | |||
| 805 | return 0; | ||
| 806 | } | ||
| 807 | |||
| 751 | /** | 808 | /** |
| 752 | * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone | 809 | * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone |
| 753 | * @tz: pointer to struct thermal_zone_device | 810 | * @tz: pointer to struct thermal_zone_device |
| 754 | * @trip: trip point the cooling devices is associated with in this zone. | 811 | * @td: descriptor of the trip point to bind @cdev to |
| 755 | * @cdev: pointer to struct thermal_cooling_device | 812 | * @cdev: pointer to struct thermal_cooling_device |
| 756 | * @cool_spec: cooling specification for @trip and @cdev | 813 | * @cool_spec: cooling specification for the trip point and @cdev |
| 757 | * | 814 | * |
| 758 | * This interface function bind a thermal cooling device to the certain trip | 815 | * This interface function bind a thermal cooling device to the certain trip |
| 759 | * point of a thermal zone device. | 816 | * point of a thermal zone device. |
| @@ -762,12 +819,11 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) · 2 unmodified lines | |||
| 762 | * Return: 0 on success, the proper error value otherwise. | 819 | * Return: 0 on success, the proper error value otherwise. |
| 763 | */ | 820 | */ |
| 764 | static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, | 821 | static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, |
| 765 | const struct thermal_trip *trip, | 822 | struct thermal_trip_desc *td, |
| 766 | struct thermal_cooling_device *cdev, | 823 | struct thermal_cooling_device *cdev, |
| 767 | struct cooling_spec *cool_spec) | 824 | struct cooling_spec *cool_spec) |
| 768 | { | 825 | { |
| 769 | struct thermal_instance *dev; | 826 | struct thermal_instance *dev; |
| 770 | struct thermal_instance *pos; | ||
| 771 | bool upper_no_limit; | 827 | bool upper_no_limit; |
| 772 | int result; | 828 | int result; |
| 773 | 829 | ||
| @@ -790,7 +846,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, · 16 unmodified lines | |||
| 790 | return -ENOMEM; | 846 | return -ENOMEM; |
| 791 | 847 | ||
| 792 | dev->cdev = cdev; | 848 | dev->cdev = cdev; |
| 793 | dev->trip = trip; | 849 | dev->trip = &td->trip; |
| 794 | dev->upper = cool_spec->upper; | 850 | dev->upper = cool_spec->upper; |
| 795 | dev->upper_no_limit = upper_no_limit; | 851 | dev->upper_no_limit = upper_no_limit; |
| 796 | dev->lower = cool_spec->lower; | 852 | dev->lower = cool_spec->lower; |
| @@ -829,24 +885,15 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, · 32 unmodified lines | |||
| 829 | if (result) | 885 | if (result) |
| 830 | goto remove_trip_file; | 886 | goto remove_trip_file; |
| 831 | 887 | ||
| 832 | mutex_lock(&cdev->lock); | 888 | result = thermal_instance_add(dev, cdev, td); |
| 833 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) | 889 | if (result) |
| 834 | if (pos->trip == trip && pos->cdev == cdev) { | 890 | goto remove_weight_file; |
| 835 | result = -EEXIST; | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | if (!result) { | ||
| 839 | list_add_tail(&dev->tz_node, &tz->thermal_instances); | ||
| 840 | list_add_tail(&dev->cdev_node, &cdev->thermal_instances); | ||
| 841 | atomic_set(&tz->need_update, 1); | ||
| 842 | 891 | ||
| 843 | thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); | 892 | thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); |
| 844 | } | ||
| 845 | mutex_unlock(&cdev->lock); | ||
| 846 | 893 | ||
| 847 | if (!result) | 894 | return 0; |
| 848 | return 0; | ||
| 849 | 895 | ||
| 896 | remove_weight_file: | ||
| 850 | device_remove_file(&tz->device, &dev->weight_attr); | 897 | device_remove_file(&tz->device, &dev->weight_attr); |
| 851 | remove_trip_file: | 898 | remove_trip_file: |
| 852 | device_remove_file(&tz->device, &dev->attr); | 899 | device_remove_file(&tz->device, &dev->attr); |
| @@ -859,10 +906,19 @@ free_mem: · 6 unmodified lines | |||
| 859 | return result; | 906 | return result; |
| 860 | } | 907 | } |
| 861 | 908 | ||
| 909 | static void thermal_instance_delete(struct thermal_instance *instance) | ||
| 910 | { | ||
| 911 | list_del(&instance->trip_node); | ||
| 912 | |||
| 913 | guard(cooling_dev)(instance->cdev); | ||
| 914 | |||
| 915 | list_del(&instance->cdev_node); | ||
| 916 | } | ||
| 917 | |||
| 862 | /** | 918 | /** |
| 863 | * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. | 919 | * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. |
| 864 | * @tz: pointer to a struct thermal_zone_device. | 920 | * @tz: pointer to a struct thermal_zone_device. |
| 865 | * @trip: trip point the cooling devices is associated with in this zone. | 921 | * @td: descriptor of the trip point to unbind @cdev from |
| 866 | * @cdev: pointer to a struct thermal_cooling_device. | 922 | * @cdev: pointer to a struct thermal_cooling_device. |
| 867 | * | 923 | * |
| 868 | * This interface function unbind a thermal cooling device from the certain | 924 | * This interface function unbind a thermal cooling device from the certain |
| @@ -870,28 +926,23 @@ free_mem: · 1 unmodified line | |||
| 870 | * This function is usually called in the thermal zone device .unbind callback. | 926 | * This function is usually called in the thermal zone device .unbind callback. |
| 871 | */ | 927 | */ |
| 872 | static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, | 928 | static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, |
| 873 | const struct thermal_trip *trip, | 929 | struct thermal_trip_desc *td, |
| 874 | struct thermal_cooling_device *cdev) | 930 | struct thermal_cooling_device *cdev) |
| 875 | { | 931 | { |
| 876 | struct thermal_instance *pos, *next; | 932 | struct thermal_instance *pos, *next; |
| 877 | 933 | ||
| 878 | mutex_lock(&cdev->lock); | 934 | list_for_each_entry_safe(pos, next, &td->thermal_instances, trip_node) { |
| 879 | list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { | 935 | if (pos->cdev == cdev) { |
| 880 | if (pos->trip == trip && pos->cdev == cdev) { | 936 | thermal_instance_delete(pos); |
| 881 | list_del(&pos->tz_node); | ||
| 882 | list_del(&pos->cdev_node); | ||
| 883 | |||
| 884 | thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); | ||
| 885 | |||
| 886 | mutex_unlock(&cdev->lock); | ||
| 887 | goto unbind; | 937 | goto unbind; |
| 888 | } | 938 | } |
| 889 | } | 939 | } |
| 890 | mutex_unlock(&cdev->lock); | ||
| 891 | 940 | ||
| 892 | return; | 941 | return; |
| 893 | 942 | ||
| 894 | unbind: | 943 | unbind: |
| 944 | thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); | ||
| 945 | |||
| 895 | device_remove_file(&tz->device, &pos->weight_attr); | 946 | device_remove_file(&tz->device, &pos->weight_attr); |
| 896 | device_remove_file(&tz->device, &pos->attr); | 947 | device_remove_file(&tz->device, &pos->attr); |
| 897 | sysfs_remove_link(&tz->device.kobj, pos->name); | 948 | sysfs_remove_link(&tz->device.kobj, pos->name); |
| @@ -924,25 +975,23 @@ static struct class *thermal_class; · 26 unmodified lines | |||
| 924 | 975 | ||
| 925 | static inline | 976 | static inline |
| 926 | void print_bind_err_msg(struct thermal_zone_device *tz, | 977 | void print_bind_err_msg(struct thermal_zone_device *tz, |
| 927 | const struct thermal_trip *trip, | 978 | const struct thermal_trip_desc *td, |
| 928 | struct thermal_cooling_device *cdev, int ret) | 979 | struct thermal_cooling_device *cdev, int ret) |
| 929 | { | 980 | { |
| 930 | dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", | 981 | dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", |
| 931 | cdev->type, thermal_zone_trip_id(tz, trip), ret); | 982 | cdev->type, thermal_zone_trip_id(tz, &td->trip), ret); |
| 932 | } | 983 | } |
| 933 | 984 | ||
| 934 | static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, | 985 | static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, |
| 935 | struct thermal_cooling_device *cdev) | 986 | struct thermal_cooling_device *cdev) |
| 936 | { | 987 | { |
| 937 | struct thermal_trip_desc *td; | 988 | struct thermal_trip_desc *td; |
| 989 | bool update_tz = false; | ||
| 938 | 990 | ||
| 939 | if (!tz->ops.should_bind) | 991 | if (!tz->ops.should_bind) |
| 940 | return; | 992 | return false; |
| 941 | |||
| 942 | mutex_lock(&tz->lock); | ||
| 943 | 993 | ||
| 944 | for_each_trip_desc(tz, td) { | 994 | for_each_trip_desc(tz, td) { |
| 945 | struct thermal_trip *trip = &td->trip; | ||
| 946 | struct cooling_spec c = { | 995 | struct cooling_spec c = { |
| 947 | .upper = THERMAL_NO_LIMIT, | 996 | .upper = THERMAL_NO_LIMIT, |
| 948 | .lower = THERMAL_NO_LIMIT, | 997 | .lower = THERMAL_NO_LIMIT, |
| @@ -950,15 +999,40 @@ static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, · 1 unmodified line | |||
| 950 | }; | 999 | }; |
| 951 | int ret; | 1000 | int ret; |
| 952 | 1001 | ||
| 953 | if (!tz->ops.should_bind(tz, trip, cdev, &c)) | 1002 | if (!tz->ops.should_bind(tz, &td->trip, cdev, &c)) |
| 954 | continue; | 1003 | continue; |
| 955 | 1004 | ||
| 956 | ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); | 1005 | ret = thermal_bind_cdev_to_trip(tz, td, cdev, &c); |
| 957 | if (ret) | 1006 | if (ret) { |
| 958 | print_bind_err_msg(tz, trip, cdev, ret); | 1007 | print_bind_err_msg(tz, td, cdev, ret); |
| 1008 | continue; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | update_tz = true; | ||
| 959 | } | 1012 | } |
| 960 | 1013 | ||
| 961 | mutex_unlock(&tz->lock); | 1014 | return update_tz; |
| 1015 | } | ||
| 1016 | |||
| 1017 | static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, | ||
| 1018 | struct thermal_cooling_device *cdev) | ||
| 1019 | { | ||
| 1020 | guard(thermal_zone)(tz); | ||
| 1021 | |||
| 1022 | if (__thermal_zone_cdev_bind(tz, cdev)) | ||
| 1023 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | static void thermal_cooling_device_init_complete(struct thermal_cooling_device *cdev) | ||
| 1027 | { | ||
| 1028 | struct thermal_zone_device *tz; | ||
| 1029 | |||
| 1030 | guard(mutex)(&thermal_list_lock); | ||
| 1031 | |||
| 1032 | list_add(&cdev->node, &thermal_cdev_list); | ||
| 1033 | |||
| 1034 | list_for_each_entry(tz, &thermal_tz_list, node) | ||
| 1035 | thermal_zone_cdev_bind(tz, cdev); | ||
| 962 | } | 1036 | } |
| 963 | 1037 | ||
| 964 | /** | 1038 | /** |
| @@ -983,7 +1057,6 @@ __thermal_cooling_device_register(struct device_node *np, · 18 unmodified lines | |||
| 983 | const struct thermal_cooling_device_ops *ops) | 1057 | const struct thermal_cooling_device_ops *ops) |
| 984 | { | 1058 | { |
| 985 | struct thermal_cooling_device *cdev; | 1059 | struct thermal_cooling_device *cdev; |
| 986 | struct thermal_zone_device *pos = NULL; | ||
| 987 | unsigned long current_state; | 1060 | unsigned long current_state; |
| 988 | int id, ret; | 1061 | int id, ret; |
| 989 | 1062 | ||
| @@ -1050,21 +1123,7 @@ __thermal_cooling_device_register(struct device_node *np, · 60 unmodified lines | |||
| 1050 | if (current_state <= cdev->max_state) | 1123 | if (current_state <= cdev->max_state) |
| 1051 | thermal_debug_cdev_add(cdev, current_state); | 1124 | thermal_debug_cdev_add(cdev, current_state); |
| 1052 | 1125 | ||
| 1053 | /* Add 'this' new cdev to the global cdev list */ | 1126 | thermal_cooling_device_init_complete(cdev); |
| 1054 | mutex_lock(&thermal_list_lock); | ||
| 1055 | |||
| 1056 | list_add(&cdev->node, &thermal_cdev_list); | ||
| 1057 | |||
| 1058 | /* Update binding information for 'this' new cdev */ | ||
| 1059 | list_for_each_entry(pos, &thermal_tz_list, node) | ||
| 1060 | thermal_zone_cdev_bind(pos, cdev); | ||
| 1061 | |||
| 1062 | list_for_each_entry(pos, &thermal_tz_list, node) | ||
| 1063 | if (atomic_cmpxchg(&pos->need_update, 1, 0)) | ||
| 1064 | thermal_zone_device_update(pos, | ||
| 1065 | THERMAL_EVENT_UNSPECIFIED); | ||
| 1066 | |||
| 1067 | mutex_unlock(&thermal_list_lock); | ||
| 1068 | 1127 | ||
| 1069 | return cdev; | 1128 | return cdev; |
| 1070 | 1129 | ||
| @@ -1207,19 +1266,19 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) · 136 unmodified lines | |||
| 1207 | * Hold thermal_list_lock throughout the update to prevent the device | 1266 | * Hold thermal_list_lock throughout the update to prevent the device |
| 1208 | * from going away while being updated. | 1267 | * from going away while being updated. |
| 1209 | */ | 1268 | */ |
| 1210 | mutex_lock(&thermal_list_lock); | 1269 | guard(mutex)(&thermal_list_lock); |
| 1211 | 1270 | ||
| 1212 | if (!thermal_cooling_device_present(cdev)) | 1271 | if (!thermal_cooling_device_present(cdev)) |
| 1213 | goto unlock_list; | 1272 | return; |
| 1214 | 1273 | ||
| 1215 | /* | 1274 | /* |
| 1216 | * Update under the cdev lock to prevent the state from being set beyond | 1275 | * Update under the cdev lock to prevent the state from being set beyond |
| 1217 | * the new limit concurrently. | 1276 | * the new limit concurrently. |
| 1218 | */ | 1277 | */ |
| 1219 | mutex_lock(&cdev->lock); | 1278 | guard(cooling_dev)(cdev); |
| 1220 | 1279 | ||
| 1221 | if (cdev->ops->get_max_state(cdev, &cdev->max_state)) | 1280 | if (cdev->ops->get_max_state(cdev, &cdev->max_state)) |
| 1222 | goto unlock; | 1281 | return; |
| 1223 | 1282 | ||
| 1224 | thermal_cooling_device_stats_reinit(cdev); | 1283 | thermal_cooling_device_stats_reinit(cdev); |
| 1225 | 1284 | ||
| @@ -1246,63 +1305,59 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) · 20 unmodified lines | |||
| 1246 | } | 1305 | } |
| 1247 | 1306 | ||
| 1248 | if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) | 1307 | if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) |
| 1249 | goto unlock; | 1308 | return; |
| 1250 | 1309 | ||
| 1251 | thermal_cooling_device_stats_update(cdev, state); | 1310 | thermal_cooling_device_stats_update(cdev, state); |
| 1252 | |||
| 1253 | unlock: | ||
| 1254 | mutex_unlock(&cdev->lock); | ||
| 1255 | |||
| 1256 | unlock_list: | ||
| 1257 | mutex_unlock(&thermal_list_lock); | ||
| 1258 | } | 1311 | } |
| 1259 | EXPORT_SYMBOL_GPL(thermal_cooling_device_update); | 1312 | EXPORT_SYMBOL_GPL(thermal_cooling_device_update); |
| 1260 | 1313 | ||
| 1261 | static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | 1314 | static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, |
| 1262 | struct thermal_cooling_device *cdev) | 1315 | struct thermal_cooling_device *cdev) |
| 1263 | { | 1316 | { |
| 1264 | struct thermal_trip_desc *td; | 1317 | struct thermal_trip_desc *td; |
| 1265 | 1318 | ||
| 1266 | mutex_lock(&tz->lock); | ||
| 1267 | |||
| 1268 | for_each_trip_desc(tz, td) | 1319 | for_each_trip_desc(tz, td) |
| 1269 | thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); | 1320 | thermal_unbind_cdev_from_trip(tz, td, cdev); |
| 1270 | |||
| 1271 | mutex_unlock(&tz->lock); | ||
| 1272 | } | 1321 | } |
| 1273 | 1322 | ||
| 1274 | /** | 1323 | static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, |
| 1275 | * thermal_cooling_device_unregister - removes a thermal cooling device | 1324 | struct thermal_cooling_device *cdev) |
| 1276 | * @cdev: the thermal cooling device to remove. | ||
| 1277 | * | ||
| 1278 | * thermal_cooling_device_unregister() must be called when a registered | ||
| 1279 | * thermal cooling device is no longer needed. | ||
| 1280 | */ | ||
| 1281 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | ||
| 1282 | { | 1325 | { |
| 1283 | struct thermal_zone_device *tz; | 1326 | guard(thermal_zone)(tz); |
| 1284 | 1327 | ||
| 1285 | if (!cdev) | 1328 | __thermal_zone_cdev_unbind(tz, cdev); |
| 1286 | return; | 1329 | } |
| 1287 | 1330 | ||
| 1288 | thermal_debug_cdev_remove(cdev); | 1331 | static bool thermal_cooling_device_exit(struct thermal_cooling_device *cdev) |
| 1332 | { | ||
| 1333 | struct thermal_zone_device *tz; | ||
| 1289 | 1334 | ||
| 1290 | mutex_lock(&thermal_list_lock); | 1335 | guard(mutex)(&thermal_list_lock); |
| 1291 | 1336 | ||
| 1292 | if (!thermal_cooling_device_present(cdev)) { | 1337 | if (!thermal_cooling_device_present(cdev)) |
| 1293 | mutex_unlock(&thermal_list_lock); | 1338 | return false; |
| 1294 | return; | ||
| 1295 | } | ||
| 1296 | 1339 | ||
| 1297 | list_del(&cdev->node); | 1340 | list_del(&cdev->node); |
| 1298 | 1341 | ||
| 1299 | /* Unbind all thermal zones associated with 'this' cdev */ | ||
| 1300 | list_for_each_entry(tz, &thermal_tz_list, node) | 1342 | list_for_each_entry(tz, &thermal_tz_list, node) |
| 1301 | thermal_zone_cdev_unbind(tz, cdev); | 1343 | thermal_zone_cdev_unbind(tz, cdev); |
| 1302 | 1344 | ||
| 1303 | mutex_unlock(&thermal_list_lock); | 1345 | return true; |
| 1346 | } | ||
| 1304 | 1347 | ||
| 1305 | device_unregister(&cdev->device); | 1348 | /** |
| 1349 | * thermal_cooling_device_unregister() - removes a thermal cooling device | ||
| 1350 | * @cdev: Thermal cooling device to remove. | ||
| 1351 | */ | ||
| 1352 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | ||
| 1353 | { | ||
| 1354 | if (!cdev) | ||
| 1355 | return; | ||
| 1356 | |||
| 1357 | thermal_debug_cdev_remove(cdev); | ||
| 1358 | |||
| 1359 | if (thermal_cooling_device_exit(cdev)) | ||
| 1360 | device_unregister(&cdev->device); | ||
| 1306 | } | 1361 | } |
| 1307 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); | 1362 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); |
| 1308 | 1363 | ||
| @@ -1314,7 +1369,7 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) · 5 unmodified lines | |||
| 1314 | if (tz->ops.get_crit_temp) | 1369 | if (tz->ops.get_crit_temp) |
| 1315 | return tz->ops.get_crit_temp(tz, temp); | 1370 | return tz->ops.get_crit_temp(tz, temp); |
| 1316 | 1371 | ||
| 1317 | mutex_lock(&tz->lock); | 1372 | guard(thermal_zone)(tz); |
| 1318 | 1373 | ||
| 1319 | for_each_trip_desc(tz, td) { | 1374 | for_each_trip_desc(tz, td) { |
| 1320 | const struct thermal_trip *trip = &td->trip; | 1375 | const struct thermal_trip *trip = &td->trip; |
| @@ -1326,12 +1381,91 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) · 5 unmodified lines | |||
| 1326 | } | 1381 | } |
| 1327 | } | 1382 | } |
| 1328 | 1383 | ||
| 1329 | mutex_unlock(&tz->lock); | ||
| 1330 | |||
| 1331 | return ret; | 1384 | return ret; |
| 1332 | } | 1385 | } |
| 1333 | EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); | 1386 | EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); |
| 1334 | 1387 | ||
| 1388 | static void thermal_zone_device_check(struct work_struct *work) | ||
| 1389 | { | ||
| 1390 | struct thermal_zone_device *tz = container_of(work, struct | ||
| 1391 | thermal_zone_device, | ||
| 1392 | poll_queue.work); | ||
| 1393 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | static void thermal_zone_device_init(struct thermal_zone_device *tz) | ||
| 1397 | { | ||
| 1398 | struct thermal_trip_desc *td, *next; | ||
| 1399 | |||
| 1400 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | ||
| 1401 | |||
| 1402 | tz->temperature = THERMAL_TEMP_INIT; | ||
| 1403 | tz->passive = 0; | ||
| 1404 | tz->prev_low_trip = -INT_MAX; | ||
| 1405 | tz->prev_high_trip = INT_MAX; | ||
| 1406 | for_each_trip_desc(tz, td) { | ||
| 1407 | struct thermal_instance *instance; | ||
| 1408 | |||
| 1409 | list_for_each_entry(instance, &td->thermal_instances, trip_node) | ||
| 1410 | instance->initialized = false; | ||
| 1411 | } | ||
| 1412 | /* | ||
| 1413 | * At this point, all valid trips need to be moved to trips_high so that | ||
| 1414 | * mitigation can be started if the zone temperature is above them. | ||
| 1415 | */ | ||
| 1416 | list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { | ||
| 1417 | if (td->trip.temperature != THERMAL_TEMP_INVALID) | ||
| 1418 | move_to_trips_high(tz, td); | ||
| 1419 | } | ||
| 1420 | /* The trips_reached list may not be empty during system resume. */ | ||
| 1421 | list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { | ||
| 1422 | if (td->trip.temperature == THERMAL_TEMP_INVALID) | ||
| 1423 | move_to_trips_invalid(tz, td); | ||
| 1424 | else | ||
| 1425 | move_to_trips_high(tz, td); | ||
| 1426 | } | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | static int thermal_zone_init_governor(struct thermal_zone_device *tz) | ||
| 1430 | { | ||
| 1431 | struct thermal_governor *governor; | ||
| 1432 | |||
| 1433 | guard(mutex)(&thermal_governor_lock); | ||
| 1434 | |||
| 1435 | if (tz->tzp) | ||
| 1436 | governor = __find_governor(tz->tzp->governor_name); | ||
| 1437 | else | ||
| 1438 | governor = def_governor; | ||
| 1439 | |||
| 1440 | return thermal_set_governor(tz, governor); | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | static void thermal_zone_init_complete(struct thermal_zone_device *tz) | ||
| 1444 | { | ||
| 1445 | struct thermal_cooling_device *cdev; | ||
| 1446 | |||
| 1447 | guard(mutex)(&thermal_list_lock); | ||
| 1448 | |||
| 1449 | list_add_tail(&tz->node, &thermal_tz_list); | ||
| 1450 | |||
| 1451 | guard(thermal_zone)(tz); | ||
| 1452 | |||
| 1453 | /* Bind cooling devices for this zone. */ | ||
| 1454 | list_for_each_entry(cdev, &thermal_cdev_list, node) | ||
| 1455 | __thermal_zone_cdev_bind(tz, cdev); | ||
| 1456 | |||
| 1457 | tz->state &= ~TZ_STATE_FLAG_INIT; | ||
| 1458 | /* | ||
| 1459 | * If system suspend or resume is in progress at this point, the | ||
| 1460 | * new thermal zone needs to be marked as suspended because | ||
| 1461 | * thermal_pm_notify() has run already. | ||
| 1462 | */ | ||
| 1463 | if (thermal_pm_suspended) | ||
| 1464 | tz->state |= TZ_STATE_FLAG_SUSPENDED; | ||
| 1465 | |||
| 1466 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||
| 1467 | } | ||
| 1468 | |||
| 1335 | /** | 1469 | /** |
| 1336 | * thermal_zone_device_register_with_trips() - register a new thermal zone device | 1470 | * thermal_zone_device_register_with_trips() - register a new thermal zone device |
| 1337 | * @type: the thermal zone device type | 1471 | * @type: the thermal zone device type |
| @@ -1366,12 +1500,10 @@ thermal_zone_device_register_with_trips(const char *type, · 28 unmodified lines | |||
| 1366 | unsigned int polling_delay) | 1500 | unsigned int polling_delay) |
| 1367 | { | 1501 | { |
| 1368 | const struct thermal_trip *trip = trips; | 1502 | const struct thermal_trip *trip = trips; |
| 1369 | struct thermal_cooling_device *cdev; | ||
| 1370 | struct thermal_zone_device *tz; | 1503 | struct thermal_zone_device *tz; |
| 1371 | struct thermal_trip_desc *td; | 1504 | struct thermal_trip_desc *td; |
| 1372 | int id; | 1505 | int id; |
| 1373 | int result; | 1506 | int result; |
| 1374 | struct thermal_governor *governor; | ||
| 1375 | 1507 | ||
| 1376 | if (!type || strlen(type) == 0) { | 1508 | if (!type || strlen(type) == 0) { |
| 1377 | pr_err("No thermal zone type defined\n"); | 1509 | pr_err("No thermal zone type defined\n"); |
| @@ -1415,8 +1547,10 @@ thermal_zone_device_register_with_trips(const char *type, · 37 unmodified lines | |||
| 1415 | } | 1547 | } |
| 1416 | } | 1548 | } |
| 1417 | 1549 | ||
| 1418 | INIT_LIST_HEAD(&tz->thermal_instances); | ||
| 1419 | INIT_LIST_HEAD(&tz->node); | 1550 | INIT_LIST_HEAD(&tz->node); |
| 1551 | INIT_LIST_HEAD(&tz->trips_high); | ||
| 1552 | INIT_LIST_HEAD(&tz->trips_reached); | ||
| 1553 | INIT_LIST_HEAD(&tz->trips_invalid); | ||
| 1420 | ida_init(&tz->ida); | 1554 | ida_init(&tz->ida); |
| 1421 | mutex_init(&tz->lock); | 1555 | mutex_init(&tz->lock); |
| 1422 | init_completion(&tz->removal); | 1556 | init_completion(&tz->removal); |
| @@ -1439,51 +1573,41 @@ thermal_zone_device_register_with_trips(const char *type, · 16 unmodified lines | |||
| 1439 | tz->num_trips = num_trips; | 1573 | tz->num_trips = num_trips; |
| 1440 | for_each_trip_desc(tz, td) { | 1574 | for_each_trip_desc(tz, td) { |
| 1441 | td->trip = *trip++; | 1575 | td->trip = *trip++; |
| 1576 | INIT_LIST_HEAD(&td->thermal_instances); | ||
| 1577 | INIT_LIST_HEAD(&td->list_node); | ||
| 1442 | /* | 1578 | /* |
| 1443 | * Mark all thresholds as invalid to start with even though | 1579 | * Mark all thresholds as invalid to start with even though |
| 1444 | * this only matters for the trips that start as invalid and | 1580 | * this only matters for the trips that start as invalid and |
| 1445 | * become valid later. | 1581 | * become valid later. |
| 1446 | */ | 1582 | */ |
| 1447 | td->threshold = INT_MAX; | 1583 | move_to_trips_invalid(tz, td); |
| 1448 | } | 1584 | } |
| 1449 | 1585 | ||
| 1450 | tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); | 1586 | tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); |
| 1451 | tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); | 1587 | tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); |
| 1452 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; | 1588 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; |
| 1453 | 1589 | ||
| 1590 | tz->state = TZ_STATE_FLAG_INIT; | ||
| 1591 | |||
| 1454 | /* sys I/F */ | 1592 | /* sys I/F */ |
| 1455 | /* Add nodes that are always present via .groups */ | 1593 | /* Add nodes that are always present via .groups */ |
| 1456 | result = thermal_zone_create_device_groups(tz); | 1594 | result = thermal_zone_create_device_groups(tz); |
| 1457 | if (result) | 1595 | if (result) |
| 1458 | goto remove_id; | 1596 | goto remove_id; |
| 1459 | 1597 | ||
| 1460 | /* A new thermal zone needs to be updated anyway. */ | ||
| 1461 | atomic_set(&tz->need_update, 1); | ||
| 1462 | |||
| 1463 | result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); | 1598 | result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); |
| 1464 | if (result) { | 1599 | if (result) { |
| 1465 | thermal_zone_destroy_device_groups(tz); | 1600 | thermal_zone_destroy_device_groups(tz); |
| 1466 | goto remove_id; | 1601 | goto remove_id; |
| 1467 | } | 1602 | } |
| 1603 | thermal_zone_device_init(tz); | ||
| 1468 | result = device_register(&tz->device); | 1604 | result = device_register(&tz->device); |
| 1469 | if (result) | 1605 | if (result) |
| 1470 | goto release_device; | 1606 | goto release_device; |
| 1471 | 1607 | ||
| 1472 | /* Update 'this' zone's governor information */ | 1608 | result = thermal_zone_init_governor(tz); |
| 1473 | mutex_lock(&thermal_governor_lock); | 1609 | if (result) |
| 1474 | |||
| 1475 | if (tz->tzp) | ||
| 1476 | governor = __find_governor(tz->tzp->governor_name); | ||
| 1477 | else | ||
| 1478 | governor = def_governor; | ||
| 1479 | |||
| 1480 | result = thermal_set_governor(tz, governor); | ||
| 1481 | if (result) { | ||
| 1482 | mutex_unlock(&thermal_governor_lock); | ||
| 1483 | goto unregister; | 1610 | goto unregister; |
| 1484 | } | ||
| 1485 | |||
| 1486 | mutex_unlock(&thermal_governor_lock); | ||
| 1487 | 1611 | ||
| 1488 | if (!tz->tzp || !tz->tzp->no_hwmon) { | 1612 | if (!tz->tzp || !tz->tzp->no_hwmon) { |
| 1489 | result = thermal_add_hwmon_sysfs(tz); | 1613 | result = thermal_add_hwmon_sysfs(tz); |
| @@ -1491,22 +1615,11 @@ thermal_zone_device_register_with_trips(const char *type, · 1 unmodified line | |||
| 1491 | goto unregister; | 1615 | goto unregister; |
| 1492 | } | 1616 | } |
| 1493 | 1617 | ||
| 1494 | mutex_lock(&thermal_list_lock); | 1618 | result = thermal_thresholds_init(tz); |
| 1495 | 1619 | if (result) | |
| 1496 | mutex_lock(&tz->lock); | 1620 | goto remove_hwmon; |
| 1497 | list_add_tail(&tz->node, &thermal_tz_list); | ||
| 1498 | mutex_unlock(&tz->lock); | ||
| 1499 | |||
| 1500 | /* Bind cooling devices for this zone */ | ||
| 1501 | list_for_each_entry(cdev, &thermal_cdev_list, node) | ||
| 1502 | thermal_zone_cdev_bind(tz, cdev); | ||
| 1503 | |||
| 1504 | mutex_unlock(&thermal_list_lock); | ||
| 1505 | 1621 | ||
| 1506 | thermal_zone_device_init(tz); | 1622 | thermal_zone_init_complete(tz); |
| 1507 | /* Update the new thermal zone and mark it as already updated. */ | ||
| 1508 | if (atomic_cmpxchg(&tz->need_update, 1, 0)) | ||
| 1509 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||
| 1510 | 1623 | ||
| 1511 | thermal_notify_tz_create(tz); | 1624 | thermal_notify_tz_create(tz); |
| 1512 | 1625 | ||
| @@ -1514,6 +1627,8 @@ thermal_zone_device_register_with_trips(const char *type, · 1 unmodified line | |||
| 1514 | 1627 | ||
| 1515 | return tz; | 1628 | return tz; |
| 1516 | 1629 | ||
| 1630 | remove_hwmon: | ||
| 1631 | thermal_remove_hwmon_sysfs(tz); | ||
| 1517 | unregister: | 1632 | unregister: |
| 1518 | device_del(&tz->device); | 1633 | device_del(&tz->device); |
| 1519 | release_device: | 1634 | release_device: |
| @@ -1563,44 +1678,46 @@ struct device *thermal_zone_device(struct thermal_zone_device *tzd) · 43 unmodified lines | |||
| 1563 | } | 1678 | } |
| 1564 | EXPORT_SYMBOL_GPL(thermal_zone_device); | 1679 | EXPORT_SYMBOL_GPL(thermal_zone_device); |
| 1565 | 1680 | ||
| 1681 | static bool thermal_zone_exit(struct thermal_zone_device *tz) | ||
| 1682 | { | ||
| 1683 | struct thermal_cooling_device *cdev; | ||
| 1684 | |||
| 1685 | guard(mutex)(&thermal_list_lock); | ||
| 1686 | |||
| 1687 | if (list_empty(&tz->node)) | ||
| 1688 | return false; | ||
| 1689 | |||
| 1690 | guard(thermal_zone)(tz); | ||
| 1691 | |||
| 1692 | tz->state |= TZ_STATE_FLAG_EXIT; | ||
| 1693 | list_del_init(&tz->node); | ||
| 1694 | |||
| 1695 | /* Unbind all cdevs associated with this thermal zone. */ | ||
| 1696 | list_for_each_entry(cdev, &thermal_cdev_list, node) | ||
| 1697 | __thermal_zone_cdev_unbind(tz, cdev); | ||
| 1698 | |||
| 1699 | return true; | ||
| 1700 | } | ||
| 1701 | |||
| 1566 | /** | 1702 | /** |
| 1567 | * thermal_zone_device_unregister - removes the registered thermal zone device | 1703 | * thermal_zone_device_unregister - removes the registered thermal zone device |
| 1568 | * @tz: the thermal zone device to remove | 1704 | * @tz: the thermal zone device to remove |
| 1569 | */ | 1705 | */ |
| 1570 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) | 1706 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) |
| 1571 | { | 1707 | { |
| 1572 | struct thermal_cooling_device *cdev; | ||
| 1573 | struct thermal_zone_device *pos = NULL; | ||
| 1574 | |||
| 1575 | if (!tz) | 1708 | if (!tz) |
| 1576 | return; | 1709 | return; |
| 1577 | 1710 | ||
| 1578 | thermal_debug_tz_remove(tz); | 1711 | thermal_debug_tz_remove(tz); |
| 1579 | 1712 | ||
| 1580 | mutex_lock(&thermal_list_lock); | 1713 | if (!thermal_zone_exit(tz)) |
| 1581 | list_for_each_entry(pos, &thermal_tz_list, node) | ||
| 1582 | if (pos == tz) | ||
| 1583 | break; | ||
| 1584 | if (pos != tz) { | ||
| 1585 | /* thermal zone device not found */ | ||
| 1586 | mutex_unlock(&thermal_list_lock); | ||
| 1587 | return; | 1714 | return; |
| 1588 | } | ||
| 1589 | |||
| 1590 | mutex_lock(&tz->lock); | ||
| 1591 | list_del(&tz->node); | ||
| 1592 | mutex_unlock(&tz->lock); | ||
| 1593 | |||
| 1594 | /* Unbind all cdevs associated with 'this' thermal zone */ | ||
| 1595 | list_for_each_entry(cdev, &thermal_cdev_list, node) | ||
| 1596 | thermal_zone_cdev_unbind(tz, cdev); | ||
| 1597 | |||
| 1598 | mutex_unlock(&thermal_list_lock); | ||
| 1599 | 1715 | ||
| 1600 | cancel_delayed_work_sync(&tz->poll_queue); | 1716 | cancel_delayed_work_sync(&tz->poll_queue); |
| 1601 | 1717 | ||
| 1602 | thermal_set_governor(tz, NULL); | 1718 | thermal_set_governor(tz, NULL); |
| 1603 | 1719 | ||
| 1720 | thermal_thresholds_exit(tz); | ||
| 1604 | thermal_remove_hwmon_sysfs(tz); | 1721 | thermal_remove_hwmon_sysfs(tz); |
| 1605 | ida_free(&thermal_tz_ida, tz->id); | 1722 | ida_free(&thermal_tz_ida, tz->id); |
| 1606 | ida_destroy(&tz->ida); | 1723 | ida_destroy(&tz->ida); |
| @@ -1632,24 +1749,23 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) · 25 unmodified lines | |||
| 1632 | unsigned int found = 0; | 1749 | unsigned int found = 0; |
| 1633 | 1750 | ||
| 1634 | if (!name) | 1751 | if (!name) |
| 1635 | goto exit; | 1752 | return ERR_PTR(-EINVAL); |
| 1753 | |||
| 1754 | guard(mutex)(&thermal_list_lock); | ||
| 1636 | 1755 | ||
| 1637 | mutex_lock(&thermal_list_lock); | ||
| 1638 | list_for_each_entry(pos, &thermal_tz_list, node) | 1756 | list_for_each_entry(pos, &thermal_tz_list, node) |
| 1639 | if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { | 1757 | if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { |
| 1640 | found++; | 1758 | found++; |
| 1641 | ref = pos; | 1759 | ref = pos; |
| 1642 | } | 1760 | } |
| 1643 | mutex_unlock(&thermal_list_lock); | ||
| 1644 | 1761 | ||
| 1645 | /* nothing has been found, thus an error code for it */ | 1762 | if (!found) |
| 1646 | if (found == 0) | 1763 | return ERR_PTR(-ENODEV); |
| 1647 | ref = ERR_PTR(-ENODEV); | 1764 | |
| 1648 | else if (found > 1) | 1765 | /* Success only when one zone is found. */ |
| 1649 | /* Success only when an unique zone is found */ | 1766 | if (found > 1) |
| 1650 | ref = ERR_PTR(-EEXIST); | 1767 | return ERR_PTR(-EEXIST); |
| 1651 | 1768 | ||
| 1652 | exit: | ||
| 1653 | return ref; | 1769 | return ref; |
| 1654 | } | 1770 | } |
| 1655 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); | 1771 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); |
| @@ -1660,9 +1776,9 @@ static void thermal_zone_device_resume(struct work_struct *work) · 4 unmodified lines | |||
| 1660 | 1776 | ||
| 1661 | tz = container_of(work, struct thermal_zone_device, poll_queue.work); | 1777 | tz = container_of(work, struct thermal_zone_device, poll_queue.work); |
| 1662 | 1778 | ||
| 1663 | mutex_lock(&tz->lock); | 1779 | guard(thermal_zone)(tz); |
| 1664 | 1780 | ||
| 1665 | tz->suspended = false; | 1781 | tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); |
| 1666 | 1782 | ||
| 1667 | thermal_debug_tz_resume(tz); | 1783 | thermal_debug_tz_resume(tz); |
| 1668 | thermal_zone_device_init(tz); | 1784 | thermal_zone_device_init(tz); |
| @@ -1670,74 +1786,81 @@ static void thermal_zone_device_resume(struct work_struct *work) · 1 unmodified line | |||
| 1670 | __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); | 1786 | __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); |
| 1671 | 1787 | ||
| 1672 | complete(&tz->resume); | 1788 | complete(&tz->resume); |
| 1673 | tz->resuming = false; | ||
| 1674 | |||
| 1675 | mutex_unlock(&tz->lock); | ||
| 1676 | } | 1789 | } |
| 1677 | 1790 | ||
| 1678 | static int thermal_pm_notify(struct notifier_block *nb, | 1791 | static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) |
| 1679 | unsigned long mode, void *_unused) | ||
| 1680 | { | 1792 | { |
| 1681 | struct thermal_zone_device *tz; | 1793 | guard(thermal_zone)(tz); |
| 1682 | 1794 | ||
| 1683 | switch (mode) { | 1795 | if (tz->state & TZ_STATE_FLAG_RESUMING) { |
| 1684 | case PM_HIBERNATION_PREPARE: | 1796 | /* |
| 1685 | case PM_RESTORE_PREPARE: | 1797 | * thermal_zone_device_resume() queued up for this zone has not |
| 1686 | case PM_SUSPEND_PREPARE: | 1798 | * acquired the lock yet, so release it to let the function run |
| 1687 | mutex_lock(&thermal_list_lock); | 1799 | * and wait util it has done the work. |
| 1800 | */ | ||
| 1801 | scoped_guard(thermal_zone_reverse, tz) { | ||
| 1802 | wait_for_completion(&tz->resume); | ||
| 1803 | } | ||
| 1804 | } | ||
| 1688 | 1805 | ||
| 1689 | list_for_each_entry(tz, &thermal_tz_list, node) { | 1806 | tz->state |= TZ_STATE_FLAG_SUSPENDED; |
| 1690 | mutex_lock(&tz->lock); | 1807 | } |
| 1691 | 1808 | ||
| 1692 | if (tz->resuming) { | 1809 | static void thermal_pm_notify_prepare(void) |
| 1693 | /* | 1810 | { |
| 1694 | * thermal_zone_device_resume() queued up for | 1811 | struct thermal_zone_device *tz; |
| 1695 | * this zone has not acquired the lock yet, so | ||
| 1696 | * release it to let the function run and wait | ||
| 1697 | * util it has done the work. | ||
| 1698 | */ | ||
| 1699 | mutex_unlock(&tz->lock); | ||
| 1700 | 1812 | ||
| 1701 | wait_for_completion(&tz->resume); | 1813 | guard(mutex)(&thermal_list_lock); |
| 1702 | 1814 | ||
| 1703 | mutex_lock(&tz->lock); | 1815 | thermal_pm_suspended = true; |
| 1704 | } | ||
| 1705 | 1816 | ||
| 1706 | tz->suspended = true; | 1817 | list_for_each_entry(tz, &thermal_tz_list, node) |
| 1818 | thermal_zone_pm_prepare(tz); | ||
| 1819 | } | ||
| 1707 | 1820 | ||
| 1708 | mutex_unlock(&tz->lock); | 1821 | static void thermal_zone_pm_complete(struct thermal_zone_device *tz) |
| 1709 | } | 1822 | { |
| 1823 | guard(thermal_zone)(tz); | ||
| 1710 | 1824 | ||
| 1711 | mutex_unlock(&thermal_list_lock); | 1825 | cancel_delayed_work(&tz->poll_queue); |
| 1712 | break; | ||
| 1713 | case PM_POST_HIBERNATION: | ||
| 1714 | case PM_POST_RESTORE: | ||
| 1715 | case PM_POST_SUSPEND: | ||
| 1716 | mutex_lock(&thermal_list_lock); | ||
| 1717 | 1826 | ||
| 1718 | list_for_each_entry(tz, &thermal_tz_list, node) { | 1827 | reinit_completion(&tz->resume); |
| 1719 | mutex_lock(&tz->lock); | 1828 | tz->state |= TZ_STATE_FLAG_RESUMING; |
| 1829 | |||
| 1830 | /* | ||
| 1831 | * Replace the work function with the resume one, which will restore the | ||
| 1832 | * original work function and schedule the polling work if needed. | ||
| 1833 | */ | ||
| 1834 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); | ||
| 1835 | /* Queue up the work without a delay. */ | ||
| 1836 | mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); | ||
| 1837 | } | ||
| 1720 | 1838 | ||
| 1721 | cancel_delayed_work(&tz->poll_queue); | 1839 | static void thermal_pm_notify_complete(void) |
| 1840 | { | ||
| 1841 | struct thermal_zone_device *tz; | ||
| 1722 | 1842 | ||
| 1723 | reinit_completion(&tz->resume); | 1843 | guard(mutex)(&thermal_list_lock); |
| 1724 | tz->resuming = true; | ||
| 1725 | 1844 | ||
| 1726 | /* | 1845 | thermal_pm_suspended = false; |
| 1727 | * Replace the work function with the resume one, which | ||
| 1728 | * will restore the original work function and schedule | ||
| 1729 | * the polling work if needed. | ||
| 1730 | */ | ||
| 1731 | INIT_DELAYED_WORK(&tz->poll_queue, | ||
| 1732 | thermal_zone_device_resume); | ||
| 1733 | /* Queue up the work without a delay. */ | ||
| 1734 | mod_delayed_work(system_freezable_power_efficient_wq, | ||
| 1735 | &tz->poll_queue, 0); | ||
| 1736 | 1846 | ||
| 1737 | mutex_unlock(&tz->lock); | 1847 | list_for_each_entry(tz, &thermal_tz_list, node) |
| 1738 | } | 1848 | thermal_zone_pm_complete(tz); |
| 1849 | } | ||
| 1739 | 1850 | ||
| 1740 | mutex_unlock(&thermal_list_lock); | 1851 | static int thermal_pm_notify(struct notifier_block *nb, |
| 1852 | unsigned long mode, void *_unused) | ||
| 1853 | { | ||
| 1854 | switch (mode) { | ||
| 1855 | case PM_HIBERNATION_PREPARE: | ||
| 1856 | case PM_RESTORE_PREPARE: | ||
| 1857 | case PM_SUSPEND_PREPARE: | ||
| 1858 | thermal_pm_notify_prepare(); | ||
| 1859 | break; | ||
| 1860 | case PM_POST_HIBERNATION: | ||
| 1861 | case PM_POST_RESTORE: | ||
| 1862 | case PM_POST_SUSPEND: | ||
| 1863 | thermal_pm_notify_complete(); | ||
| 1741 | break; | 1864 | break; |
| 1742 | default: | 1865 | default: |
| 1743 | break; | 1866 | break; |
| @@ -9,10 +9,12 @@ · 8 unmodified lines | |||
| 9 | #ifndef __THERMAL_CORE_H__ | 9 | #ifndef __THERMAL_CORE_H__ |
| 10 | #define __THERMAL_CORE_H__ | 10 | #define __THERMAL_CORE_H__ |
| 11 | 11 | ||
| 12 | #include <linux/cleanup.h> | ||
| 12 | #include <linux/device.h> | 13 | #include <linux/device.h> |
| 13 | #include <linux/thermal.h> | 14 | #include <linux/thermal.h> |
| 14 | 15 | ||
| 15 | #include "thermal_netlink.h" | 16 | #include "thermal_netlink.h" |
| 17 | #include "thermal_thresholds.h" | ||
| 16 | #include "thermal_debugfs.h" | 18 | #include "thermal_debugfs.h" |
| 17 | 19 | ||
| 18 | struct thermal_attr { | 20 | struct thermal_attr { |
| @@ -29,8 +31,8 @@ struct thermal_trip_attrs { · 10 unmodified lines | |||
| 29 | struct thermal_trip_desc { | 31 | struct thermal_trip_desc { |
| 30 | struct thermal_trip trip; | 32 | struct thermal_trip trip; |
| 31 | struct thermal_trip_attrs trip_attrs; | 33 | struct thermal_trip_attrs trip_attrs; |
| 32 | struct list_head notify_list_node; | 34 | struct list_head list_node; |
| 33 | int notify_temp; | 35 | struct list_head thermal_instances; |
| 34 | int threshold; | 36 | int threshold; |
| 35 | }; | 37 | }; |
| 36 | 38 | ||
| @@ -61,6 +63,13 @@ struct thermal_governor { · 24 unmodified lines | |||
| 61 | struct list_head governor_list; | 63 | struct list_head governor_list; |
| 62 | }; | 64 | }; |
| 63 | 65 | ||
| 66 | #define TZ_STATE_FLAG_SUSPENDED BIT(0) | ||
| 67 | #define TZ_STATE_FLAG_RESUMING BIT(1) | ||
| 68 | #define TZ_STATE_FLAG_INIT BIT(2) | ||
| 69 | #define TZ_STATE_FLAG_EXIT BIT(3) | ||
| 70 | |||
| 71 | #define TZ_STATE_READY 0 | ||
| 72 | |||
| 64 | /** | 73 | /** |
| 65 | * struct thermal_zone_device - structure for a thermal zone | 74 | * struct thermal_zone_device - structure for a thermal zone |
| 66 | * @id: unique id number for each thermal zone | 75 | * @id: unique id number for each thermal zone |
| @@ -68,6 +77,9 @@ struct thermal_governor { · 1 unmodified line | |||
| 68 | * @device: &struct device for this thermal zone | 77 | * @device: &struct device for this thermal zone |
| 69 | * @removal: removal completion | 78 | * @removal: removal completion |
| 70 | * @resume: resume completion | 79 | * @resume: resume completion |
| 80 | * @trips_high: trips above the current zone temperature | ||
| 81 | * @trips_reached: trips below or at the current zone temperature | ||
| 82 | * @trips_invalid: trips with invalid temperature | ||
| 71 | * @mode: current mode of this thermal zone | 83 | * @mode: current mode of this thermal zone |
| 72 | * @devdata: private pointer for device private data | 84 | * @devdata: private pointer for device private data |
| 73 | * @num_trips: number of trip points the thermal zone supports | 85 | * @num_trips: number of trip points the thermal zone supports |
| @@ -88,20 +100,17 @@ struct thermal_governor { · 14 unmodified lines | |||
| 88 | trip point. | 100 | trip point. |
| 89 | * @prev_high_trip: the above current temperature if you've crossed a | 101 | * @prev_high_trip: the above current temperature if you've crossed a |
| 90 | passive trip point. | 102 | passive trip point. |
| 91 | * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. | ||
| 92 | * @ops: operations this &thermal_zone_device supports | 103 | * @ops: operations this &thermal_zone_device supports |
| 93 | * @tzp: thermal zone parameters | 104 | * @tzp: thermal zone parameters |
| 94 | * @governor: pointer to the governor for this thermal zone | 105 | * @governor: pointer to the governor for this thermal zone |
| 95 | * @governor_data: private pointer for governor data | 106 | * @governor_data: private pointer for governor data |
| 96 | * @thermal_instances: list of &struct thermal_instance of this thermal zone | ||
| 97 | * @ida: &struct ida to generate unique id for this zone's cooling | 107 | * @ida: &struct ida to generate unique id for this zone's cooling |
| 98 | * devices | 108 | * devices |
| 99 | * @lock: lock to protect thermal_instances list | 109 | * @lock: lock to protect thermal_instances list |
| 100 | * @node: node in thermal_tz_list (in thermal_core.c) | 110 | * @node: node in thermal_tz_list (in thermal_core.c) |
| 101 | * @poll_queue: delayed work for polling | 111 | * @poll_queue: delayed work for polling |
| 102 | * @notify_event: Last notification event | 112 | * @notify_event: Last notification event |
| 103 | * @suspended: thermal zone suspend indicator | 113 | * @state: current state of the thermal zone |
| 104 | * @resuming: indicates whether or not thermal zone resume is in progress | ||
| 105 | * @trips: array of struct thermal_trip objects | 114 | * @trips: array of struct thermal_trip objects |
| 106 | */ | 115 | */ |
| 107 | struct thermal_zone_device { | 116 | struct thermal_zone_device { |
| @@ -111,6 +120,9 @@ struct thermal_zone_device { · 3 unmodified lines | |||
| 111 | struct completion removal; | 120 | struct completion removal; |
| 112 | struct completion resume; | 121 | struct completion resume; |
| 113 | struct attribute_group trips_attribute_group; | 122 | struct attribute_group trips_attribute_group; |
| 123 | struct list_head trips_high; | ||
| 124 | struct list_head trips_reached; | ||
| 125 | struct list_head trips_invalid; | ||
| 114 | enum thermal_device_mode mode; | 126 | enum thermal_device_mode mode; |
| 115 | void *devdata; | 127 | void *devdata; |
| 116 | int num_trips; | 128 | int num_trips; |
| @@ -123,25 +135,29 @@ struct thermal_zone_device { · 6 unmodified lines | |||
| 123 | int passive; | 135 | int passive; |
| 124 | int prev_low_trip; | 136 | int prev_low_trip; |
| 125 | int prev_high_trip; | 137 | int prev_high_trip; |
| 126 | atomic_t need_update; | ||
| 127 | struct thermal_zone_device_ops ops; | 138 | struct thermal_zone_device_ops ops; |
| 128 | struct thermal_zone_params *tzp; | 139 | struct thermal_zone_params *tzp; |
| 129 | struct thermal_governor *governor; | 140 | struct thermal_governor *governor; |
| 130 | void *governor_data; | 141 | void *governor_data; |
| 131 | struct list_head thermal_instances; | ||
| 132 | struct ida ida; | 142 | struct ida ida; |
| 133 | struct mutex lock; | 143 | struct mutex lock; |
| 134 | struct list_head node; | 144 | struct list_head node; |
| 135 | struct delayed_work poll_queue; | 145 | struct delayed_work poll_queue; |
| 136 | enum thermal_notify_event notify_event; | 146 | enum thermal_notify_event notify_event; |
| 137 | bool suspended; | 147 | u8 state; |
| 138 | bool resuming; | ||
| 139 | #ifdef CONFIG_THERMAL_DEBUGFS | 148 | #ifdef CONFIG_THERMAL_DEBUGFS |
| 140 | struct thermal_debugfs *debugfs; | 149 | struct thermal_debugfs *debugfs; |
| 141 | #endif | 150 | #endif |
| 151 | struct list_head user_thresholds; | ||
| 142 | struct thermal_trip_desc trips[] __counted_by(num_trips); | 152 | struct thermal_trip_desc trips[] __counted_by(num_trips); |
| 143 | }; | 153 | }; |
| 144 | 154 | ||
| 155 | DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock), | ||
| 156 | mutex_unlock(&_T->lock)) | ||
| 157 | |||
| 158 | DEFINE_GUARD(thermal_zone_reverse, struct thermal_zone_device *, | ||
| 159 | mutex_unlock(&_T->lock), mutex_lock(&_T->lock)) | ||
| 160 | |||
| 145 | /* Initial thermal zone temperature. */ | 161 | /* Initial thermal zone temperature. */ |
| 146 | #define THERMAL_TEMP_INIT INT_MIN | 162 | #define THERMAL_TEMP_INIT INT_MIN |
| 147 | 163 | ||
| @@ -204,6 +220,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) · 56 unmodified lines | |||
| 204 | } | 220 | } |
| 205 | 221 | ||
| 206 | void thermal_cdev_update(struct thermal_cooling_device *); | 222 | void thermal_cdev_update(struct thermal_cooling_device *); |
| 223 | void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev); | ||
| 207 | void __thermal_cdev_update(struct thermal_cooling_device *cdev); | 224 | void __thermal_cdev_update(struct thermal_cooling_device *cdev); |
| 208 | 225 | ||
| 209 | int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); | 226 | int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); |
| @@ -226,7 +243,7 @@ struct thermal_instance { · 16 unmodified lines | |||
| 226 | struct device_attribute attr; | 243 | struct device_attribute attr; |
| 227 | char weight_attr_name[THERMAL_NAME_LENGTH]; | 244 | char weight_attr_name[THERMAL_NAME_LENGTH]; |
| 228 | struct device_attribute weight_attr; | 245 | struct device_attribute weight_attr; |
| 229 | struct list_head tz_node; /* node in tz->thermal_instances */ | 246 | struct list_head trip_node; /* node in trip->thermal_instances */ |
| 230 | struct list_head cdev_node; /* node in cdev->thermal_instances */ | 247 | struct list_head cdev_node; /* node in cdev->thermal_instances */ |
| 231 | unsigned int weight; /* The weight of the cooling device */ | 248 | unsigned int weight; /* The weight of the cooling device */ |
| 232 | bool upper_no_limit; | 249 | bool upper_no_limit; |
| @@ -261,8 +278,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high); · 28 unmodified lines | |||
| 261 | int thermal_zone_trip_id(const struct thermal_zone_device *tz, | 278 | int thermal_zone_trip_id(const struct thermal_zone_device *tz, |
| 262 | const struct thermal_trip *trip); | 279 | const struct thermal_trip *trip); |
| 263 | int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); | 280 | int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); |
| 264 | void thermal_zone_trip_down(struct thermal_zone_device *tz, | ||
| 265 | const struct thermal_trip *trip); | ||
| 266 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, | 281 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, |
| 267 | struct thermal_trip *trip, int hyst); | 282 | struct thermal_trip *trip, int hyst); |
| 268 | 283 | ||
| @@ -516,6 +516,19 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) · 515 unmodified lines | |||
| 516 | cdev->debugfs = thermal_dbg; | 516 | cdev->debugfs = thermal_dbg; |
| 517 | } | 517 | } |
| 518 | 518 | ||
| 519 | static struct thermal_debugfs *thermal_debug_cdev_clear(struct thermal_cooling_device *cdev) | ||
| 520 | { | ||
| 521 | struct thermal_debugfs *thermal_dbg; | ||
| 522 | |||
| 523 | guard(cooling_dev)(cdev); | ||
| 524 | |||
| 525 | thermal_dbg = cdev->debugfs; | ||
| 526 | if (thermal_dbg) | ||
| 527 | cdev->debugfs = NULL; | ||
| 528 | |||
| 529 | return thermal_dbg; | ||
| 530 | } | ||
| 531 | |||
| 519 | /** | 532 | /** |
| 520 | * thermal_debug_cdev_remove - Remove a cooling device debugfs entry | 533 | * thermal_debug_cdev_remove - Remove a cooling device debugfs entry |
| 521 | * | 534 | * |
| @@ -527,17 +540,9 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) · 5 unmodified lines | |||
| 527 | { | 540 | { |
| 528 | struct thermal_debugfs *thermal_dbg; | 541 | struct thermal_debugfs *thermal_dbg; |
| 529 | 542 | ||
| 530 | mutex_lock(&cdev->lock); | 543 | thermal_dbg = thermal_debug_cdev_clear(cdev); |
| 531 | 544 | if (!thermal_dbg) | |
| 532 | thermal_dbg = cdev->debugfs; | ||
| 533 | if (!thermal_dbg) { | ||
| 534 | mutex_unlock(&cdev->lock); | ||
| 535 | return; | 545 | return; |
| 536 | } | ||
| 537 | |||
| 538 | cdev->debugfs = NULL; | ||
| 539 | |||
| 540 | mutex_unlock(&cdev->lock); | ||
| 541 | 546 | ||
| 542 | mutex_lock(&thermal_dbg->lock); | 547 | mutex_lock(&thermal_dbg->lock); |
| 543 | 548 | ||
| @@ -885,6 +890,19 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) · 341 unmodified lines | |||
| 885 | tz->debugfs = thermal_dbg; | 890 | tz->debugfs = thermal_dbg; |
| 886 | } | 891 | } |
| 887 | 892 | ||
| 893 | static struct thermal_debugfs *thermal_debug_tz_clear(struct thermal_zone_device *tz) | ||
| 894 | { | ||
| 895 | struct thermal_debugfs *thermal_dbg; | ||
| 896 | |||
| 897 | guard(thermal_zone)(tz); | ||
| 898 | |||
| 899 | thermal_dbg = tz->debugfs; | ||
| 900 | if (thermal_dbg) | ||
| 901 | tz->debugfs = NULL; | ||
| 902 | |||
| 903 | return thermal_dbg; | ||
| 904 | } | ||
| 905 | |||
| 888 | void thermal_debug_tz_remove(struct thermal_zone_device *tz) | 906 | void thermal_debug_tz_remove(struct thermal_zone_device *tz) |
| 889 | { | 907 | { |
| 890 | struct thermal_debugfs *thermal_dbg; | 908 | struct thermal_debugfs *thermal_dbg; |
| @@ -892,17 +910,9 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz) · 1 unmodified line | |||
| 892 | struct tz_debugfs *tz_dbg; | 910 | struct tz_debugfs *tz_dbg; |
| 893 | int *trips_crossed; | 911 | int *trips_crossed; |
| 894 | 912 | ||
| 895 | mutex_lock(&tz->lock); | 913 | thermal_dbg = thermal_debug_tz_clear(tz); |
| 896 | 914 | if (!thermal_dbg) | |
| 897 | thermal_dbg = tz->debugfs; | ||
| 898 | if (!thermal_dbg) { | ||
| 899 | mutex_unlock(&tz->lock); | ||
| 900 | return; | 915 | return; |
| 901 | } | ||
| 902 | |||
| 903 | tz->debugfs = NULL; | ||
| 904 | |||
| 905 | mutex_unlock(&tz->lock); | ||
| 906 | 916 | ||
| 907 | tz_dbg = &thermal_dbg->tz_dbg; | 917 | tz_dbg = &thermal_dbg->tz_dbg; |
| 908 | 918 | ||
| @@ -43,10 +43,11 @@ static bool thermal_instance_present(struct thermal_zone_device *tz, · 42 unmodified lines | |||
| 43 | struct thermal_cooling_device *cdev, | 43 | struct thermal_cooling_device *cdev, |
| 44 | const struct thermal_trip *trip) | 44 | const struct thermal_trip *trip) |
| 45 | { | 45 | { |
| 46 | const struct thermal_trip_desc *td = trip_to_trip_desc(trip); | ||
| 46 | struct thermal_instance *ti; | 47 | struct thermal_instance *ti; |
| 47 | 48 | ||
| 48 | list_for_each_entry(ti, &tz->thermal_instances, tz_node) { | 49 | list_for_each_entry(ti, &td->thermal_instances, trip_node) { |
| 49 | if (ti->trip == trip && ti->cdev == cdev) | 50 | if (ti->cdev == cdev) |
| 50 | return true; | 51 | return true; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| @@ -57,17 +58,10 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, · 4 unmodified lines | |||
| 57 | const struct thermal_trip *trip, | 58 | const struct thermal_trip *trip, |
| 58 | struct thermal_cooling_device *cdev) | 59 | struct thermal_cooling_device *cdev) |
| 59 | { | 60 | { |
| 60 | bool ret; | 61 | guard(thermal_zone)(tz); |
| 62 | guard(cooling_dev)(cdev); | ||
| 61 | 63 | ||
| 62 | mutex_lock(&tz->lock); | 64 | return thermal_instance_present(tz, cdev, trip); |
| 63 | mutex_lock(&cdev->lock); | ||
| 64 | |||
| 65 | ret = thermal_instance_present(tz, cdev, trip); | ||
| 66 | |||
| 67 | mutex_unlock(&cdev->lock); | ||
| 68 | mutex_unlock(&tz->lock); | ||
| 69 | |||
| 70 | return ret; | ||
| 71 | } | 65 | } |
| 72 | EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); | 66 | EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); |
| 73 | 67 | ||
| @@ -137,19 +131,14 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) · 63 unmodified lines | |||
| 137 | if (IS_ERR_OR_NULL(tz)) | 131 | if (IS_ERR_OR_NULL(tz)) |
| 138 | return -EINVAL; | 132 | return -EINVAL; |
| 139 | 133 | ||
| 140 | mutex_lock(&tz->lock); | 134 | guard(thermal_zone)(tz); |
| 141 | 135 | ||
| 142 | if (!tz->ops.get_temp) { | 136 | if (!tz->ops.get_temp) |
| 143 | ret = -EINVAL; | 137 | return -EINVAL; |
| 144 | goto unlock; | ||
| 145 | } | ||
| 146 | 138 | ||
| 147 | ret = __thermal_zone_get_temp(tz, temp); | 139 | ret = __thermal_zone_get_temp(tz, temp); |
| 148 | if (!ret && *temp <= THERMAL_TEMP_INVALID) | 140 | if (!ret && *temp <= THERMAL_TEMP_INVALID) |
| 149 | ret = -ENODATA; | 141 | return -ENODATA; |
| 150 | |||
| 151 | unlock: | ||
| 152 | mutex_unlock(&tz->lock); | ||
| 153 | 142 | ||
| 154 | return ret; | 143 | return ret; |
| 155 | } | 144 | } |
| @@ -201,12 +190,23 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) · 45 unmodified lines | |||
| 201 | */ | 190 | */ |
| 202 | void thermal_cdev_update(struct thermal_cooling_device *cdev) | 191 | void thermal_cdev_update(struct thermal_cooling_device *cdev) |
| 203 | { | 192 | { |
| 204 | mutex_lock(&cdev->lock); | 193 | guard(cooling_dev)(cdev); |
| 194 | |||
| 205 | if (!cdev->updated) { | 195 | if (!cdev->updated) { |
| 206 | __thermal_cdev_update(cdev); | 196 | __thermal_cdev_update(cdev); |
| 207 | cdev->updated = true; | 197 | cdev->updated = true; |
| 208 | } | 198 | } |
| 209 | mutex_unlock(&cdev->lock); | 199 | } |
| 200 | |||
| 201 | /** | ||
| 202 | * thermal_cdev_update_nocheck() - Unconditionally update cooling device state | ||
| 203 | * @cdev: Target cooling device. | ||
| 204 | */ | ||
| 205 | void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev) | ||
| 206 | { | ||
| 207 | guard(cooling_dev)(cdev); | ||
| 208 | |||
| 209 | __thermal_cdev_update(cdev); | ||
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | /** | 212 | /** |
| @@ -78,12 +78,9 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) · 77 unmodified lines | |||
| 78 | int temperature; | 78 | int temperature; |
| 79 | int ret; | 79 | int ret; |
| 80 | 80 | ||
| 81 | mutex_lock(&tz->lock); | 81 | guard(thermal_zone)(tz); |
| 82 | 82 | ||
| 83 | ret = tz->ops.get_crit_temp(tz, &temperature); | 83 | ret = tz->ops.get_crit_temp(tz, &temperature); |
| 84 | |||
| 85 | mutex_unlock(&tz->lock); | ||
| 86 | |||
| 87 | if (ret) | 84 | if (ret) |
| 88 | return ret; | 85 | return ret; |
| 89 | 86 | ||
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
| 10 | #include <linux/notifier.h> | 10 | #include <linux/notifier.h> |
| 11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
| 12 | #include <net/sock.h> | ||
| 12 | #include <net/genetlink.h> | 13 | #include <net/genetlink.h> |
| 13 | #include <uapi/linux/thermal.h> | 14 | #include <uapi/linux/thermal.h> |
| 14 | 15 | ||
| @@ -49,6 +50,11 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = · 34 unmodified lines | |||
| 49 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, | 50 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, |
| 50 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, | 51 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, |
| 51 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, | 52 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, |
| 53 | |||
| 54 | /* Thresholds */ | ||
| 55 | [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, | ||
| 56 | [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, | ||
| 57 | [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, | ||
| 52 | }; | 58 | }; |
| 53 | 59 | ||
| 54 | struct param { | 60 | struct param { |
| @@ -62,6 +68,8 @@ struct param { · 7 unmodified lines | |||
| 62 | int trip_type; | 68 | int trip_type; |
| 63 | int trip_hyst; | 69 | int trip_hyst; |
| 64 | int temp; | 70 | int temp; |
| 71 | int prev_temp; | ||
| 72 | int direction; | ||
| 65 | int cdev_state; | 73 | int cdev_state; |
| 66 | int cdev_max_state; | 74 | int cdev_max_state; |
| 67 | struct thermal_genl_cpu_caps *cpu_capabilities; | 75 | struct thermal_genl_cpu_caps *cpu_capabilities; |
| @@ -234,6 +242,34 @@ out_cancel_nest: · 166 unmodified lines | |||
| 234 | return -EMSGSIZE; | 242 | return -EMSGSIZE; |
| 235 | } | 243 | } |
| 236 | 244 | ||
| 245 | static int thermal_genl_event_threshold_add(struct param *p) | ||
| 246 | { | ||
| 247 | if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || | ||
| 248 | nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) || | ||
| 249 | nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) | ||
| 250 | return -EMSGSIZE; | ||
| 251 | |||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | static int thermal_genl_event_threshold_flush(struct param *p) | ||
| 256 | { | ||
| 257 | if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) | ||
| 258 | return -EMSGSIZE; | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int thermal_genl_event_threshold_up(struct param *p) | ||
| 264 | { | ||
| 265 | if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || | ||
| 266 | nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_PREV_TEMP, p->prev_temp) || | ||
| 267 | nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp)) | ||
| 268 | return -EMSGSIZE; | ||
| 269 | |||
| 270 | return 0; | ||
| 271 | } | ||
| 272 | |||
| 237 | int thermal_genl_event_tz_delete(struct param *p) | 273 | int thermal_genl_event_tz_delete(struct param *p) |
| 238 | __attribute__((alias("thermal_genl_event_tz"))); | 274 | __attribute__((alias("thermal_genl_event_tz"))); |
| 239 | 275 | ||
| @@ -246,6 +282,12 @@ int thermal_genl_event_tz_disable(struct param *p) · 6 unmodified lines | |||
| 246 | int thermal_genl_event_tz_trip_down(struct param *p) | 282 | int thermal_genl_event_tz_trip_down(struct param *p) |
| 247 | __attribute__((alias("thermal_genl_event_tz_trip_up"))); | 283 | __attribute__((alias("thermal_genl_event_tz_trip_up"))); |
| 248 | 284 | ||
| 285 | int thermal_genl_event_threshold_delete(struct param *p) | ||
| 286 | __attribute__((alias("thermal_genl_event_threshold_add"))); | ||
| 287 | |||
| 288 | int thermal_genl_event_threshold_down(struct param *p) | ||
| 289 | __attribute__((alias("thermal_genl_event_threshold_up"))); | ||
| 290 | |||
| 249 | static cb_t event_cb[] = { | 291 | static cb_t event_cb[] = { |
| 250 | [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, | 292 | [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, |
| 251 | [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, | 293 | [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, |
| @@ -259,6 +301,11 @@ static cb_t event_cb[] = { · 7 unmodified lines | |||
| 259 | [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, | 301 | [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, |
| 260 | [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, | 302 | [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, |
| 261 | [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, | 303 | [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, |
| 304 | [THERMAL_GENL_EVENT_THRESHOLD_ADD] = thermal_genl_event_threshold_add, | ||
| 305 | [THERMAL_GENL_EVENT_THRESHOLD_DELETE] = thermal_genl_event_threshold_delete, | ||
| 306 | [THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = thermal_genl_event_threshold_flush, | ||
| 307 | [THERMAL_GENL_EVENT_THRESHOLD_DOWN] = thermal_genl_event_threshold_down, | ||
| 308 | [THERMAL_GENL_EVENT_THRESHOLD_UP] = thermal_genl_event_threshold_up, | ||
| 262 | }; | 309 | }; |
| 263 | 310 | ||
| 264 | /* | 311 | /* |
| @@ -401,6 +448,43 @@ int thermal_genl_cpu_capability_event(int count, · 136 unmodified lines | |||
| 401 | } | 448 | } |
| 402 | EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); | 449 | EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); |
| 403 | 450 | ||
| 451 | int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | ||
| 452 | int temperature, int direction) | ||
| 453 | { | ||
| 454 | struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | ||
| 455 | |||
| 456 | return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p); | ||
| 457 | } | ||
| 458 | |||
| 459 | int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | ||
| 460 | int temperature, int direction) | ||
| 461 | { | ||
| 462 | struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | ||
| 463 | |||
| 464 | return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p); | ||
| 465 | } | ||
| 466 | |||
| 467 | int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) | ||
| 468 | { | ||
| 469 | struct param p = { .tz_id = tz->id }; | ||
| 470 | |||
| 471 | return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p); | ||
| 472 | } | ||
| 473 | |||
| 474 | int thermal_notify_threshold_down(const struct thermal_zone_device *tz) | ||
| 475 | { | ||
| 476 | struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; | ||
| 477 | |||
| 478 | return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p); | ||
| 479 | } | ||
| 480 | |||
| 481 | int thermal_notify_threshold_up(const struct thermal_zone_device *tz) | ||
| 482 | { | ||
| 483 | struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; | ||
| 484 | |||
| 485 | return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p); | ||
| 486 | } | ||
| 487 | |||
| 404 | /*************************** Command encoding ********************************/ | 488 | /*************************** Command encoding ********************************/ |
| 405 | 489 | ||
| 406 | static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, | 490 | static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, |
| @@ -459,7 +543,7 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) · 52 unmodified lines | |||
| 459 | if (!start_trip) | 543 | if (!start_trip) |
| 460 | return -EMSGSIZE; | 544 | return -EMSGSIZE; |
| 461 | 545 | ||
| 462 | mutex_lock(&tz->lock); | 546 | guard(thermal_zone)(tz); |
| 463 | 547 | ||
| 464 | for_each_trip_desc(tz, td) { | 548 | for_each_trip_desc(tz, td) { |
| 465 | const struct thermal_trip *trip = &td->trip; | 549 | const struct thermal_trip *trip = &td->trip; |
| @@ -469,19 +553,12 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) · 3 unmodified lines | |||
| 469 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || | 553 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || |
| 470 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) || | 554 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) || |
| 471 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis)) | 555 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis)) |
| 472 | goto out_cancel_nest; | 556 | return -EMSGSIZE; |
| 473 | } | 557 | } |
| 474 | 558 | ||
| 475 | mutex_unlock(&tz->lock); | ||
| 476 | |||
| 477 | nla_nest_end(msg, start_trip); | 559 | nla_nest_end(msg, start_trip); |
| 478 | 560 | ||
| 479 | return 0; | 561 | return 0; |
| 480 | |||
| 481 | out_cancel_nest: | ||
| 482 | mutex_unlock(&tz->lock); | ||
| 483 | |||
| 484 | return -EMSGSIZE; | ||
| 485 | } | 562 | } |
| 486 | 563 | ||
| 487 | static int thermal_genl_cmd_tz_get_temp(struct param *p) | 564 | static int thermal_genl_cmd_tz_get_temp(struct param *p) |
| @@ -512,7 +589,7 @@ static int thermal_genl_cmd_tz_get_temp(struct param *p) · 24 unmodified lines | |||
| 512 | static int thermal_genl_cmd_tz_get_gov(struct param *p) | 589 | static int thermal_genl_cmd_tz_get_gov(struct param *p) |
| 513 | { | 590 | { |
| 514 | struct sk_buff *msg = p->msg; | 591 | struct sk_buff *msg = p->msg; |
| 515 | int id, ret = 0; | 592 | int id; |
| 516 | 593 | ||
| 517 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) | 594 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) |
| 518 | return -EINVAL; | 595 | return -EINVAL; |
| @@ -523,16 +600,14 @@ static int thermal_genl_cmd_tz_get_gov(struct param *p) · 4 unmodified lines | |||
| 523 | if (!tz) | 600 | if (!tz) |
| 524 | return -EINVAL; | 601 | return -EINVAL; |
| 525 | 602 | ||
| 526 | mutex_lock(&tz->lock); | 603 | guard(thermal_zone)(tz); |
| 527 | 604 | ||
| 528 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || | 605 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || |
| 529 | nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, | 606 | nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, |
| 530 | tz->governor->name)) | 607 | tz->governor->name)) |
| 531 | ret = -EMSGSIZE; | 608 | return -EMSGSIZE; |
| 532 | |||
| 533 | mutex_unlock(&tz->lock); | ||
| 534 | 609 | ||
| 535 | return ret; | 610 | return 0; |
| 536 | } | 611 | } |
| 537 | 612 | ||
| 538 | static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, | 613 | static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, |
| @@ -572,12 +647,128 @@ out_cancel_nest: · 33 unmodified lines | |||
| 572 | return ret; | 647 | return ret; |
| 573 | } | 648 | } |
| 574 | 649 | ||
| 650 | static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg) | ||
| 651 | { | ||
| 652 | struct sk_buff *msg = arg; | ||
| 653 | |||
| 654 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) || | ||
| 655 | nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, threshold->direction)) | ||
| 656 | return -1; | ||
| 657 | |||
| 658 | return 0; | ||
| 659 | } | ||
| 660 | |||
| 661 | static int thermal_genl_cmd_threshold_get(struct param *p) | ||
| 662 | { | ||
| 663 | struct sk_buff *msg = p->msg; | ||
| 664 | struct nlattr *start_trip; | ||
| 665 | int id, ret; | ||
| 666 | |||
| 667 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) | ||
| 668 | return -EINVAL; | ||
| 669 | |||
| 670 | id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | ||
| 671 | |||
| 672 | CLASS(thermal_zone_get_by_id, tz)(id); | ||
| 673 | if (!tz) | ||
| 674 | return -EINVAL; | ||
| 675 | |||
| 676 | start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD); | ||
| 677 | if (!start_trip) | ||
| 678 | return -EMSGSIZE; | ||
| 679 | |||
| 680 | ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg); | ||
| 681 | if (ret) | ||
| 682 | return -EMSGSIZE; | ||
| 683 | |||
| 684 | nla_nest_end(msg, start_trip); | ||
| 685 | |||
| 686 | return 0; | ||
| 687 | } | ||
| 688 | |||
| 689 | static int thermal_genl_cmd_threshold_add(struct param *p) | ||
| 690 | { | ||
| 691 | int id, temp, direction; | ||
| 692 | |||
| 693 | if (!capable(CAP_SYS_ADMIN)) | ||
| 694 | return -EPERM; | ||
| 695 | |||
| 696 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || | ||
| 697 | !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || | ||
| 698 | !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) | ||
| 699 | return -EINVAL; | ||
| 700 | |||
| 701 | id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | ||
| 702 | temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); | ||
| 703 | direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); | ||
| 704 | |||
| 705 | CLASS(thermal_zone_get_by_id, tz)(id); | ||
| 706 | if (!tz) | ||
| 707 | return -EINVAL; | ||
| 708 | |||
| 709 | guard(thermal_zone)(tz); | ||
| 710 | |||
| 711 | return thermal_thresholds_add(tz, temp, direction); | ||
| 712 | } | ||
| 713 | |||
| 714 | static int thermal_genl_cmd_threshold_delete(struct param *p) | ||
| 715 | { | ||
| 716 | int id, temp, direction; | ||
| 717 | |||
| 718 | if (!capable(CAP_SYS_ADMIN)) | ||
| 719 | return -EPERM; | ||
| 720 | |||
| 721 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || | ||
| 722 | !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || | ||
| 723 | !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) | ||
| 724 | return -EINVAL; | ||
| 725 | |||
| 726 | id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | ||
| 727 | temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); | ||
| 728 | direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); | ||
| 729 | |||
| 730 | CLASS(thermal_zone_get_by_id, tz)(id); | ||
| 731 | if (!tz) | ||
| 732 | return -EINVAL; | ||
| 733 | |||
| 734 | guard(thermal_zone)(tz); | ||
| 735 | |||
| 736 | return thermal_thresholds_delete(tz, temp, direction); | ||
| 737 | } | ||
| 738 | |||
| 739 | static int thermal_genl_cmd_threshold_flush(struct param *p) | ||
| 740 | { | ||
| 741 | int id; | ||
| 742 | |||
| 743 | if (!capable(CAP_SYS_ADMIN)) | ||
| 744 | return -EPERM; | ||
| 745 | |||
| 746 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) | ||
| 747 | return -EINVAL; | ||
| 748 | |||
| 749 | id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | ||
| 750 | |||
| 751 | CLASS(thermal_zone_get_by_id, tz)(id); | ||
| 752 | if (!tz) | ||
| 753 | return -EINVAL; | ||
| 754 | |||
| 755 | guard(thermal_zone)(tz); | ||
| 756 | |||
| 757 | thermal_thresholds_flush(tz); | ||
| 758 | |||
| 759 | return 0; | ||
| 760 | } | ||
| 761 | |||
| 575 | static cb_t cmd_cb[] = { | 762 | static cb_t cmd_cb[] = { |
| 576 | [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, | 763 | [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, |
| 577 | [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, | 764 | [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, |
| 578 | [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, | 765 | [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, |
| 579 | [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, | 766 | [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, |
| 580 | [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, | 767 | [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, |
| 768 | [THERMAL_GENL_CMD_THRESHOLD_GET] = thermal_genl_cmd_threshold_get, | ||
| 769 | [THERMAL_GENL_CMD_THRESHOLD_ADD] = thermal_genl_cmd_threshold_add, | ||
| 770 | [THERMAL_GENL_CMD_THRESHOLD_DELETE] = thermal_genl_cmd_threshold_delete, | ||
| 771 | [THERMAL_GENL_CMD_THRESHOLD_FLUSH] = thermal_genl_cmd_threshold_flush, | ||
| 581 | }; | 772 | }; |
| 582 | 773 | ||
| 583 | static int thermal_genl_cmd_dumpit(struct sk_buff *skb, | 774 | static int thermal_genl_cmd_dumpit(struct sk_buff *skb, |
| @@ -688,6 +879,26 @@ static const struct genl_small_ops thermal_genl_ops[] = { · 104 unmodified lines | |||
| 688 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 879 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
| 689 | .dumpit = thermal_genl_cmd_dumpit, | 880 | .dumpit = thermal_genl_cmd_dumpit, |
| 690 | }, | 881 | }, |
| 882 | { | ||
| 883 | .cmd = THERMAL_GENL_CMD_THRESHOLD_GET, | ||
| 884 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | ||
| 885 | .doit = thermal_genl_cmd_doit, | ||
| 886 | }, | ||
| 887 | { | ||
| 888 | .cmd = THERMAL_GENL_CMD_THRESHOLD_ADD, | ||
| 889 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | ||
| 890 | .doit = thermal_genl_cmd_doit, | ||
| 891 | }, | ||
| 892 | { | ||
| 893 | .cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE, | ||
| 894 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | ||
| 895 | .doit = thermal_genl_cmd_doit, | ||
| 896 | }, | ||
| 897 | { | ||
| 898 | .cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH, | ||
| 899 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | ||
| 900 | .doit = thermal_genl_cmd_doit, | ||
| 901 | }, | ||
| 691 | }; | 902 | }; |
| 692 | 903 | ||
| 693 | static struct genl_family thermal_genl_family __ro_after_init = { | 904 | static struct genl_family thermal_genl_family __ro_after_init = { |
| @@ -700,7 +911,7 @@ static struct genl_family thermal_genl_family __ro_after_init = { · 6 unmodified lines | |||
| 700 | .unbind = thermal_genl_unbind, | 911 | .unbind = thermal_genl_unbind, |
| 701 | .small_ops = thermal_genl_ops, | 912 | .small_ops = thermal_genl_ops, |
| 702 | .n_small_ops = ARRAY_SIZE(thermal_genl_ops), | 913 | .n_small_ops = ARRAY_SIZE(thermal_genl_ops), |
| 703 | .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, | 914 | .resv_start_op = __THERMAL_GENL_CMD_MAX, |
| 704 | .mcgrps = thermal_genl_mcgrps, | 915 | .mcgrps = thermal_genl_mcgrps, |
| 705 | .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), | 916 | .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), |
| 706 | }; | 917 | }; |
| @@ -53,6 +53,13 @@ int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, · 52 unmodified lines | |||
| 53 | int thermal_genl_sampling_temp(int id, int temp); | 53 | int thermal_genl_sampling_temp(int id, int temp); |
| 54 | int thermal_genl_cpu_capability_event(int count, | 54 | int thermal_genl_cpu_capability_event(int count, |
| 55 | struct thermal_genl_cpu_caps *caps); | 55 | struct thermal_genl_cpu_caps *caps); |
| 56 | int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | ||
| 57 | int temperature, int direction); | ||
| 58 | int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | ||
| 59 | int temperature, int direction); | ||
| 60 | int thermal_notify_threshold_flush(const struct thermal_zone_device *tz); | ||
| 61 | int thermal_notify_threshold_down(const struct thermal_zone_device *tz); | ||
| 62 | int thermal_notify_threshold_up(const struct thermal_zone_device *tz); | ||
| 56 | #else | 63 | #else |
| 57 | static inline int thermal_netlink_init(void) | 64 | static inline int thermal_netlink_init(void) |
| 58 | { | 65 | { |
| @@ -139,6 +146,33 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge · 80 unmodified lines | |||
| 139 | return 0; | 146 | return 0; |
| 140 | } | 147 | } |
| 141 | 148 | ||
| 149 | static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | ||
| 150 | int temperature, int direction) | ||
| 151 | { | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | ||
| 156 | int temperature, int direction) | ||
| 157 | { | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) | ||
| 162 | { | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz) | ||
| 167 | { | ||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | |||
| 171 | static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz) | ||
| 172 | { | ||
| 173 | return 0; | ||
| 174 | } | ||
| 175 | |||
| 142 | static inline void __init thermal_netlink_exit(void) {} | 176 | static inline void __init thermal_netlink_exit(void) {} |
| 143 | 177 | ||
| 144 | #endif /* CONFIG_THERMAL_NETLINK */ | 178 | #endif /* CONFIG_THERMAL_NETLINK */ |
| @@ -50,13 +50,13 @@ static ssize_t · 49 unmodified lines | |||
| 50 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) | 50 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 51 | { | 51 | { |
| 52 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 52 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 53 | int enabled; | ||
| 54 | 53 | ||
| 55 | mutex_lock(&tz->lock); | 54 | guard(thermal_zone)(tz); |
| 56 | enabled = tz->mode == THERMAL_DEVICE_ENABLED; | ||
| 57 | mutex_unlock(&tz->lock); | ||
| 58 | 55 | ||
| 59 | return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled"); | 56 | if (tz->mode == THERMAL_DEVICE_ENABLED) |
| 57 | return sprintf(buf, "enabled\n"); | ||
| 58 | |||
| 59 | return sprintf(buf, "disabled\n"); | ||
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | static ssize_t | 62 | static ssize_t |
| @@ -103,38 +103,34 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, · 40 unmodified lines | |||
| 103 | { | 103 | { |
| 104 | struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); | 104 | struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); |
| 105 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 105 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 106 | int ret, temp; | 106 | int temp; |
| 107 | 107 | ||
| 108 | ret = kstrtoint(buf, 10, &temp); | 108 | if (kstrtoint(buf, 10, &temp)) |
| 109 | if (ret) | ||
| 110 | return -EINVAL; | 109 | return -EINVAL; |
| 111 | 110 | ||
| 112 | mutex_lock(&tz->lock); | 111 | guard(thermal_zone)(tz); |
| 113 | 112 | ||
| 114 | if (temp == trip->temperature) | 113 | if (temp == trip->temperature) |
| 115 | goto unlock; | 114 | return count; |
| 116 | 115 | ||
| 117 | /* Arrange the condition to avoid integer overflows. */ | 116 | /* Arrange the condition to avoid integer overflows. */ |
| 118 | if (temp != THERMAL_TEMP_INVALID && | 117 | if (temp != THERMAL_TEMP_INVALID && |
| 119 | temp <= trip->hysteresis + THERMAL_TEMP_INVALID) { | 118 | temp <= trip->hysteresis + THERMAL_TEMP_INVALID) |
| 120 | ret = -EINVAL; | 119 | return -EINVAL; |
| 121 | goto unlock; | ||
| 122 | } | ||
| 123 | 120 | ||
| 124 | if (tz->ops.set_trip_temp) { | 121 | if (tz->ops.set_trip_temp) { |
| 122 | int ret; | ||
| 123 | |||
| 125 | ret = tz->ops.set_trip_temp(tz, trip, temp); | 124 | ret = tz->ops.set_trip_temp(tz, trip, temp); |
| 126 | if (ret) | 125 | if (ret) |
| 127 | goto unlock; | 126 | return ret; |
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | thermal_zone_set_trip_temp(tz, trip, temp); | 129 | thermal_zone_set_trip_temp(tz, trip, temp); |
| 131 | 130 | ||
| 132 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); | 131 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); |
| 133 | 132 | ||
| 134 | unlock: | 133 | return count; |
| 135 | mutex_unlock(&tz->lock); | ||
| 136 | |||
| 137 | return ret ? ret : count; | ||
| 138 | } | 134 | } |
| 139 | 135 | ||
| 140 | static ssize_t | 136 | static ssize_t |
| @@ -152,16 +148,15 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, · 11 unmodified lines | |||
| 152 | { | 148 | { |
| 153 | struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); | 149 | struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); |
| 154 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 150 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 155 | int ret, hyst; | 151 | int hyst; |
| 156 | 152 | ||
| 157 | ret = kstrtoint(buf, 10, &hyst); | 153 | if (kstrtoint(buf, 10, &hyst) || hyst < 0) |
| 158 | if (ret || hyst < 0) | ||
| 159 | return -EINVAL; | 154 | return -EINVAL; |
| 160 | 155 | ||
| 161 | mutex_lock(&tz->lock); | 156 | guard(thermal_zone)(tz); |
| 162 | 157 | ||
| 163 | if (hyst == trip->hysteresis) | 158 | if (hyst == trip->hysteresis) |
| 164 | goto unlock; | 159 | return count; |
| 165 | 160 | ||
| 166 | /* | 161 | /* |
| 167 | * Allow the hysteresis to be updated when the temperature is invalid | 162 | * Allow the hysteresis to be updated when the temperature is invalid |
| @@ -171,22 +166,17 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, · 3 unmodified lines | |||
| 171 | */ | 166 | */ |
| 172 | if (trip->temperature == THERMAL_TEMP_INVALID) { | 167 | if (trip->temperature == THERMAL_TEMP_INVALID) { |
| 173 | WRITE_ONCE(trip->hysteresis, hyst); | 168 | WRITE_ONCE(trip->hysteresis, hyst); |
| 174 | goto unlock; | 169 | return count; |
| 175 | } | 170 | } |
| 176 | 171 | ||
| 177 | if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) { | 172 | if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) |
| 178 | ret = -EINVAL; | 173 | return -EINVAL; |
| 179 | goto unlock; | ||
| 180 | } | ||
| 181 | 174 | ||
| 182 | thermal_zone_set_trip_hyst(tz, trip, hyst); | 175 | thermal_zone_set_trip_hyst(tz, trip, hyst); |
| 183 | 176 | ||
| 184 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); | 177 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); |
| 185 | 178 | ||
| 186 | unlock: | 179 | return count; |
| 187 | mutex_unlock(&tz->lock); | ||
| 188 | |||
| 189 | return ret ? ret : count; | ||
| 190 | } | 180 | } |
| 191 | 181 | ||
| 192 | static ssize_t | 182 | static ssize_t |
| @@ -236,25 +226,26 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, · 43 unmodified lines | |||
| 236 | const char *buf, size_t count) | 226 | const char *buf, size_t count) |
| 237 | { | 227 | { |
| 238 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 228 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 239 | int ret = 0; | ||
| 240 | int temperature; | 229 | int temperature; |
| 241 | 230 | ||
| 242 | if (kstrtoint(buf, 10, &temperature)) | 231 | if (kstrtoint(buf, 10, &temperature)) |
| 243 | return -EINVAL; | 232 | return -EINVAL; |
| 244 | 233 | ||
| 245 | mutex_lock(&tz->lock); | 234 | guard(thermal_zone)(tz); |
| 246 | 235 | ||
| 247 | if (!tz->ops.set_emul_temp) | 236 | if (tz->ops.set_emul_temp) { |
| 248 | tz->emul_temperature = temperature; | 237 | int ret; |
| 249 | else | ||
| 250 | ret = tz->ops.set_emul_temp(tz, temperature); | ||
| 251 | 238 | ||
| 252 | if (!ret) | 239 | ret = tz->ops.set_emul_temp(tz, temperature); |
| 253 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | 240 | if (ret) |
| 241 | return ret; | ||
| 242 | } else { | ||
| 243 | tz->emul_temperature = temperature; | ||
| 244 | } | ||
| 254 | 245 | ||
| 255 | mutex_unlock(&tz->lock); | 246 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
| 256 | 247 | ||
| 257 | return ret ? ret : count; | 248 | return count; |
| 258 | } | 249 | } |
| 259 | static DEVICE_ATTR_WO(emul_temp); | 250 | static DEVICE_ATTR_WO(emul_temp); |
| 260 | #endif | 251 | #endif |
| @@ -553,14 +544,15 @@ cur_state_store(struct device *dev, struct device_attribute *attr, · 292 unmodified lines | |||
| 553 | if (state > cdev->max_state) | 544 | if (state > cdev->max_state) |
| 554 | return -EINVAL; | 545 | return -EINVAL; |
| 555 | 546 | ||
| 556 | mutex_lock(&cdev->lock); | 547 | guard(cooling_dev)(cdev); |
| 557 | 548 | ||
| 558 | result = cdev->ops->set_cur_state(cdev, state); | 549 | result = cdev->ops->set_cur_state(cdev, state); |
| 559 | if (!result) | 550 | if (result) |
| 560 | thermal_cooling_device_stats_update(cdev, state); | 551 | return result; |
| 561 | 552 | ||
| 562 | mutex_unlock(&cdev->lock); | 553 | thermal_cooling_device_stats_update(cdev, state); |
| 563 | return result ? result : count; | 554 | |
| 555 | return count; | ||
| 564 | } | 556 | } |
| 565 | 557 | ||
| 566 | static struct device_attribute | 558 | static struct device_attribute |
| @@ -634,21 +626,18 @@ static ssize_t total_trans_show(struct device *dev, · 67 unmodified lines | |||
| 634 | { | 626 | { |
| 635 | struct thermal_cooling_device *cdev = to_cooling_device(dev); | 627 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
| 636 | struct cooling_dev_stats *stats; | 628 | struct cooling_dev_stats *stats; |
| 637 | int ret = 0; | 629 | int ret; |
| 638 | 630 | ||
| 639 | mutex_lock(&cdev->lock); | 631 | guard(cooling_dev)(cdev); |
| 640 | 632 | ||
| 641 | stats = cdev->stats; | 633 | stats = cdev->stats; |
| 642 | if (!stats) | 634 | if (!stats) |
| 643 | goto unlock; | 635 | return 0; |
| 644 | 636 | ||
| 645 | spin_lock(&stats->lock); | 637 | spin_lock(&stats->lock); |
| 646 | ret = sprintf(buf, "%u\n", stats->total_trans); | 638 | ret = sprintf(buf, "%u\n", stats->total_trans); |
| 647 | spin_unlock(&stats->lock); | 639 | spin_unlock(&stats->lock); |
| 648 | 640 | ||
| 649 | unlock: | ||
| 650 | mutex_unlock(&cdev->lock); | ||
| 651 | |||
| 652 | return ret; | 641 | return ret; |
| 653 | } | 642 | } |
| 654 | 643 | ||
| @@ -661,11 +650,11 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, · 6 unmodified lines | |||
| 661 | ssize_t len = 0; | 650 | ssize_t len = 0; |
| 662 | int i; | 651 | int i; |
| 663 | 652 | ||
| 664 | mutex_lock(&cdev->lock); | 653 | guard(cooling_dev)(cdev); |
| 665 | 654 | ||
| 666 | stats = cdev->stats; | 655 | stats = cdev->stats; |
| 667 | if (!stats) | 656 | if (!stats) |
| 668 | goto unlock; | 657 | return 0; |
| 669 | 658 | ||
| 670 | spin_lock(&stats->lock); | 659 | spin_lock(&stats->lock); |
| 671 | 660 | ||
| @@ -677,9 +666,6 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, · 5 unmodified lines | |||
| 677 | } | 666 | } |
| 678 | spin_unlock(&stats->lock); | 667 | spin_unlock(&stats->lock); |
| 679 | 668 | ||
| 680 | unlock: | ||
| 681 | mutex_unlock(&cdev->lock); | ||
| 682 | |||
| 683 | return len; | 669 | return len; |
| 684 | } | 670 | } |
| 685 | 671 | ||
| @@ -691,11 +677,11 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, · 5 unmodified lines | |||
| 691 | struct cooling_dev_stats *stats; | 677 | struct cooling_dev_stats *stats; |
| 692 | int i, states; | 678 | int i, states; |
| 693 | 679 | ||
| 694 | mutex_lock(&cdev->lock); | 680 | guard(cooling_dev)(cdev); |
| 695 | 681 | ||
| 696 | stats = cdev->stats; | 682 | stats = cdev->stats; |
| 697 | if (!stats) | 683 | if (!stats) |
| 698 | goto unlock; | 684 | return count; |
| 699 | 685 | ||
| 700 | states = cdev->max_state + 1; | 686 | states = cdev->max_state + 1; |
| 701 | 687 | ||
| @@ -711,9 +697,6 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, · 9 unmodified lines | |||
| 711 | 697 | ||
| 712 | spin_unlock(&stats->lock); | 698 | spin_unlock(&stats->lock); |
| 713 | 699 | ||
| 714 | unlock: | ||
| 715 | mutex_unlock(&cdev->lock); | ||
| 716 | |||
| 717 | return count; | 700 | return count; |
| 718 | } | 701 | } |
| 719 | 702 | ||
| @@ -725,13 +708,11 @@ static ssize_t trans_table_show(struct device *dev, · 5 unmodified lines | |||
| 725 | ssize_t len = 0; | 708 | ssize_t len = 0; |
| 726 | int i, j; | 709 | int i, j; |
| 727 | 710 | ||
| 728 | mutex_lock(&cdev->lock); | 711 | guard(cooling_dev)(cdev); |
| 729 | 712 | ||
| 730 | stats = cdev->stats; | 713 | stats = cdev->stats; |
| 731 | if (!stats) { | 714 | if (!stats) |
| 732 | len = -ENODATA; | 715 | return -ENODATA; |
| 733 | goto unlock; | ||
| 734 | } | ||
| 735 | 716 | ||
| 736 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); | 717 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); |
| 737 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); | 718 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); |
| @@ -740,10 +721,8 @@ static ssize_t trans_table_show(struct device *dev, · 2 unmodified lines | |||
| 740 | break; | 721 | break; |
| 741 | len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); | 722 | len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); |
| 742 | } | 723 | } |
| 743 | if (len >= PAGE_SIZE) { | 724 | if (len >= PAGE_SIZE) |
| 744 | len = PAGE_SIZE; | 725 | return PAGE_SIZE; |
| 745 | goto unlock; | ||
| 746 | } | ||
| 747 | 726 | ||
| 748 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); | 727 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| 749 | 728 | ||
| @@ -769,9 +748,6 @@ static ssize_t trans_table_show(struct device *dev, · 19 unmodified lines | |||
| 769 | len = -EFBIG; | 748 | len = -EFBIG; |
| 770 | } | 749 | } |
| 771 | 750 | ||
| 772 | unlock: | ||
| 773 | mutex_unlock(&cdev->lock); | ||
| 774 | |||
| 775 | return len; | 751 | return len; |
| 776 | } | 752 | } |
| 777 | 753 | ||
| @@ -894,13 +870,11 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr, · 116 unmodified lines | |||
| 894 | instance = container_of(attr, struct thermal_instance, weight_attr); | 870 | instance = container_of(attr, struct thermal_instance, weight_attr); |
| 895 | 871 | ||
| 896 | /* Don't race with governors using the 'weight' value */ | 872 | /* Don't race with governors using the 'weight' value */ |
| 897 | mutex_lock(&tz->lock); | 873 | guard(thermal_zone)(tz); |
| 898 | 874 | ||
| 899 | instance->weight = weight; | 875 | instance->weight = weight; |
| 900 | 876 | ||
| 901 | thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); | 877 | thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); |
| 902 | 878 | ||
| 903 | mutex_unlock(&tz->lock); | ||
| 904 | |||
| 905 | return count; | 879 | return count; |
| 906 | } | 880 | } |
| @@ -0,0 +1,240 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2024 Linaro Limited | ||
| 4 | * | ||
| 5 | * Author: Daniel Lezcano <daniel.lezcano@linaro.org> | ||
| 6 | * | ||
| 7 | * Thermal thresholds | ||
| 8 | */ | ||
| 9 | #include <linux/list.h> | ||
| 10 | #include <linux/list_sort.h> | ||
| 11 | #include <linux/slab.h> | ||
| 12 | |||
| 13 | #include "thermal_core.h" | ||
| 14 | #include "thermal_thresholds.h" | ||
| 15 | |||
| 16 | int thermal_thresholds_init(struct thermal_zone_device *tz) | ||
| 17 | { | ||
| 18 | INIT_LIST_HEAD(&tz->user_thresholds); | ||
| 19 | |||
| 20 | return 0; | ||
| 21 | } | ||
| 22 | |||
| 23 | static void __thermal_thresholds_flush(struct thermal_zone_device *tz) | ||
| 24 | { | ||
| 25 | struct list_head *thresholds = &tz->user_thresholds; | ||
| 26 | struct user_threshold *entry, *tmp; | ||
| 27 | |||
| 28 | list_for_each_entry_safe(entry, tmp, thresholds, list_node) { | ||
| 29 | list_del(&entry->list_node); | ||
| 30 | kfree(entry); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void thermal_thresholds_flush(struct thermal_zone_device *tz) | ||
| 35 | { | ||
| 36 | lockdep_assert_held(&tz->lock); | ||
| 37 | |||
| 38 | __thermal_thresholds_flush(tz); | ||
| 39 | |||
| 40 | thermal_notify_threshold_flush(tz); | ||
| 41 | |||
| 42 | __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS); | ||
| 43 | } | ||
| 44 | |||
| 45 | void thermal_thresholds_exit(struct thermal_zone_device *tz) | ||
| 46 | { | ||
| 47 | __thermal_thresholds_flush(tz); | ||
| 48 | } | ||
| 49 | |||
| 50 | static int __thermal_thresholds_cmp(void *data, | ||
| 51 | const struct list_head *l1, | ||
| 52 | const struct list_head *l2) | ||
| 53 | { | ||
| 54 | struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node); | ||
| 55 | struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node); | ||
| 56 | |||
| 57 | return t1->temperature - t2->temperature; | ||
| 58 | } | ||
| 59 | |||
| 60 | static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds, | ||
| 61 | int temperature) | ||
| 62 | { | ||
| 63 | struct user_threshold *t; | ||
| 64 | |||
| 65 | list_for_each_entry(t, thresholds, list_node) | ||
| 66 | if (t->temperature == temperature) | ||
| 67 | return t; | ||
| 68 | |||
| 69 | return NULL; | ||
| 70 | } | ||
| 71 | |||
| 72 | static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature, | ||
| 73 | int last_temperature, int direction, | ||
| 74 | int *low, int *high) | ||
| 75 | { | ||
| 76 | |||
| 77 | if (temperature >= threshold->temperature) { | ||
| 78 | if (threshold->temperature > *low && | ||
| 79 | THERMAL_THRESHOLD_WAY_DOWN & threshold->direction) | ||
| 80 | *low = threshold->temperature; | ||
| 81 | |||
| 82 | if (last_temperature < threshold->temperature && | ||
| 83 | threshold->direction & direction) | ||
| 84 | return true; | ||
| 85 | } else { | ||
| 86 | if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP | ||
| 87 | & threshold->direction) | ||
| 88 | *high = threshold->temperature; | ||
| 89 | |||
| 90 | if (last_temperature >= threshold->temperature && | ||
| 91 | threshold->direction & direction) | ||
| 92 | return true; | ||
| 93 | } | ||
| 94 | |||
| 95 | return false; | ||
| 96 | } | ||
| 97 | |||
| 98 | static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature, | ||
| 99 | int last_temperature, int *low, int *high) | ||
| 100 | { | ||
| 101 | struct user_threshold *t; | ||
| 102 | |||
| 103 | list_for_each_entry(t, thresholds, list_node) { | ||
| 104 | if (__thermal_threshold_is_crossed(t, temperature, last_temperature, | ||
| 105 | THERMAL_THRESHOLD_WAY_UP, low, high)) | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | return false; | ||
| 110 | } | ||
| 111 | |||
| 112 | static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature, | ||
| 113 | int last_temperature, int *low, int *high) | ||
| 114 | { | ||
| 115 | struct user_threshold *t; | ||
| 116 | |||
| 117 | list_for_each_entry_reverse(t, thresholds, list_node) { | ||
| 118 | if (__thermal_threshold_is_crossed(t, temperature, last_temperature, | ||
| 119 | THERMAL_THRESHOLD_WAY_DOWN, low, high)) | ||
| 120 | return true; | ||
| 121 | } | ||
| 122 | |||
| 123 | return false; | ||
| 124 | } | ||
| 125 | |||
| 126 | void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high) | ||
| 127 | { | ||
| 128 | struct list_head *thresholds = &tz->user_thresholds; | ||
| 129 | |||
| 130 | int temperature = tz->temperature; | ||
| 131 | int last_temperature = tz->last_temperature; | ||
| 132 | |||
| 133 | lockdep_assert_held(&tz->lock); | ||
| 134 | |||
| 135 | /* | ||
| 136 | * We need a second update in order to detect a threshold being crossed | ||
| 137 | */ | ||
| 138 | if (last_temperature == THERMAL_TEMP_INVALID) | ||
| 139 | return; | ||
| 140 | |||
| 141 | /* | ||
| 142 | * The temperature is stable, so obviously we can not have | ||
| 143 | * crossed a threshold. | ||
| 144 | */ | ||
| 145 | if (last_temperature == temperature) | ||
| 146 | return; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Since last update the temperature: | ||
| 150 | * - increased : thresholds are crossed the way up | ||
| 151 | * - decreased : thresholds are crossed the way down | ||
| 152 | */ | ||
| 153 | if (temperature > last_temperature) { | ||
| 154 | if (thermal_thresholds_handle_raising(thresholds, temperature, | ||
| 155 | last_temperature, low, high)) | ||
| 156 | thermal_notify_threshold_up(tz); | ||
| 157 | } else { | ||
| 158 | if (thermal_thresholds_handle_dropping(thresholds, temperature, | ||
| 159 | last_temperature, low, high)) | ||
| 160 | thermal_notify_threshold_down(tz); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | int thermal_thresholds_add(struct thermal_zone_device *tz, | ||
| 165 | int temperature, int direction) | ||
| 166 | { | ||
| 167 | struct list_head *thresholds = &tz->user_thresholds; | ||
| 168 | struct user_threshold *t; | ||
| 169 | |||
| 170 | lockdep_assert_held(&tz->lock); | ||
| 171 | |||
| 172 | t = __thermal_thresholds_find(thresholds, temperature); | ||
| 173 | if (t) { | ||
| 174 | if (t->direction == direction) | ||
| 175 | return -EEXIST; | ||
| 176 | |||
| 177 | t->direction |= direction; | ||
| 178 | } else { | ||
| 179 | |||
| 180 | t = kmalloc(sizeof(*t), GFP_KERNEL); | ||
| 181 | if (!t) | ||
| 182 | return -ENOMEM; | ||
| 183 | |||
| 184 | INIT_LIST_HEAD(&t->list_node); | ||
| 185 | t->temperature = temperature; | ||
| 186 | t->direction = direction; | ||
| 187 | list_add(&t->list_node, thresholds); | ||
| 188 | list_sort(NULL, thresholds, __thermal_thresholds_cmp); | ||
| 189 | } | ||
| 190 | |||
| 191 | thermal_notify_threshold_add(tz, temperature, direction); | ||
| 192 | |||
| 193 | __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD); | ||
| 194 | |||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | int thermal_thresholds_delete(struct thermal_zone_device *tz, | ||
| 199 | int temperature, int direction) | ||
| 200 | { | ||
| 201 | struct list_head *thresholds = &tz->user_thresholds; | ||
| 202 | struct user_threshold *t; | ||
| 203 | |||
| 204 | lockdep_assert_held(&tz->lock); | ||
| 205 | |||
| 206 | t = __thermal_thresholds_find(thresholds, temperature); | ||
| 207 | if (!t) | ||
| 208 | return -ENOENT; | ||
| 209 | |||
| 210 | if (t->direction == direction) { | ||
| 211 | list_del(&t->list_node); | ||
| 212 | kfree(t); | ||
| 213 | } else { | ||
| 214 | t->direction &= ~direction; | ||
| 215 | } | ||
| 216 | |||
| 217 | thermal_notify_threshold_delete(tz, temperature, direction); | ||
| 218 | |||
| 219 | __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD); | ||
| 220 | |||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | int thermal_thresholds_for_each(struct thermal_zone_device *tz, | ||
| 225 | int (*cb)(struct user_threshold *, void *arg), void *arg) | ||
| 226 | { | ||
| 227 | struct list_head *thresholds = &tz->user_thresholds; | ||
| 228 | struct user_threshold *entry; | ||
| 229 | int ret; | ||
| 230 | |||
| 231 | guard(thermal_zone)(tz); | ||
| 232 | |||
| 233 | list_for_each_entry(entry, thresholds, list_node) { | ||
| 234 | ret = cb(entry, arg); | ||
| 235 | if (ret) | ||
| 236 | return ret; | ||
| 237 | } | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef __THERMAL_THRESHOLDS_H__ | ||
| 3 | #define __THERMAL_THRESHOLDS_H__ | ||
| 4 | |||
| 5 | struct user_threshold { | ||
| 6 | struct list_head list_node; | ||
| 7 | int temperature; | ||
| 8 | int direction; | ||
| 9 | }; | ||
| 10 | |||
| 11 | int thermal_thresholds_init(struct thermal_zone_device *tz); | ||
| 12 | void thermal_thresholds_exit(struct thermal_zone_device *tz); | ||
| 13 | void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high); | ||
| 14 | void thermal_thresholds_flush(struct thermal_zone_device *tz); | ||
| 15 | int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction); | ||
| 16 | int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction); | ||
| 17 | int thermal_thresholds_for_each(struct thermal_zone_device *tz, | ||
| 18 | int (*cb)(struct user_threshold *, void *arg), void *arg); | ||
| 19 | #endif | ||
| @@ -45,13 +45,9 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz, · 44 unmodified lines | |||
| 45 | int (*cb)(struct thermal_trip *, void *), | 45 | int (*cb)(struct thermal_trip *, void *), |
| 46 | void *data) | 46 | void *data) |
| 47 | { | 47 | { |
| 48 | int ret; | 48 | guard(thermal_zone)(tz); |
| 49 | |||
| 50 | mutex_lock(&tz->lock); | ||
| 51 | ret = for_each_thermal_trip(tz, cb, data); | ||
| 52 | mutex_unlock(&tz->lock); | ||
| 53 | 49 | ||
| 54 | return ret; | 50 | return for_each_thermal_trip(tz, cb, data); |
| 55 | } | 51 | } |
| 56 | EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); | 52 | EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); |
| 57 | 53 | ||
| @@ -92,43 +88,3 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, · 34 unmodified lines | |||
| 92 | */ | 88 | */ |
| 93 | return trip_to_trip_desc(trip) - tz->trips; | 89 | return trip_to_trip_desc(trip) - tz->trips; |
| 94 | } | 90 | } |
| 95 | |||
| 96 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, | ||
| 97 | struct thermal_trip *trip, int hyst) | ||
| 98 | { | ||
| 99 | WRITE_ONCE(trip->hysteresis, hyst); | ||
| 100 | thermal_notify_tz_trip_change(tz, trip); | ||
| 101 | } | ||
| 102 | |||
| 103 | void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, | ||
| 104 | struct thermal_trip *trip, int temp) | ||
| 105 | { | ||
| 106 | if (trip->temperature == temp) | ||
| 107 | return; | ||
| 108 | |||
| 109 | WRITE_ONCE(trip->temperature, temp); | ||
| 110 | thermal_notify_tz_trip_change(tz, trip); | ||
| 111 | |||
| 112 | if (temp == THERMAL_TEMP_INVALID) { | ||
| 113 | struct thermal_trip_desc *td = trip_to_trip_desc(trip); | ||
| 114 | |||
| 115 | if (tz->temperature >= td->threshold) { | ||
| 116 | /* | ||
| 117 | * The trip has been crossed on the way up, so some | ||
| 118 | * adjustments are needed to compensate for the lack | ||
| 119 | * of it going forward. | ||
| 120 | */ | ||
| 121 | if (trip->type == THERMAL_TRIP_PASSIVE) { | ||
| 122 | tz->passive--; | ||
| 123 | WARN_ON_ONCE(tz->passive < 0); | ||
| 124 | } | ||
| 125 | thermal_zone_trip_down(tz, trip); | ||
| 126 | } | ||
| 127 | /* | ||
| 128 | * Invalidate the threshold to avoid triggering a spurious | ||
| 129 | * trip crossing notification when the trip becomes valid. | ||
| 130 | */ | ||
| 131 | td->threshold = INT_MAX; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); | ||
| @@ -56,6 +56,9 @@ enum thermal_notify_event { · 55 unmodified lines | |||
| 56 | THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */ | 56 | THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */ |
| 57 | THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */ | 57 | THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */ |
| 58 | THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */ | 58 | THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */ |
| 59 | THERMAL_TZ_ADD_THRESHOLD, /* Threshold added */ | ||
| 60 | THERMAL_TZ_DEL_THRESHOLD, /* Threshold deleted */ | ||
| 61 | THERMAL_TZ_FLUSH_THRESHOLDS, /* All thresholds deleted */ | ||
| 59 | }; | 62 | }; |
| 60 | 63 | ||
| 61 | /** | 64 | /** |
| @@ -137,6 +140,9 @@ struct thermal_cooling_device { · 75 unmodified lines | |||
| 137 | #endif | 140 | #endif |
| 138 | }; | 141 | }; |
| 139 | 142 | ||
| 143 | DEFINE_GUARD(cooling_dev, struct thermal_cooling_device *, mutex_lock(&_T->lock), | ||
| 144 | mutex_unlock(&_T->lock)) | ||
| 145 | |||
| 140 | /* Structure to define Thermal Zone parameters */ | 146 | /* Structure to define Thermal Zone parameters */ |
| 141 | struct thermal_zone_params { | 147 | struct thermal_zone_params { |
| 142 | const char *governor_name; | 148 | const char *governor_name; |
| @@ -3,6 +3,8 @@ · 2 unmodified lines | |||
| 3 | #define _UAPI_LINUX_THERMAL_H | 3 | #define _UAPI_LINUX_THERMAL_H |
| 4 | 4 | ||
| 5 | #define THERMAL_NAME_LENGTH 20 | 5 | #define THERMAL_NAME_LENGTH 20 |
| 6 | #define THERMAL_THRESHOLD_WAY_UP BIT(0) | ||
| 7 | #define THERMAL_THRESHOLD_WAY_DOWN BIT(1) | ||
| 6 | 8 | ||
| 7 | enum thermal_device_mode { | 9 | enum thermal_device_mode { |
| 8 | THERMAL_DEVICE_DISABLED = 0, | 10 | THERMAL_DEVICE_DISABLED = 0, |
| @@ -18,7 +20,7 @@ enum thermal_trip_type { · 9 unmodified lines | |||
| 18 | 20 | ||
| 19 | /* Adding event notification support elements */ | 21 | /* Adding event notification support elements */ |
| 20 | #define THERMAL_GENL_FAMILY_NAME "thermal" | 22 | #define THERMAL_GENL_FAMILY_NAME "thermal" |
| 21 | #define THERMAL_GENL_VERSION 0x01 | 23 | #define THERMAL_GENL_VERSION 0x02 |
| 22 | #define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" | 24 | #define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" |
| 23 | #define THERMAL_GENL_EVENT_GROUP_NAME "event" | 25 | #define THERMAL_GENL_EVENT_GROUP_NAME "event" |
| 24 | 26 | ||
| @@ -28,6 +30,7 @@ enum thermal_genl_attr { · 3 unmodified lines | |||
| 28 | THERMAL_GENL_ATTR_TZ, | 30 | THERMAL_GENL_ATTR_TZ, |
| 29 | THERMAL_GENL_ATTR_TZ_ID, | 31 | THERMAL_GENL_ATTR_TZ_ID, |
| 30 | THERMAL_GENL_ATTR_TZ_TEMP, | 32 | THERMAL_GENL_ATTR_TZ_TEMP, |
| 33 | THERMAL_GENL_ATTR_TZ_PREV_TEMP, | ||
| 31 | THERMAL_GENL_ATTR_TZ_TRIP, | 34 | THERMAL_GENL_ATTR_TZ_TRIP, |
| 32 | THERMAL_GENL_ATTR_TZ_TRIP_ID, | 35 | THERMAL_GENL_ATTR_TZ_TRIP_ID, |
| 33 | THERMAL_GENL_ATTR_TZ_TRIP_TYPE, | 36 | THERMAL_GENL_ATTR_TZ_TRIP_TYPE, |
| @@ -48,6 +51,9 @@ enum thermal_genl_attr { · 14 unmodified lines | |||
| 48 | THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, | 51 | THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, |
| 49 | THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, | 52 | THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, |
| 50 | THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, | 53 | THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, |
| 54 | THERMAL_GENL_ATTR_THRESHOLD, | ||
| 55 | THERMAL_GENL_ATTR_THRESHOLD_TEMP, | ||
| 56 | THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, | ||
| 51 | __THERMAL_GENL_ATTR_MAX, | 57 | __THERMAL_GENL_ATTR_MAX, |
| 52 | }; | 58 | }; |
| 53 | #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) | 59 | #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) |
| @@ -75,6 +81,11 @@ enum thermal_genl_event { · 21 unmodified lines | |||
| 75 | THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ | 81 | THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ |
| 76 | THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ | 82 | THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ |
| 77 | THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */ | 83 | THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */ |
| 84 | THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A thresold has been added */ | ||
| 85 | THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A thresold has been deleted */ | ||
| 86 | THERMAL_GENL_EVENT_THRESHOLD_FLUSH, /* All thresolds have been deleted */ | ||
| 87 | THERMAL_GENL_EVENT_THRESHOLD_UP, /* A thresold has been crossed the way up */ | ||
| 88 | THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A thresold has been crossed the way down */ | ||
| 78 | __THERMAL_GENL_EVENT_MAX, | 89 | __THERMAL_GENL_EVENT_MAX, |
| 79 | }; | 90 | }; |
| 80 | #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) | 91 | #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) |
| @@ -82,12 +93,16 @@ enum thermal_genl_event { · 1 unmodified line | |||
| 82 | /* Commands supported by the thermal_genl_family */ | 93 | /* Commands supported by the thermal_genl_family */ |
| 83 | enum thermal_genl_cmd { | 94 | enum thermal_genl_cmd { |
| 84 | THERMAL_GENL_CMD_UNSPEC, | 95 | THERMAL_GENL_CMD_UNSPEC, |
| 85 | THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ | 96 | THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ |
| 86 | THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ | 97 | THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ |
| 87 | THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ | 98 | THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ |
| 88 | THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ | 99 | THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ |
| 89 | THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ | 100 | THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ |
| 90 | THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ | 101 | THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ |
| 102 | THERMAL_GENL_CMD_THRESHOLD_GET, /* List of thresholds */ | ||
| 103 | THERMAL_GENL_CMD_THRESHOLD_ADD, /* Add a threshold */ | ||
| 104 | THERMAL_GENL_CMD_THRESHOLD_DELETE, /* Delete a threshold */ | ||
| 105 | THERMAL_GENL_CMD_THRESHOLD_FLUSH, /* Flush all the thresholds */ | ||
| 91 | __THERMAL_GENL_CMD_MAX, | 106 | __THERMAL_GENL_CMD_MAX, |
| 92 | }; | 107 | }; |
| 93 | #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) | 108 | #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
| 6 | #include <stdlib.h> | 6 | #include <stdlib.h> |
| 7 | #include <unistd.h> | 7 | #include <unistd.h> |
| 8 | #include <limits.h> | ||
| 8 | 9 | ||
| 9 | #include <thermal.h> | 10 | #include <thermal.h> |
| 10 | #include "thermal_nl.h" | 11 | #include "thermal_nl.h" |
| @@ -33,6 +34,11 @@ static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { · 22 unmodified lines | |||
| 33 | [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, | 34 | [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, |
| 34 | [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, | 35 | [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, |
| 35 | [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, | 36 | [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, |
| 37 | |||
| 38 | /* Thresholds */ | ||
| 39 | [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, | ||
| 40 | [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, | ||
| 41 | [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, | ||
| 36 | }; | 42 | }; |
| 37 | 43 | ||
| 38 | static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) | 44 | static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) |
| @@ -182,6 +188,48 @@ static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz) · 143 unmodified lines | |||
| 182 | return THERMAL_SUCCESS; | 188 | return THERMAL_SUCCESS; |
| 183 | } | 189 | } |
| 184 | 190 | ||
| 191 | static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz) | ||
| 192 | { | ||
| 193 | struct nlattr *attr; | ||
| 194 | struct thermal_threshold *__tt = NULL; | ||
| 195 | size_t size = 0; | ||
| 196 | int rem; | ||
| 197 | |||
| 198 | /* | ||
| 199 | * The size contains the size of the array and we want to | ||
| 200 | * access the last element, size - 1. | ||
| 201 | * | ||
| 202 | * The variable size is initialized to zero but it will be | ||
| 203 | * then incremented by the first if() statement. The message | ||
| 204 | * attributes are ordered, so the first if() statement will be | ||
| 205 | * always called before the second one. If it happens that is | ||
| 206 | * not the case, then it is a kernel bug. | ||
| 207 | */ | ||
| 208 | nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) { | ||
| 209 | |||
| 210 | if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) { | ||
| 211 | |||
| 212 | size++; | ||
| 213 | |||
| 214 | __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); | ||
| 215 | if (!__tt) | ||
| 216 | return THERMAL_ERROR; | ||
| 217 | |||
| 218 | __tt[size - 1].temperature = nla_get_u32(attr); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION) | ||
| 222 | __tt[size - 1].direction = nla_get_u32(attr); | ||
| 223 | } | ||
| 224 | |||
| 225 | if (__tt) | ||
| 226 | __tt[size].temperature = INT_MAX; | ||
| 227 | |||
| 228 | tz->thresholds = __tt; | ||
| 229 | |||
| 230 | return THERMAL_SUCCESS; | ||
| 231 | } | ||
| 232 | |||
| 185 | static int handle_netlink(struct nl_cache_ops *unused, | 233 | static int handle_netlink(struct nl_cache_ops *unused, |
| 186 | struct genl_cmd *cmd, | 234 | struct genl_cmd *cmd, |
| 187 | struct genl_info *info, void *arg) | 235 | struct genl_info *info, void *arg) |
| @@ -210,6 +258,10 @@ static int handle_netlink(struct nl_cache_ops *unused, · 22 unmodified lines | |||
| 210 | ret = parse_tz_get_gov(info, arg); | 258 | ret = parse_tz_get_gov(info, arg); |
| 211 | break; | 259 | break; |
| 212 | 260 | ||
| 261 | case THERMAL_GENL_CMD_THRESHOLD_GET: | ||
| 262 | ret = parse_threshold_get(info, arg); | ||
| 263 | break; | ||
| 264 | |||
| 213 | default: | 265 | default: |
| 214 | return THERMAL_ERROR; | 266 | return THERMAL_ERROR; |
| 215 | } | 267 | } |
| @@ -253,6 +305,34 @@ static struct genl_cmd thermal_cmds[] = { · 37 unmodified lines | |||
| 253 | .c_maxattr = THERMAL_GENL_ATTR_MAX, | 305 | .c_maxattr = THERMAL_GENL_ATTR_MAX, |
| 254 | .c_attr_policy = thermal_genl_policy, | 306 | .c_attr_policy = thermal_genl_policy, |
| 255 | }, | 307 | }, |
| 308 | { | ||
| 309 | .c_id = THERMAL_GENL_CMD_THRESHOLD_GET, | ||
| 310 | .c_name = (char *)"Get thresholds list", | ||
| 311 | .c_msg_parser = handle_netlink, | ||
| 312 | .c_maxattr = THERMAL_GENL_ATTR_MAX, | ||
| 313 | .c_attr_policy = thermal_genl_policy, | ||
| 314 | }, | ||
| 315 | { | ||
| 316 | .c_id = THERMAL_GENL_CMD_THRESHOLD_ADD, | ||
| 317 | .c_name = (char *)"Add a threshold", | ||
| 318 | .c_msg_parser = handle_netlink, | ||
| 319 | .c_maxattr = THERMAL_GENL_ATTR_MAX, | ||
| 320 | .c_attr_policy = thermal_genl_policy, | ||
| 321 | }, | ||
| 322 | { | ||
| 323 | .c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE, | ||
| 324 | .c_name = (char *)"Delete a threshold", | ||
| 325 | .c_msg_parser = handle_netlink, | ||
| 326 | .c_maxattr = THERMAL_GENL_ATTR_MAX, | ||
| 327 | .c_attr_policy = thermal_genl_policy, | ||
| 328 | }, | ||
| 329 | { | ||
| 330 | .c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH, | ||
| 331 | .c_name = (char *)"Flush the thresholds", | ||
| 332 | .c_msg_parser = handle_netlink, | ||
| 333 | .c_maxattr = THERMAL_GENL_ATTR_MAX, | ||
| 334 | .c_attr_policy = thermal_genl_policy, | ||
| 335 | }, | ||
| 256 | }; | 336 | }; |
| 257 | 337 | ||
| 258 | static struct genl_ops thermal_cmd_ops = { | 338 | static struct genl_ops thermal_cmd_ops = { |
| @@ -261,9 +341,41 @@ static struct genl_ops thermal_cmd_ops = { · 2 unmodified lines | |||
| 261 | .o_ncmds = ARRAY_SIZE(thermal_cmds), | 341 | .o_ncmds = ARRAY_SIZE(thermal_cmds), |
| 262 | }; | 342 | }; |
| 263 | 343 | ||
| 264 | static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd, | 344 | struct cmd_param { |
| 265 | int flags, void *arg) | 345 | int tz_id; |
| 346 | int temp; | ||
| 347 | int direction; | ||
| 348 | }; | ||
| 349 | |||
| 350 | typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *); | ||
| 351 | |||
| 352 | static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p) | ||
| 266 | { | 353 | { |
| 354 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) | ||
| 355 | return -1; | ||
| 356 | |||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p) | ||
| 361 | { | ||
| 362 | if (thermal_genl_tz_id_encode(msg, p)) | ||
| 363 | return -1; | ||
| 364 | |||
| 365 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp)) | ||
| 366 | return -1; | ||
| 367 | |||
| 368 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) | ||
| 369 | return -1; | ||
| 370 | |||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb, | ||
| 375 | struct cmd_param *param, | ||
| 376 | int cmd, int flags, void *arg) | ||
| 377 | { | ||
| 378 | thermal_error_t ret = THERMAL_ERROR; | ||
| 267 | struct nl_msg *msg; | 379 | struct nl_msg *msg; |
| 268 | void *hdr; | 380 | void *hdr; |
| 269 | 381 | ||
| @@ -274,45 +386,95 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int · 4 unmodified lines | |||
| 274 | hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, | 386 | hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, |
| 275 | 0, flags, cmd, THERMAL_GENL_VERSION); | 387 | 0, flags, cmd, THERMAL_GENL_VERSION); |
| 276 | if (!hdr) | 388 | if (!hdr) |
| 277 | return THERMAL_ERROR; | 389 | goto out; |
| 278 | 390 | ||
| 279 | if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id)) | 391 | if (cmd_cb && cmd_cb(msg, param)) |
| 280 | return THERMAL_ERROR; | 392 | goto out; |
| 281 | 393 | ||
| 282 | if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) | 394 | if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) |
| 283 | return THERMAL_ERROR; | 395 | goto out; |
| 284 | 396 | ||
| 397 | ret = THERMAL_SUCCESS; | ||
| 398 | out: | ||
| 285 | nlmsg_free(msg); | 399 | nlmsg_free(msg); |
| 286 | 400 | ||
| 287 | return THERMAL_SUCCESS; | 401 | return ret; |
| 288 | } | 402 | } |
| 289 | 403 | ||
| 290 | thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) | 404 | thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) |
| 291 | { | 405 | { |
| 292 | return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID, | 406 | return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID, |
| 293 | NLM_F_DUMP | NLM_F_ACK, tz); | 407 | NLM_F_DUMP | NLM_F_ACK, tz); |
| 294 | } | 408 | } |
| 295 | 409 | ||
| 296 | thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) | 410 | thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) |
| 297 | { | 411 | { |
| 298 | return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, | 412 | return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET, |
| 299 | NLM_F_DUMP | NLM_F_ACK, tc); | 413 | NLM_F_DUMP | NLM_F_ACK, tc); |
| 300 | } | 414 | } |
| 301 | 415 | ||
| 302 | thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) | 416 | thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) |
| 303 | { | 417 | { |
| 304 | return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP, | 418 | struct cmd_param p = { .tz_id = tz->id }; |
| 305 | 0, tz); | 419 | |
| 420 | return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | ||
| 421 | THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz); | ||
| 306 | } | 422 | } |
| 307 | 423 | ||
| 308 | thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) | 424 | thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) |
| 309 | { | 425 | { |
| 310 | return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); | 426 | struct cmd_param p = { .tz_id = tz->id }; |
| 427 | |||
| 428 | return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | ||
| 429 | THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); | ||
| 311 | } | 430 | } |
| 312 | 431 | ||
| 313 | thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) | 432 | thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) |
| 314 | { | 433 | { |
| 315 | return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); | 434 | struct cmd_param p = { .tz_id = tz->id }; |
| 435 | |||
| 436 | return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | ||
| 437 | THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); | ||
| 438 | } | ||
| 439 | |||
| 440 | thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, | ||
| 441 | struct thermal_zone *tz) | ||
| 442 | { | ||
| 443 | struct cmd_param p = { .tz_id = tz->id }; | ||
| 444 | |||
| 445 | return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | ||
| 446 | THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz); | ||
| 447 | } | ||
| 448 | |||
| 449 | thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, | ||
| 450 | struct thermal_zone *tz, | ||
| 451 | int temperature, | ||
| 452 | int direction) | ||
| 453 | { | ||
| 454 | struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | ||
| 455 | |||
| 456 | return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, | ||
| 457 | THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz); | ||
| 458 | } | ||
| 459 | |||
| 460 | thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, | ||
| 461 | struct thermal_zone *tz, | ||
| 462 | int temperature, | ||
| 463 | int direction) | ||
| 464 | { | ||
| 465 | struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | ||
| 466 | |||
| 467 | return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, | ||
| 468 | THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz); | ||
| 469 | } | ||
| 470 | |||
| 471 | thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, | ||
| 472 | struct thermal_zone *tz) | ||
| 473 | { | ||
| 474 | struct cmd_param p = { .tz_id = tz->id }; | ||
| 475 | |||
| 476 | return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | ||
| 477 | THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz); | ||
| 316 | } | 478 | } |
| 317 | 479 | ||
| 318 | thermal_error_t thermal_cmd_exit(struct thermal_handler *th) | 480 | thermal_error_t thermal_cmd_exit(struct thermal_handler *th) |
| @@ -94,6 +94,30 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) · 93 unmodified lines | |||
| 94 | case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: | 94 | case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: |
| 95 | return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | 95 | return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), |
| 96 | nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); | 96 | nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); |
| 97 | |||
| 98 | case THERMAL_GENL_EVENT_THRESHOLD_ADD: | ||
| 99 | return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | ||
| 100 | nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), | ||
| 101 | nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); | ||
| 102 | |||
| 103 | case THERMAL_GENL_EVENT_THRESHOLD_DELETE: | ||
| 104 | return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | ||
| 105 | nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), | ||
| 106 | nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); | ||
| 107 | |||
| 108 | case THERMAL_GENL_EVENT_THRESHOLD_FLUSH: | ||
| 109 | return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); | ||
| 110 | |||
| 111 | case THERMAL_GENL_EVENT_THRESHOLD_UP: | ||
| 112 | return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | ||
| 113 | nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), | ||
| 114 | nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); | ||
| 115 | |||
| 116 | case THERMAL_GENL_EVENT_THRESHOLD_DOWN: | ||
| 117 | return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | ||
| 118 | nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), | ||
| 119 | nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); | ||
| 120 | |||
| 97 | default: | 121 | default: |
| 98 | return -1; | 122 | return -1; |
| 99 | } | 123 | } |
| @@ -101,19 +125,24 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) · 1 unmodified line | |||
| 101 | 125 | ||
| 102 | static void thermal_events_ops_init(struct thermal_events_ops *ops) | 126 | static void thermal_events_ops_init(struct thermal_events_ops *ops) |
| 103 | { | 127 | { |
| 104 | enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; | 128 | enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; |
| 105 | enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; | 129 | enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; |
| 106 | enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; | 130 | enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; |
| 107 | enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; | 131 | enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; |
| 108 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; | 132 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; |
| 109 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; | 133 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; |
| 110 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; | 134 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; |
| 111 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; | 135 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; |
| 112 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; | 136 | enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; |
| 113 | enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; | 137 | enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; |
| 114 | enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; | 138 | enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; |
| 115 | enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; | 139 | enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; |
| 116 | enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; | 140 | enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; |
| 141 | enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add; | ||
| 142 | enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete; | ||
| 143 | enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush; | ||
| 144 | enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up; | ||
| 145 | enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down; | ||
| 117 | } | 146 | } |
| 118 | 147 | ||
| 119 | thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg) | 148 | thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg) |
| @@ -4,11 +4,20 @@ · 3 unmodified lines | |||
| 4 | #define __LIBTHERMAL_H | 4 | #define __LIBTHERMAL_H |
| 5 | 5 | ||
| 6 | #include <linux/thermal.h> | 6 | #include <linux/thermal.h> |
| 7 | #include <sys/types.h> | ||
| 7 | 8 | ||
| 8 | #ifndef LIBTHERMAL_API | 9 | #ifndef LIBTHERMAL_API |
| 9 | #define LIBTHERMAL_API __attribute__((visibility("default"))) | 10 | #define LIBTHERMAL_API __attribute__((visibility("default"))) |
| 10 | #endif | 11 | #endif |
| 11 | 12 | ||
| 13 | #ifndef THERMAL_THRESHOLD_WAY_UP | ||
| 14 | #define THERMAL_THRESHOLD_WAY_UP 0x1 | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #ifndef THERMAL_THRESHOLD_WAY_DOWN | ||
| 18 | #define THERMAL_THRESHOLD_WAY_DOWN 0x2 | ||
| 19 | #endif | ||
| 20 | |||
| 12 | #ifdef __cplusplus | 21 | #ifdef __cplusplus |
| 13 | extern "C" { | 22 | extern "C" { |
| 14 | #endif | 23 | #endif |
| @@ -31,6 +40,11 @@ struct thermal_events_ops { · 16 unmodified lines | |||
| 31 | int (*cdev_delete)(int cdev_id, void *arg); | 40 | int (*cdev_delete)(int cdev_id, void *arg); |
| 32 | int (*cdev_update)(int cdev_id, int cur_state, void *arg); | 41 | int (*cdev_update)(int cdev_id, int cur_state, void *arg); |
| 33 | int (*gov_change)(int tz_id, const char *gov_name, void *arg); | 42 | int (*gov_change)(int tz_id, const char *gov_name, void *arg); |
| 43 | int (*threshold_add)(int tz_id, int temperature, int direction, void *arg); | ||
| 44 | int (*threshold_delete)(int tz_id, int temperature, int direction, void *arg); | ||
| 45 | int (*threshold_flush)(int tz_id, void *arg); | ||
| 46 | int (*threshold_up)(int tz_id, int temp, int prev_temp, void *arg); | ||
| 47 | int (*threshold_down)(int tz_id, int temp, int prev_temp, void *arg); | ||
| 34 | }; | 48 | }; |
| 35 | 49 | ||
| 36 | struct thermal_ops { | 50 | struct thermal_ops { |
| @@ -45,12 +59,18 @@ struct thermal_trip { · 8 unmodified lines | |||
| 45 | int hyst; | 59 | int hyst; |
| 46 | }; | 60 | }; |
| 47 | 61 | ||
| 62 | struct thermal_threshold { | ||
| 63 | int temperature; | ||
| 64 | int direction; | ||
| 65 | }; | ||
| 66 | |||
| 48 | struct thermal_zone { | 67 | struct thermal_zone { |
| 49 | int id; | 68 | int id; |
| 50 | int temp; | 69 | int temp; |
| 51 | char name[THERMAL_NAME_LENGTH]; | 70 | char name[THERMAL_NAME_LENGTH]; |
| 52 | char governor[THERMAL_NAME_LENGTH]; | 71 | char governor[THERMAL_NAME_LENGTH]; |
| 53 | struct thermal_trip *trip; | 72 | struct thermal_trip *trip; |
| 73 | struct thermal_threshold *thresholds; | ||
| 54 | }; | 74 | }; |
| 55 | 75 | ||
| 56 | struct thermal_cdev { | 76 | struct thermal_cdev { |
| @@ -74,12 +94,16 @@ typedef int (*cb_tt_t)(struct thermal_trip *, void *); · 17 unmodified lines | |||
| 74 | 94 | ||
| 75 | typedef int (*cb_tc_t)(struct thermal_cdev *, void *); | 95 | typedef int (*cb_tc_t)(struct thermal_cdev *, void *); |
| 76 | 96 | ||
| 97 | typedef int (*cb_th_t)(struct thermal_threshold *, void *); | ||
| 98 | |||
| 77 | LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); | 99 | LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); |
| 78 | 100 | ||
| 79 | LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); | 101 | LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); |
| 80 | 102 | ||
| 81 | LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); | 103 | LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); |
| 82 | 104 | ||
| 105 | LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg); | ||
| 106 | |||
| 83 | LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, | 107 | LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, |
| 84 | const char *name); | 108 | const char *name); |
| 85 | 109 | ||
| @@ -124,6 +148,22 @@ LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler * · 38 unmodified lines | |||
| 124 | LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, | 148 | LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, |
| 125 | struct thermal_zone *tz); | 149 | struct thermal_zone *tz); |
| 126 | 150 | ||
| 151 | LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, | ||
| 152 | struct thermal_zone *tz); | ||
| 153 | |||
| 154 | LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, | ||
| 155 | struct thermal_zone *tz, | ||
| 156 | int temperature, | ||
| 157 | int direction); | ||
| 158 | |||
| 159 | LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, | ||
| 160 | struct thermal_zone *tz, | ||
| 161 | int temperature, | ||
| 162 | int direction); | ||
| 163 | |||
| 164 | LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, | ||
| 165 | struct thermal_zone *tz); | ||
| 166 | |||
| 127 | /* | 167 | /* |
| 128 | * Netlink thermal samples | 168 | * Netlink thermal samples |
| 129 | */ | 169 | */ |
| @@ -4,6 +4,7 @@ LIBTHERMAL_0.0.1 { · 3 unmodified lines | |||
| 4 | for_each_thermal_zone; | 4 | for_each_thermal_zone; |
| 5 | for_each_thermal_trip; | 5 | for_each_thermal_trip; |
| 6 | for_each_thermal_cdev; | 6 | for_each_thermal_cdev; |
| 7 | for_each_thermal_threshold; | ||
| 7 | thermal_zone_find_by_name; | 8 | thermal_zone_find_by_name; |
| 8 | thermal_zone_find_by_id; | 9 | thermal_zone_find_by_id; |
| 9 | thermal_zone_discover; | 10 | thermal_zone_discover; |
| @@ -17,6 +18,10 @@ LIBTHERMAL_0.0.1 { · 7 unmodified lines | |||
| 17 | thermal_cmd_get_trip; | 18 | thermal_cmd_get_trip; |
| 18 | thermal_cmd_get_governor; | 19 | thermal_cmd_get_governor; |
| 19 | thermal_cmd_get_temp; | 20 | thermal_cmd_get_temp; |
| 21 | thermal_cmd_threshold_get; | ||
| 22 | thermal_cmd_threshold_add; | ||
| 23 | thermal_cmd_threshold_delete; | ||
| 24 | thermal_cmd_threshold_flush; | ||
| 20 | thermal_sampling_init; | 25 | thermal_sampling_init; |
| 21 | thermal_sampling_handle; | 26 | thermal_sampling_handle; |
| 22 | thermal_sampling_fd; | 27 | thermal_sampling_fd; |
| @@ -1,10 +1,24 @@ | |||
| 1 | // SPDX-License-Identifier: LGPL-2.1+ | 1 | // SPDX-License-Identifier: LGPL-2.1+ |
| 2 | // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> | 2 | // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> |
| 3 | #include <stdio.h> | 3 | #include <stdio.h> |
| 4 | #include <limits.h> | ||
| 4 | #include <thermal.h> | 5 | #include <thermal.h> |
| 5 | 6 | ||
| 6 | #include "thermal_nl.h" | 7 | #include "thermal_nl.h" |
| 7 | 8 | ||
| 9 | int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg) | ||
| 10 | { | ||
| 11 | int i, ret = 0; | ||
| 12 | |||
| 13 | if (!th) | ||
| 14 | return 0; | ||
| 15 | |||
| 16 | for (i = 0; th[i].temperature != INT_MAX; i++) | ||
| 17 | ret |= cb(&th[i], arg); | ||
| 18 | |||
| 19 | return ret; | ||
| 20 | } | ||
| 21 | |||
| 8 | int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) | 22 | int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) |
| 9 | { | 23 | { |
| 10 | int i, ret = 0; | 24 | int i, ret = 0; |
| @@ -80,6 +94,9 @@ static int __thermal_zone_discover(struct thermal_zone *tz, void *th) · 69 unmodified lines | |||
| 80 | if (thermal_cmd_get_trip(th, tz) < 0) | 94 | if (thermal_cmd_get_trip(th, tz) < 0) |
| 81 | return -1; | 95 | return -1; |
| 82 | 96 | ||
| 97 | if (thermal_cmd_threshold_get(th, tz)) | ||
| 98 | return -1; | ||
| 99 | |||
| 83 | if (thermal_cmd_get_governor(th, tz)) | 100 | if (thermal_cmd_get_governor(th, tz)) |
| 84 | return -1; | 101 | return -1; |
| 85 | 102 | ||
| @@ -3,7 +3,7 @@ · 2 unmodified lines | |||
| 3 | 3 | ||
| 4 | LIBTHERMAL_TOOLS_VERSION = 0 | 4 | LIBTHERMAL_TOOLS_VERSION = 0 |
| 5 | LIBTHERMAL_TOOLS_PATCHLEVEL = 0 | 5 | LIBTHERMAL_TOOLS_PATCHLEVEL = 0 |
| 6 | LIBTHERMAL_TOOLS_EXTRAVERSION = 1 | 6 | LIBTHERMAL_TOOLS_EXTRAVERSION = 2 |
| 7 | 7 | ||
| 8 | MAKEFLAGS += --no-print-directory | 8 | MAKEFLAGS += --no-print-directory |
| 9 | 9 | ||
| @@ -38,6 +38,14 @@ struct thermal_data { · 37 unmodified lines | |||
| 38 | struct thermal_handler *th; | 38 | struct thermal_handler *th; |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | static int show_threshold(struct thermal_threshold *th, __maybe_unused void *arg) | ||
| 42 | { | ||
| 43 | INFO("threshold temp=%d, direction=%d\n", | ||
| 44 | th->temperature, th->direction); | ||
| 45 | |||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 41 | static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg) | 49 | static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg) |
| 42 | { | 50 | { |
| 43 | INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n", | 51 | INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n", |
| @@ -70,6 +78,8 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) · 26 unmodified lines | |||
| 70 | 78 | ||
| 71 | for_each_thermal_trip(tz->trip, show_trip, NULL); | 79 | for_each_thermal_trip(tz->trip, show_trip, NULL); |
| 72 | 80 | ||
| 81 | for_each_thermal_threshold(tz->thresholds, show_threshold, NULL); | ||
| 82 | |||
| 73 | show_temp(tz, arg); | 83 | show_temp(tz, arg); |
| 74 | 84 | ||
| 75 | show_governor(tz, arg); | 85 | show_governor(tz, arg); |
| @@ -77,6 +87,30 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) · 1 unmodified line | |||
| 77 | return 0; | 87 | return 0; |
| 78 | } | 88 | } |
| 79 | 89 | ||
| 90 | static int set_threshold(struct thermal_zone *tz, __maybe_unused void *arg) | ||
| 91 | { | ||
| 92 | struct thermal_handler *th = arg; | ||
| 93 | int thresholds[] = { 43000, 65000, 49000, 55000, 57000 }; | ||
| 94 | size_t i; | ||
| 95 | |||
| 96 | INFO("Setting threshold for thermal zone '%s', id=%d\n", tz->name, tz->id); | ||
| 97 | |||
| 98 | if (thermal_cmd_threshold_flush(th, tz)) { | ||
| 99 | ERROR("Failed to flush all previous thresholds\n"); | ||
| 100 | return -1; | ||
| 101 | } | ||
| 102 | |||
| 103 | for (i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); i++) | ||
| 104 | if (thermal_cmd_threshold_add(th, tz, thresholds[i], | ||
| 105 | THERMAL_THRESHOLD_WAY_UP | | ||
| 106 | THERMAL_THRESHOLD_WAY_DOWN)) { | ||
| 107 | ERROR("Failed to set threshold\n"); | ||
| 108 | return -1; | ||
| 109 | } | ||
| 110 | |||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 80 | static int tz_create(const char *name, int tz_id, __maybe_unused void *arg) | 114 | static int tz_create(const char *name, int tz_id, __maybe_unused void *arg) |
| 81 | { | 115 | { |
| 82 | INFO("Thermal zone '%s'/%d created\n", name, tz_id); | 116 | INFO("Thermal zone '%s'/%d created\n", name, tz_id); |
| @@ -197,20 +231,62 @@ static int gov_change(int tz_id, const char *name, __maybe_unused void *arg) · 114 unmodified lines | |||
| 197 | return 0; | 231 | return 0; |
| 198 | } | 232 | } |
| 199 | 233 | ||
| 234 | static int threshold_add(int tz_id, int temp, int direction, __maybe_unused void *arg) | ||
| 235 | { | ||
| 236 | INFO("Threshold added tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int threshold_delete(int tz_id, int temp, int direction, __maybe_unused void *arg) | ||
| 242 | { | ||
| 243 | INFO("Threshold deleted tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); | ||
| 244 | |||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | |||
| 248 | static int threshold_flush(int tz_id, __maybe_unused void *arg) | ||
| 249 | { | ||
| 250 | INFO("Thresholds flushed tz_id=%d\n", tz_id); | ||
| 251 | |||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | static int threshold_up(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) | ||
| 256 | { | ||
| 257 | INFO("Threshold crossed way up tz_id=%d: temp=%d, prev_temp=%d\n", | ||
| 258 | tz_id, temp, prev_temp); | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int threshold_down(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) | ||
| 264 | { | ||
| 265 | INFO("Threshold crossed way down tz_id=%d: temp=%d, prev_temp=%d\n", | ||
| 266 | tz_id, temp, prev_temp); | ||
| 267 | |||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 200 | static struct thermal_ops ops = { | 271 | static struct thermal_ops ops = { |
| 201 | .events.tz_create = tz_create, | 272 | .events.tz_create = tz_create, |
| 202 | .events.tz_delete = tz_delete, | 273 | .events.tz_delete = tz_delete, |
| 203 | .events.tz_disable = tz_disable, | 274 | .events.tz_disable = tz_disable, |
| 204 | .events.tz_enable = tz_enable, | 275 | .events.tz_enable = tz_enable, |
| 205 | .events.trip_high = trip_high, | 276 | .events.trip_high = trip_high, |
| 206 | .events.trip_low = trip_low, | 277 | .events.trip_low = trip_low, |
| 207 | .events.trip_add = trip_add, | 278 | .events.trip_add = trip_add, |
| 208 | .events.trip_delete = trip_delete, | 279 | .events.trip_delete = trip_delete, |
| 209 | .events.trip_change = trip_change, | 280 | .events.trip_change = trip_change, |
| 210 | .events.cdev_add = cdev_add, | 281 | .events.cdev_add = cdev_add, |
| 211 | .events.cdev_delete = cdev_delete, | 282 | .events.cdev_delete = cdev_delete, |
| 212 | .events.cdev_update = cdev_update, | 283 | .events.cdev_update = cdev_update, |
| 213 | .events.gov_change = gov_change | 284 | .events.gov_change = gov_change, |
| 285 | .events.threshold_add = threshold_add, | ||
| 286 | .events.threshold_delete = threshold_delete, | ||
| 287 | .events.threshold_flush = threshold_flush, | ||
| 288 | .events.threshold_up = threshold_up, | ||
| 289 | .events.threshold_down = threshold_down, | ||
| 214 | }; | 290 | }; |
| 215 | 291 | ||
| 216 | static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg) | 292 | static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg) |
| @@ -280,6 +356,7 @@ enum { · 63 unmodified lines | |||
| 280 | THERMAL_ENGINE_DAEMON_ERROR, | 356 | THERMAL_ENGINE_DAEMON_ERROR, |
| 281 | THERMAL_ENGINE_LOG_ERROR, | 357 | THERMAL_ENGINE_LOG_ERROR, |
| 282 | THERMAL_ENGINE_THERMAL_ERROR, | 358 | THERMAL_ENGINE_THERMAL_ERROR, |
| 359 | THERMAL_ENGINE_THRESHOLD_ERROR, | ||
| 283 | THERMAL_ENGINE_MAINLOOP_ERROR, | 360 | THERMAL_ENGINE_MAINLOOP_ERROR, |
| 284 | }; | 361 | }; |
| 285 | 362 | ||
| @@ -318,6 +395,8 @@ int main(int argc, char *argv[]) · 32 unmodified lines | |||
| 318 | return THERMAL_ENGINE_THERMAL_ERROR; | 395 | return THERMAL_ENGINE_THERMAL_ERROR; |
| 319 | } | 396 | } |
| 320 | 397 | ||
| 398 | for_each_thermal_zone(td.tz, set_threshold, td.th); | ||
| 399 | |||
| 321 | for_each_thermal_zone(td.tz, show_tz, td.th); | 400 | for_each_thermal_zone(td.tz, show_tz, td.th); |
| 322 | 401 | ||
| 323 | if (mainloop_init()) { | 402 | if (mainloop_init()) { |