gov_bang_bang.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * gov_bang_bang.c - A simple thermal throttling governor using hysteresis
  4. *
  5. * Copyright (C) 2014 Peter Kaestle <peter@piie.net>
  6. *
  7. * Based on step_wise.c with following Copyrights:
  8. * Copyright (C) 2012 Intel Corp
  9. * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
  10. *
  11. * Regulation Logic: a two point regulation, deliver cooling state depending
  12. * on the previous state shown in this diagram:
  13. *
  14. * Fan: OFF ON
  15. *
  16. * |
  17. * |
  18. * trip_temp: +---->+
  19. * | | ^
  20. * | | |
  21. * | | Temperature
  22. * (trip_temp - hyst): +<----+
  23. * |
  24. * |
  25. * |
  26. *
  27. * * If the fan is not running and temperature exceeds trip_temp, the fan
  28. * gets turned on.
  29. * * In case the fan is running, temperature must fall below
  30. * (trip_temp - hyst) so that the fan gets turned off again.
  31. */
  32. #include <linux/thermal.h>
  33. #include "thermal_core.h"
  34. static void bang_bang_set_instance_target(struct thermal_instance *instance,
  35. unsigned int target)
  36. {
  37. if (instance->target != 0 && instance->target != 1 &&
  38. instance->target != THERMAL_NO_TARGET)
  39. pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n",
  40. instance->target, instance->name);
  41. /*
  42. * Enable the fan when the trip is crossed on the way up and disable it
  43. * when the trip is crossed on the way down.
  44. */
  45. instance->target = target;
  46. instance->initialized = true;
  47. dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target);
  48. thermal_cdev_update_nocheck(instance->cdev);
  49. }
  50. /**
  51. * bang_bang_trip_crossed - controls devices associated with the given zone
  52. * @tz: thermal_zone_device
  53. * @trip: the trip point
  54. * @upward: whether or not the trip has been crossed on the way up
  55. */
  56. static void bang_bang_trip_crossed(struct thermal_zone_device *tz,
  57. const struct thermal_trip *trip,
  58. bool upward)
  59. {
  60. const struct thermal_trip_desc *td = trip_to_trip_desc(trip);
  61. struct thermal_instance *instance;
  62. lockdep_assert_held(&tz->lock);
  63. dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
  64. thermal_zone_trip_id(tz, trip), trip->temperature,
  65. tz->temperature, trip->hysteresis);
  66. list_for_each_entry(instance, &td->thermal_instances, trip_node)
  67. bang_bang_set_instance_target(instance, upward);
  68. }
  69. static void bang_bang_manage(struct thermal_zone_device *tz)
  70. {
  71. const struct thermal_trip_desc *td;
  72. struct thermal_instance *instance;
  73. /* If the code below has run already, nothing needs to be done. */
  74. if (tz->governor_data)
  75. return;
  76. for_each_trip_desc(tz, td) {
  77. const struct thermal_trip *trip = &td->trip;
  78. bool turn_on;
  79. if (trip->temperature == THERMAL_TEMP_INVALID ||
  80. trip->type == THERMAL_TRIP_CRITICAL ||
  81. trip->type == THERMAL_TRIP_HOT)
  82. continue;
  83. /*
  84. * Adjust the target states for uninitialized thermal instances
  85. * to the thermal zone temperature and the trip point threshold.
  86. */
  87. turn_on = tz->temperature >= td->threshold;
  88. list_for_each_entry(instance, &td->thermal_instances, trip_node) {
  89. if (!instance->initialized)
  90. bang_bang_set_instance_target(instance, turn_on);
  91. }
  92. }
  93. tz->governor_data = (void *)true;
  94. }
  95. static void bang_bang_update_tz(struct thermal_zone_device *tz,
  96. enum thermal_notify_event reason)
  97. {
  98. /*
  99. * Let bang_bang_manage() know that it needs to walk trips after binding
  100. * a new cdev and after system resume.
  101. */
  102. if (reason == THERMAL_TZ_BIND_CDEV || reason == THERMAL_TZ_RESUME)
  103. tz->governor_data = NULL;
  104. }
  105. static struct thermal_governor thermal_gov_bang_bang = {
  106. .name = "bang_bang",
  107. .trip_crossed = bang_bang_trip_crossed,
  108. .manage = bang_bang_manage,
  109. .update_tz = bang_bang_update_tz,
  110. };
  111. THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang);