cpu_hwmon.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #include <linux/err.h>
  2. #include <linux/module.h>
  3. #include <linux/reboot.h>
  4. #include <linux/jiffies.h>
  5. #include <linux/hwmon.h>
  6. #include <linux/hwmon-sysfs.h>
  7. #include <loongson.h>
  8. #include <boot_param.h>
  9. #include <loongson_hwmon.h>
  10. /*
  11. * Loongson-3 series cpu has two sensors inside,
  12. * each of them from 0 to 255,
  13. * if more than 127, that is dangerous.
  14. * here only provide sensor1 data, because it always hot than sensor0
  15. */
  16. int loongson3_cpu_temp(int cpu)
  17. {
  18. u32 reg, prid_rev;
  19. reg = LOONGSON_CHIPTEMP(cpu);
  20. prid_rev = read_c0_prid() & PRID_REV_MASK;
  21. switch (prid_rev) {
  22. case PRID_REV_LOONGSON3A_R1:
  23. reg = (reg >> 8) & 0xff;
  24. break;
  25. case PRID_REV_LOONGSON3A_R2:
  26. case PRID_REV_LOONGSON3B_R1:
  27. case PRID_REV_LOONGSON3B_R2:
  28. reg = ((reg >> 8) & 0xff) - 100;
  29. break;
  30. case PRID_REV_LOONGSON3A_R3_0:
  31. case PRID_REV_LOONGSON3A_R3_1:
  32. reg = (reg & 0xffff)*731/0x4000 - 273;
  33. break;
  34. }
  35. return (int)reg * 1000;
  36. }
  37. static int nr_packages;
  38. static struct device *cpu_hwmon_dev;
  39. static ssize_t get_hwmon_name(struct device *dev,
  40. struct device_attribute *attr, char *buf);
  41. static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0);
  42. static struct attribute *cpu_hwmon_attributes[] = {
  43. &sensor_dev_attr_name.dev_attr.attr,
  44. NULL
  45. };
  46. /* Hwmon device attribute group */
  47. static struct attribute_group cpu_hwmon_attribute_group = {
  48. .attrs = cpu_hwmon_attributes,
  49. };
  50. /* Hwmon device get name */
  51. static ssize_t get_hwmon_name(struct device *dev,
  52. struct device_attribute *attr, char *buf)
  53. {
  54. return sprintf(buf, "cpu-hwmon\n");
  55. }
  56. static ssize_t get_cpu_temp(struct device *dev,
  57. struct device_attribute *attr, char *buf);
  58. static ssize_t cpu_temp_label(struct device *dev,
  59. struct device_attribute *attr, char *buf);
  60. static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu_temp, NULL, 1);
  61. static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu_temp_label, NULL, 1);
  62. static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu_temp, NULL, 2);
  63. static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu_temp_label, NULL, 2);
  64. static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, get_cpu_temp, NULL, 3);
  65. static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, cpu_temp_label, NULL, 3);
  66. static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, get_cpu_temp, NULL, 4);
  67. static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, cpu_temp_label, NULL, 4);
  68. static const struct attribute *hwmon_cputemp[4][3] = {
  69. {
  70. &sensor_dev_attr_temp1_input.dev_attr.attr,
  71. &sensor_dev_attr_temp1_label.dev_attr.attr,
  72. NULL
  73. },
  74. {
  75. &sensor_dev_attr_temp2_input.dev_attr.attr,
  76. &sensor_dev_attr_temp2_label.dev_attr.attr,
  77. NULL
  78. },
  79. {
  80. &sensor_dev_attr_temp3_input.dev_attr.attr,
  81. &sensor_dev_attr_temp3_label.dev_attr.attr,
  82. NULL
  83. },
  84. {
  85. &sensor_dev_attr_temp4_input.dev_attr.attr,
  86. &sensor_dev_attr_temp4_label.dev_attr.attr,
  87. NULL
  88. }
  89. };
  90. static ssize_t cpu_temp_label(struct device *dev,
  91. struct device_attribute *attr, char *buf)
  92. {
  93. int id = (to_sensor_dev_attr(attr))->index - 1;
  94. return sprintf(buf, "CPU %d Temperature\n", id);
  95. }
  96. static ssize_t get_cpu_temp(struct device *dev,
  97. struct device_attribute *attr, char *buf)
  98. {
  99. int id = (to_sensor_dev_attr(attr))->index - 1;
  100. int value = loongson3_cpu_temp(id);
  101. return sprintf(buf, "%d\n", value);
  102. }
  103. static int create_sysfs_cputemp_files(struct kobject *kobj)
  104. {
  105. int i, ret = 0;
  106. for (i=0; i<nr_packages; i++)
  107. ret = sysfs_create_files(kobj, hwmon_cputemp[i]);
  108. return ret;
  109. }
  110. static void remove_sysfs_cputemp_files(struct kobject *kobj)
  111. {
  112. int i;
  113. for (i=0; i<nr_packages; i++)
  114. sysfs_remove_files(kobj, hwmon_cputemp[i]);
  115. }
  116. #define CPU_THERMAL_THRESHOLD 90000
  117. static struct delayed_work thermal_work;
  118. static void do_thermal_timer(struct work_struct *work)
  119. {
  120. int i, value, temp_max = 0;
  121. for (i=0; i<nr_packages; i++) {
  122. value = loongson3_cpu_temp(i);
  123. if (value > temp_max)
  124. temp_max = value;
  125. }
  126. if (temp_max <= CPU_THERMAL_THRESHOLD)
  127. schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
  128. else
  129. orderly_poweroff(true);
  130. }
  131. static int __init loongson_hwmon_init(void)
  132. {
  133. int ret;
  134. pr_info("Loongson Hwmon Enter...\n");
  135. cpu_hwmon_dev = hwmon_device_register(NULL);
  136. if (IS_ERR(cpu_hwmon_dev)) {
  137. ret = PTR_ERR(cpu_hwmon_dev);
  138. pr_err("hwmon_device_register fail!\n");
  139. goto fail_hwmon_device_register;
  140. }
  141. nr_packages = loongson_sysconf.nr_cpus /
  142. loongson_sysconf.cores_per_package;
  143. ret = sysfs_create_group(&cpu_hwmon_dev->kobj,
  144. &cpu_hwmon_attribute_group);
  145. if (ret) {
  146. pr_err("fail to create loongson hwmon!\n");
  147. goto fail_sysfs_create_group_hwmon;
  148. }
  149. ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
  150. if (ret) {
  151. pr_err("fail to create cpu temperature interface!\n");
  152. goto fail_create_sysfs_cputemp_files;
  153. }
  154. INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
  155. schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
  156. return ret;
  157. fail_create_sysfs_cputemp_files:
  158. sysfs_remove_group(&cpu_hwmon_dev->kobj,
  159. &cpu_hwmon_attribute_group);
  160. fail_sysfs_create_group_hwmon:
  161. hwmon_device_unregister(cpu_hwmon_dev);
  162. fail_hwmon_device_register:
  163. return ret;
  164. }
  165. static void __exit loongson_hwmon_exit(void)
  166. {
  167. cancel_delayed_work_sync(&thermal_work);
  168. remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
  169. sysfs_remove_group(&cpu_hwmon_dev->kobj,
  170. &cpu_hwmon_attribute_group);
  171. hwmon_device_unregister(cpu_hwmon_dev);
  172. }
  173. module_init(loongson_hwmon_init);
  174. module_exit(loongson_hwmon_exit);
  175. MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>");
  176. MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>");
  177. MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
  178. MODULE_LICENSE("GPL");