thermal_hwmon.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * thermal_hwmon.c - Generic Thermal Management hwmon support.
  4. *
  5. * Code based on Intel thermal_core.c. Copyrights of the original code:
  6. * Copyright (C) 2008 Intel Corp
  7. * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
  8. * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
  9. *
  10. * Copyright (C) 2013 Texas Instruments
  11. * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
  12. */
  13. #include <linux/err.h>
  14. #include <linux/export.h>
  15. #include <linux/hwmon.h>
  16. #include <linux/slab.h>
  17. #include <linux/thermal.h>
  18. #include "thermal_hwmon.h"
  19. /* hwmon sys I/F */
  20. /* thermal zone devices with the same type share one hwmon device */
  21. struct thermal_hwmon_device {
  22. char type[THERMAL_NAME_LENGTH];
  23. struct device *device;
  24. int count;
  25. struct list_head tz_list;
  26. struct list_head node;
  27. };
  28. struct thermal_hwmon_attr {
  29. struct device_attribute attr;
  30. char name[16];
  31. };
  32. /* one temperature input for each thermal zone */
  33. struct thermal_hwmon_temp {
  34. struct list_head hwmon_node;
  35. struct thermal_zone_device *tz;
  36. struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
  37. struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
  38. };
  39. static LIST_HEAD(thermal_hwmon_list);
  40. static DEFINE_MUTEX(thermal_hwmon_list_lock);
  41. static ssize_t
  42. temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
  43. {
  44. int temperature;
  45. int ret;
  46. struct thermal_hwmon_attr *hwmon_attr
  47. = container_of(attr, struct thermal_hwmon_attr, attr);
  48. struct thermal_hwmon_temp *temp
  49. = container_of(hwmon_attr, struct thermal_hwmon_temp,
  50. temp_input);
  51. struct thermal_zone_device *tz = temp->tz;
  52. ret = thermal_zone_get_temp(tz, &temperature);
  53. if (ret)
  54. return ret;
  55. return sprintf(buf, "%d\n", temperature);
  56. }
  57. static ssize_t
  58. temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
  59. {
  60. struct thermal_hwmon_attr *hwmon_attr
  61. = container_of(attr, struct thermal_hwmon_attr, attr);
  62. struct thermal_hwmon_temp *temp
  63. = container_of(hwmon_attr, struct thermal_hwmon_temp,
  64. temp_crit);
  65. struct thermal_zone_device *tz = temp->tz;
  66. int temperature;
  67. int ret;
  68. ret = tz->ops->get_crit_temp(tz, &temperature);
  69. if (ret)
  70. return ret;
  71. return sprintf(buf, "%d\n", temperature);
  72. }
  73. static struct thermal_hwmon_device *
  74. thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
  75. {
  76. struct thermal_hwmon_device *hwmon;
  77. char type[THERMAL_NAME_LENGTH];
  78. mutex_lock(&thermal_hwmon_list_lock);
  79. list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
  80. strcpy(type, tz->type);
  81. strreplace(type, '-', '_');
  82. if (!strcmp(hwmon->type, type)) {
  83. mutex_unlock(&thermal_hwmon_list_lock);
  84. return hwmon;
  85. }
  86. }
  87. mutex_unlock(&thermal_hwmon_list_lock);
  88. return NULL;
  89. }
  90. /* Find the temperature input matching a given thermal zone */
  91. static struct thermal_hwmon_temp *
  92. thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
  93. const struct thermal_zone_device *tz)
  94. {
  95. struct thermal_hwmon_temp *temp;
  96. mutex_lock(&thermal_hwmon_list_lock);
  97. list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
  98. if (temp->tz == tz) {
  99. mutex_unlock(&thermal_hwmon_list_lock);
  100. return temp;
  101. }
  102. mutex_unlock(&thermal_hwmon_list_lock);
  103. return NULL;
  104. }
  105. static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
  106. {
  107. int temp;
  108. return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp);
  109. }
  110. int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
  111. {
  112. struct thermal_hwmon_device *hwmon;
  113. struct thermal_hwmon_temp *temp;
  114. int new_hwmon_device = 1;
  115. int result;
  116. hwmon = thermal_hwmon_lookup_by_type(tz);
  117. if (hwmon) {
  118. new_hwmon_device = 0;
  119. goto register_sys_interface;
  120. }
  121. hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
  122. if (!hwmon)
  123. return -ENOMEM;
  124. INIT_LIST_HEAD(&hwmon->tz_list);
  125. strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
  126. strreplace(hwmon->type, '-', '_');
  127. hwmon->device = hwmon_device_register_with_info(&tz->device, hwmon->type,
  128. hwmon, NULL, NULL);
  129. if (IS_ERR(hwmon->device)) {
  130. result = PTR_ERR(hwmon->device);
  131. goto free_mem;
  132. }
  133. register_sys_interface:
  134. temp = kzalloc(sizeof(*temp), GFP_KERNEL);
  135. if (!temp) {
  136. result = -ENOMEM;
  137. goto unregister_name;
  138. }
  139. temp->tz = tz;
  140. hwmon->count++;
  141. snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
  142. "temp%d_input", hwmon->count);
  143. temp->temp_input.attr.attr.name = temp->temp_input.name;
  144. temp->temp_input.attr.attr.mode = 0444;
  145. temp->temp_input.attr.show = temp_input_show;
  146. sysfs_attr_init(&temp->temp_input.attr.attr);
  147. result = device_create_file(hwmon->device, &temp->temp_input.attr);
  148. if (result)
  149. goto free_temp_mem;
  150. if (thermal_zone_crit_temp_valid(tz)) {
  151. snprintf(temp->temp_crit.name,
  152. sizeof(temp->temp_crit.name),
  153. "temp%d_crit", hwmon->count);
  154. temp->temp_crit.attr.attr.name = temp->temp_crit.name;
  155. temp->temp_crit.attr.attr.mode = 0444;
  156. temp->temp_crit.attr.show = temp_crit_show;
  157. sysfs_attr_init(&temp->temp_crit.attr.attr);
  158. result = device_create_file(hwmon->device,
  159. &temp->temp_crit.attr);
  160. if (result)
  161. goto unregister_input;
  162. }
  163. mutex_lock(&thermal_hwmon_list_lock);
  164. if (new_hwmon_device)
  165. list_add_tail(&hwmon->node, &thermal_hwmon_list);
  166. list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
  167. mutex_unlock(&thermal_hwmon_list_lock);
  168. return 0;
  169. unregister_input:
  170. device_remove_file(hwmon->device, &temp->temp_input.attr);
  171. free_temp_mem:
  172. kfree(temp);
  173. unregister_name:
  174. if (new_hwmon_device)
  175. hwmon_device_unregister(hwmon->device);
  176. free_mem:
  177. if (new_hwmon_device)
  178. kfree(hwmon);
  179. return result;
  180. }
  181. EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
  182. void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
  183. {
  184. struct thermal_hwmon_device *hwmon;
  185. struct thermal_hwmon_temp *temp;
  186. hwmon = thermal_hwmon_lookup_by_type(tz);
  187. if (unlikely(!hwmon)) {
  188. /* Should never happen... */
  189. dev_dbg(&tz->device, "hwmon device lookup failed!\n");
  190. return;
  191. }
  192. temp = thermal_hwmon_lookup_temp(hwmon, tz);
  193. if (unlikely(!temp)) {
  194. /* Should never happen... */
  195. dev_dbg(&tz->device, "temperature input lookup failed!\n");
  196. return;
  197. }
  198. device_remove_file(hwmon->device, &temp->temp_input.attr);
  199. if (thermal_zone_crit_temp_valid(tz))
  200. device_remove_file(hwmon->device, &temp->temp_crit.attr);
  201. mutex_lock(&thermal_hwmon_list_lock);
  202. list_del(&temp->hwmon_node);
  203. kfree(temp);
  204. if (!list_empty(&hwmon->tz_list)) {
  205. mutex_unlock(&thermal_hwmon_list_lock);
  206. return;
  207. }
  208. list_del(&hwmon->node);
  209. mutex_unlock(&thermal_hwmon_list_lock);
  210. hwmon_device_unregister(hwmon->device);
  211. kfree(hwmon);
  212. }
  213. EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);
  214. static void devm_thermal_hwmon_release(struct device *dev, void *res)
  215. {
  216. thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res);
  217. }
  218. int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
  219. {
  220. struct thermal_zone_device **ptr;
  221. int ret;
  222. ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr),
  223. GFP_KERNEL);
  224. if (!ptr)
  225. return -ENOMEM;
  226. ret = thermal_add_hwmon_sysfs(tz);
  227. if (ret) {
  228. devres_free(ptr);
  229. return ret;
  230. }
  231. *ptr = tz;
  232. devres_add(&tz->device, ptr);
  233. return ret;
  234. }
  235. EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);