temp.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright 2012 The Nouveau community
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  17. * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18. * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20. * OTHER DEALINGS IN THE SOFTWARE.
  21. *
  22. * Authors: Martin Peres
  23. */
  24. #include "priv.h"
  25. static void
  26. nvkm_therm_temp_set_defaults(struct nvkm_therm *therm)
  27. {
  28. therm->bios_sensor.offset_constant = 0;
  29. therm->bios_sensor.thrs_fan_boost.temp = 90;
  30. therm->bios_sensor.thrs_fan_boost.hysteresis = 3;
  31. therm->bios_sensor.thrs_down_clock.temp = 95;
  32. therm->bios_sensor.thrs_down_clock.hysteresis = 3;
  33. therm->bios_sensor.thrs_critical.temp = 105;
  34. therm->bios_sensor.thrs_critical.hysteresis = 5;
  35. therm->bios_sensor.thrs_shutdown.temp = 135;
  36. therm->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */
  37. }
  38. static void
  39. nvkm_therm_temp_safety_checks(struct nvkm_therm *therm)
  40. {
  41. struct nvbios_therm_sensor *s = &therm->bios_sensor;
  42. /* enforce a minimum hysteresis on thresholds */
  43. s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
  44. s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
  45. s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
  46. s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
  47. }
  48. /* must be called with alarm_program_lock taken ! */
  49. void
  50. nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *therm,
  51. enum nvkm_therm_thrs thrs,
  52. enum nvkm_therm_thrs_state st)
  53. {
  54. therm->sensor.alarm_state[thrs] = st;
  55. }
  56. /* must be called with alarm_program_lock taken ! */
  57. enum nvkm_therm_thrs_state
  58. nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *therm,
  59. enum nvkm_therm_thrs thrs)
  60. {
  61. return therm->sensor.alarm_state[thrs];
  62. }
  63. static void
  64. nv_poweroff_work(struct work_struct *work)
  65. {
  66. orderly_poweroff(true);
  67. kfree(work);
  68. }
  69. void
  70. nvkm_therm_sensor_event(struct nvkm_therm *therm, enum nvkm_therm_thrs thrs,
  71. enum nvkm_therm_thrs_direction dir)
  72. {
  73. struct nvkm_subdev *subdev = &therm->subdev;
  74. bool active;
  75. const char *thresolds[] = {
  76. "fanboost", "downclock", "critical", "shutdown"
  77. };
  78. int temperature = therm->func->temp_get(therm);
  79. if (thrs < 0 || thrs > 3)
  80. return;
  81. if (dir == NVKM_THERM_THRS_FALLING)
  82. nvkm_info(subdev,
  83. "temperature (%i C) went below the '%s' threshold\n",
  84. temperature, thresolds[thrs]);
  85. else
  86. nvkm_info(subdev, "temperature (%i C) hit the '%s' threshold\n",
  87. temperature, thresolds[thrs]);
  88. active = (dir == NVKM_THERM_THRS_RISING);
  89. switch (thrs) {
  90. case NVKM_THERM_THRS_FANBOOST:
  91. if (active) {
  92. nvkm_therm_fan_set(therm, true, 100);
  93. nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
  94. }
  95. break;
  96. case NVKM_THERM_THRS_DOWNCLOCK:
  97. if (therm->emergency.downclock)
  98. therm->emergency.downclock(therm, active);
  99. break;
  100. case NVKM_THERM_THRS_CRITICAL:
  101. if (therm->emergency.pause)
  102. therm->emergency.pause(therm, active);
  103. break;
  104. case NVKM_THERM_THRS_SHUTDOWN:
  105. if (active) {
  106. struct work_struct *work;
  107. work = kmalloc(sizeof(*work), GFP_ATOMIC);
  108. if (work) {
  109. INIT_WORK(work, nv_poweroff_work);
  110. schedule_work(work);
  111. }
  112. }
  113. break;
  114. case NVKM_THERM_THRS_NR:
  115. break;
  116. }
  117. }
  118. /* must be called with alarm_program_lock taken ! */
  119. static void
  120. nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm,
  121. const struct nvbios_therm_threshold *thrs,
  122. enum nvkm_therm_thrs thrs_name)
  123. {
  124. enum nvkm_therm_thrs_direction direction;
  125. enum nvkm_therm_thrs_state prev_state, new_state;
  126. int temp = therm->func->temp_get(therm);
  127. prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
  128. if (temp >= thrs->temp && prev_state == NVKM_THERM_THRS_LOWER) {
  129. direction = NVKM_THERM_THRS_RISING;
  130. new_state = NVKM_THERM_THRS_HIGHER;
  131. } else if (temp <= thrs->temp - thrs->hysteresis &&
  132. prev_state == NVKM_THERM_THRS_HIGHER) {
  133. direction = NVKM_THERM_THRS_FALLING;
  134. new_state = NVKM_THERM_THRS_LOWER;
  135. } else
  136. return; /* nothing to do */
  137. nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
  138. nvkm_therm_sensor_event(therm, thrs_name, direction);
  139. }
  140. static void
  141. alarm_timer_callback(struct nvkm_alarm *alarm)
  142. {
  143. struct nvkm_therm *therm =
  144. container_of(alarm, struct nvkm_therm, sensor.therm_poll_alarm);
  145. struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
  146. struct nvkm_timer *tmr = therm->subdev.device->timer;
  147. unsigned long flags;
  148. spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
  149. nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
  150. NVKM_THERM_THRS_FANBOOST);
  151. nvkm_therm_threshold_hyst_polling(therm,
  152. &sensor->thrs_down_clock,
  153. NVKM_THERM_THRS_DOWNCLOCK);
  154. nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
  155. NVKM_THERM_THRS_CRITICAL);
  156. nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
  157. NVKM_THERM_THRS_SHUTDOWN);
  158. spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
  159. /* schedule the next poll in one second */
  160. if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head))
  161. nvkm_timer_alarm(tmr, 1000000000ULL, alarm);
  162. }
  163. void
  164. nvkm_therm_program_alarms_polling(struct nvkm_therm *therm)
  165. {
  166. struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
  167. nvkm_debug(&therm->subdev,
  168. "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
  169. sensor->thrs_fan_boost.temp,
  170. sensor->thrs_fan_boost.hysteresis,
  171. sensor->thrs_down_clock.temp,
  172. sensor->thrs_down_clock.hysteresis,
  173. sensor->thrs_critical.temp,
  174. sensor->thrs_critical.hysteresis,
  175. sensor->thrs_shutdown.temp,
  176. sensor->thrs_shutdown.hysteresis);
  177. alarm_timer_callback(&therm->sensor.therm_poll_alarm);
  178. }
  179. int
  180. nvkm_therm_sensor_init(struct nvkm_therm *therm)
  181. {
  182. therm->func->program_alarms(therm);
  183. return 0;
  184. }
  185. int
  186. nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend)
  187. {
  188. struct nvkm_timer *tmr = therm->subdev.device->timer;
  189. if (suspend)
  190. nvkm_timer_alarm_cancel(tmr, &therm->sensor.therm_poll_alarm);
  191. return 0;
  192. }
  193. void
  194. nvkm_therm_sensor_preinit(struct nvkm_therm *therm)
  195. {
  196. const char *sensor_avail = "yes";
  197. if (therm->func->temp_get(therm) < 0)
  198. sensor_avail = "no";
  199. nvkm_debug(&therm->subdev, "internal sensor: %s\n", sensor_avail);
  200. }
  201. int
  202. nvkm_therm_sensor_ctor(struct nvkm_therm *therm)
  203. {
  204. struct nvkm_subdev *subdev = &therm->subdev;
  205. struct nvkm_bios *bios = subdev->device->bios;
  206. nvkm_alarm_init(&therm->sensor.therm_poll_alarm, alarm_timer_callback);
  207. nvkm_therm_temp_set_defaults(therm);
  208. if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
  209. &therm->bios_sensor))
  210. nvkm_error(subdev, "nvbios_therm_sensor_parse failed\n");
  211. nvkm_therm_temp_safety_checks(therm);
  212. return 0;
  213. }