base.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. int
  26. nvkm_therm_temp_get(struct nvkm_therm *therm)
  27. {
  28. if (therm->func->temp_get)
  29. return therm->func->temp_get(therm);
  30. return -ENODEV;
  31. }
  32. static int
  33. nvkm_therm_update_trip(struct nvkm_therm *therm)
  34. {
  35. struct nvbios_therm_trip_point *trip = therm->fan->bios.trip,
  36. *cur_trip = NULL,
  37. *last_trip = therm->last_trip;
  38. u8 temp = therm->func->temp_get(therm);
  39. u16 duty, i;
  40. /* look for the trip point corresponding to the current temperature */
  41. cur_trip = NULL;
  42. for (i = 0; i < therm->fan->bios.nr_fan_trip; i++) {
  43. if (temp >= trip[i].temp)
  44. cur_trip = &trip[i];
  45. }
  46. /* account for the hysteresis cycle */
  47. if (last_trip && temp <= (last_trip->temp) &&
  48. temp > (last_trip->temp - last_trip->hysteresis))
  49. cur_trip = last_trip;
  50. if (cur_trip) {
  51. duty = cur_trip->fan_duty;
  52. therm->last_trip = cur_trip;
  53. } else {
  54. duty = 0;
  55. therm->last_trip = NULL;
  56. }
  57. return duty;
  58. }
  59. static int
  60. nvkm_therm_update_linear(struct nvkm_therm *therm)
  61. {
  62. u8 linear_min_temp = therm->fan->bios.linear_min_temp;
  63. u8 linear_max_temp = therm->fan->bios.linear_max_temp;
  64. u8 temp = therm->func->temp_get(therm);
  65. u16 duty;
  66. /* handle the non-linear part first */
  67. if (temp < linear_min_temp)
  68. return therm->fan->bios.min_duty;
  69. else if (temp > linear_max_temp)
  70. return therm->fan->bios.max_duty;
  71. /* we are in the linear zone */
  72. duty = (temp - linear_min_temp);
  73. duty *= (therm->fan->bios.max_duty - therm->fan->bios.min_duty);
  74. duty /= (linear_max_temp - linear_min_temp);
  75. duty += therm->fan->bios.min_duty;
  76. return duty;
  77. }
  78. static void
  79. nvkm_therm_update(struct nvkm_therm *therm, int mode)
  80. {
  81. struct nvkm_subdev *subdev = &therm->subdev;
  82. struct nvkm_timer *tmr = subdev->device->timer;
  83. unsigned long flags;
  84. bool immd = true;
  85. bool poll = true;
  86. int duty = -1;
  87. spin_lock_irqsave(&therm->lock, flags);
  88. if (mode < 0)
  89. mode = therm->mode;
  90. therm->mode = mode;
  91. switch (mode) {
  92. case NVKM_THERM_CTRL_MANUAL:
  93. nvkm_timer_alarm_cancel(tmr, &therm->alarm);
  94. duty = nvkm_therm_fan_get(therm);
  95. if (duty < 0)
  96. duty = 100;
  97. poll = false;
  98. break;
  99. case NVKM_THERM_CTRL_AUTO:
  100. switch(therm->fan->bios.fan_mode) {
  101. case NVBIOS_THERM_FAN_TRIP:
  102. duty = nvkm_therm_update_trip(therm);
  103. break;
  104. case NVBIOS_THERM_FAN_LINEAR:
  105. duty = nvkm_therm_update_linear(therm);
  106. break;
  107. case NVBIOS_THERM_FAN_OTHER:
  108. if (therm->cstate)
  109. duty = therm->cstate;
  110. poll = false;
  111. break;
  112. }
  113. immd = false;
  114. break;
  115. case NVKM_THERM_CTRL_NONE:
  116. default:
  117. nvkm_timer_alarm_cancel(tmr, &therm->alarm);
  118. poll = false;
  119. }
  120. if (list_empty(&therm->alarm.head) && poll)
  121. nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
  122. spin_unlock_irqrestore(&therm->lock, flags);
  123. if (duty >= 0) {
  124. nvkm_debug(subdev, "FAN target request: %d%%\n", duty);
  125. nvkm_therm_fan_set(therm, immd, duty);
  126. }
  127. }
  128. int
  129. nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir)
  130. {
  131. struct nvkm_subdev *subdev = &therm->subdev;
  132. if (!dir || (dir < 0 && fan < therm->cstate) ||
  133. (dir > 0 && fan > therm->cstate)) {
  134. nvkm_debug(subdev, "default fan speed -> %d%%\n", fan);
  135. therm->cstate = fan;
  136. nvkm_therm_update(therm, -1);
  137. }
  138. return 0;
  139. }
  140. static void
  141. nvkm_therm_alarm(struct nvkm_alarm *alarm)
  142. {
  143. struct nvkm_therm *therm =
  144. container_of(alarm, struct nvkm_therm, alarm);
  145. nvkm_therm_update(therm, -1);
  146. }
  147. int
  148. nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
  149. {
  150. struct nvkm_subdev *subdev = &therm->subdev;
  151. struct nvkm_device *device = subdev->device;
  152. static const char *name[] = {
  153. "disabled",
  154. "manual",
  155. "automatic"
  156. };
  157. /* The default PPWR ucode on fermi interferes with fan management */
  158. if ((mode >= ARRAY_SIZE(name)) ||
  159. (mode != NVKM_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
  160. !device->pmu))
  161. return -EINVAL;
  162. /* do not allow automatic fan management if the thermal sensor is
  163. * not available */
  164. if (mode == NVKM_THERM_CTRL_AUTO &&
  165. therm->func->temp_get(therm) < 0)
  166. return -EINVAL;
  167. if (therm->mode == mode)
  168. return 0;
  169. nvkm_debug(subdev, "fan management: %s\n", name[mode]);
  170. nvkm_therm_update(therm, mode);
  171. return 0;
  172. }
  173. int
  174. nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type)
  175. {
  176. switch (type) {
  177. case NVKM_THERM_ATTR_FAN_MIN_DUTY:
  178. return therm->fan->bios.min_duty;
  179. case NVKM_THERM_ATTR_FAN_MAX_DUTY:
  180. return therm->fan->bios.max_duty;
  181. case NVKM_THERM_ATTR_FAN_MODE:
  182. return therm->mode;
  183. case NVKM_THERM_ATTR_THRS_FAN_BOOST:
  184. return therm->bios_sensor.thrs_fan_boost.temp;
  185. case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
  186. return therm->bios_sensor.thrs_fan_boost.hysteresis;
  187. case NVKM_THERM_ATTR_THRS_DOWN_CLK:
  188. return therm->bios_sensor.thrs_down_clock.temp;
  189. case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
  190. return therm->bios_sensor.thrs_down_clock.hysteresis;
  191. case NVKM_THERM_ATTR_THRS_CRITICAL:
  192. return therm->bios_sensor.thrs_critical.temp;
  193. case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
  194. return therm->bios_sensor.thrs_critical.hysteresis;
  195. case NVKM_THERM_ATTR_THRS_SHUTDOWN:
  196. return therm->bios_sensor.thrs_shutdown.temp;
  197. case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
  198. return therm->bios_sensor.thrs_shutdown.hysteresis;
  199. }
  200. return -EINVAL;
  201. }
  202. int
  203. nvkm_therm_attr_set(struct nvkm_therm *therm,
  204. enum nvkm_therm_attr_type type, int value)
  205. {
  206. switch (type) {
  207. case NVKM_THERM_ATTR_FAN_MIN_DUTY:
  208. if (value < 0)
  209. value = 0;
  210. if (value > therm->fan->bios.max_duty)
  211. value = therm->fan->bios.max_duty;
  212. therm->fan->bios.min_duty = value;
  213. return 0;
  214. case NVKM_THERM_ATTR_FAN_MAX_DUTY:
  215. if (value < 0)
  216. value = 0;
  217. if (value < therm->fan->bios.min_duty)
  218. value = therm->fan->bios.min_duty;
  219. therm->fan->bios.max_duty = value;
  220. return 0;
  221. case NVKM_THERM_ATTR_FAN_MODE:
  222. return nvkm_therm_fan_mode(therm, value);
  223. case NVKM_THERM_ATTR_THRS_FAN_BOOST:
  224. therm->bios_sensor.thrs_fan_boost.temp = value;
  225. therm->func->program_alarms(therm);
  226. return 0;
  227. case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
  228. therm->bios_sensor.thrs_fan_boost.hysteresis = value;
  229. therm->func->program_alarms(therm);
  230. return 0;
  231. case NVKM_THERM_ATTR_THRS_DOWN_CLK:
  232. therm->bios_sensor.thrs_down_clock.temp = value;
  233. therm->func->program_alarms(therm);
  234. return 0;
  235. case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
  236. therm->bios_sensor.thrs_down_clock.hysteresis = value;
  237. therm->func->program_alarms(therm);
  238. return 0;
  239. case NVKM_THERM_ATTR_THRS_CRITICAL:
  240. therm->bios_sensor.thrs_critical.temp = value;
  241. therm->func->program_alarms(therm);
  242. return 0;
  243. case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
  244. therm->bios_sensor.thrs_critical.hysteresis = value;
  245. therm->func->program_alarms(therm);
  246. return 0;
  247. case NVKM_THERM_ATTR_THRS_SHUTDOWN:
  248. therm->bios_sensor.thrs_shutdown.temp = value;
  249. therm->func->program_alarms(therm);
  250. return 0;
  251. case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
  252. therm->bios_sensor.thrs_shutdown.hysteresis = value;
  253. therm->func->program_alarms(therm);
  254. return 0;
  255. }
  256. return -EINVAL;
  257. }
  258. static void
  259. nvkm_therm_intr(struct nvkm_subdev *subdev)
  260. {
  261. struct nvkm_therm *therm = nvkm_therm(subdev);
  262. if (therm->func->intr)
  263. therm->func->intr(therm);
  264. }
  265. static int
  266. nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend)
  267. {
  268. struct nvkm_therm *therm = nvkm_therm(subdev);
  269. if (therm->func->fini)
  270. therm->func->fini(therm);
  271. nvkm_therm_fan_fini(therm, suspend);
  272. nvkm_therm_sensor_fini(therm, suspend);
  273. if (suspend) {
  274. therm->suspend = therm->mode;
  275. therm->mode = NVKM_THERM_CTRL_NONE;
  276. }
  277. return 0;
  278. }
  279. static int
  280. nvkm_therm_oneinit(struct nvkm_subdev *subdev)
  281. {
  282. struct nvkm_therm *therm = nvkm_therm(subdev);
  283. nvkm_therm_sensor_ctor(therm);
  284. nvkm_therm_ic_ctor(therm);
  285. nvkm_therm_fan_ctor(therm);
  286. nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
  287. nvkm_therm_sensor_preinit(therm);
  288. return 0;
  289. }
  290. static int
  291. nvkm_therm_init(struct nvkm_subdev *subdev)
  292. {
  293. struct nvkm_therm *therm = nvkm_therm(subdev);
  294. therm->func->init(therm);
  295. if (therm->suspend >= 0) {
  296. /* restore the pwm value only when on manual or auto mode */
  297. if (therm->suspend > 0)
  298. nvkm_therm_fan_set(therm, true, therm->fan->percent);
  299. nvkm_therm_fan_mode(therm, therm->suspend);
  300. }
  301. nvkm_therm_sensor_init(therm);
  302. nvkm_therm_fan_init(therm);
  303. return 0;
  304. }
  305. static void *
  306. nvkm_therm_dtor(struct nvkm_subdev *subdev)
  307. {
  308. struct nvkm_therm *therm = nvkm_therm(subdev);
  309. kfree(therm->fan);
  310. return therm;
  311. }
  312. static const struct nvkm_subdev_func
  313. nvkm_therm = {
  314. .dtor = nvkm_therm_dtor,
  315. .oneinit = nvkm_therm_oneinit,
  316. .init = nvkm_therm_init,
  317. .fini = nvkm_therm_fini,
  318. .intr = nvkm_therm_intr,
  319. };
  320. int
  321. nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device,
  322. int index, struct nvkm_therm **ptherm)
  323. {
  324. struct nvkm_therm *therm;
  325. if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL)))
  326. return -ENOMEM;
  327. nvkm_subdev_ctor(&nvkm_therm, device, index, &therm->subdev);
  328. therm->func = func;
  329. nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm);
  330. spin_lock_init(&therm->lock);
  331. spin_lock_init(&therm->sensor.alarm_program_lock);
  332. therm->fan_get = nvkm_therm_fan_user_get;
  333. therm->fan_set = nvkm_therm_fan_user_set;
  334. therm->attr_get = nvkm_therm_attr_get;
  335. therm->attr_set = nvkm_therm_attr_set;
  336. therm->mode = therm->suspend = -1; /* undefined */
  337. return 0;
  338. }