cpu_hwmon.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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:
  31. reg = (reg & 0xffff)*731/0x4000 - 273;
  32. break;
  33. }
  34. return (int)reg * 1000;
  35. }
  36. static struct device *cpu_hwmon_dev;
  37. static ssize_t get_hwmon_name(struct device *dev,
  38. struct device_attribute *attr, char *buf);
  39. static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0);
  40. static struct attribute *cpu_hwmon_attributes[] = {
  41. &sensor_dev_attr_name.dev_attr.attr,
  42. NULL
  43. };
  44. /* Hwmon device attribute group */
  45. static struct attribute_group cpu_hwmon_attribute_group = {
  46. .attrs = cpu_hwmon_attributes,
  47. };
  48. /* Hwmon device get name */
  49. static ssize_t get_hwmon_name(struct device *dev,
  50. struct device_attribute *attr, char *buf)
  51. {
  52. return sprintf(buf, "cpu-hwmon\n");
  53. }
  54. static ssize_t get_cpu0_temp(struct device *dev,
  55. struct device_attribute *attr, char *buf);
  56. static ssize_t get_cpu1_temp(struct device *dev,
  57. struct device_attribute *attr, char *buf);
  58. static ssize_t cpu0_temp_label(struct device *dev,
  59. struct device_attribute *attr, char *buf);
  60. static ssize_t cpu1_temp_label(struct device *dev,
  61. struct device_attribute *attr, char *buf);
  62. static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1);
  63. static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1);
  64. static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2);
  65. static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2);
  66. static const struct attribute *hwmon_cputemp1[] = {
  67. &sensor_dev_attr_temp1_input.dev_attr.attr,
  68. &sensor_dev_attr_temp1_label.dev_attr.attr,
  69. NULL
  70. };
  71. static const struct attribute *hwmon_cputemp2[] = {
  72. &sensor_dev_attr_temp2_input.dev_attr.attr,
  73. &sensor_dev_attr_temp2_label.dev_attr.attr,
  74. NULL
  75. };
  76. static ssize_t cpu0_temp_label(struct device *dev,
  77. struct device_attribute *attr, char *buf)
  78. {
  79. return sprintf(buf, "CPU 0 Temperature\n");
  80. }
  81. static ssize_t cpu1_temp_label(struct device *dev,
  82. struct device_attribute *attr, char *buf)
  83. {
  84. return sprintf(buf, "CPU 1 Temperature\n");
  85. }
  86. static ssize_t get_cpu0_temp(struct device *dev,
  87. struct device_attribute *attr, char *buf)
  88. {
  89. int value = loongson3_cpu_temp(0);
  90. return sprintf(buf, "%d\n", value);
  91. }
  92. static ssize_t get_cpu1_temp(struct device *dev,
  93. struct device_attribute *attr, char *buf)
  94. {
  95. int value = loongson3_cpu_temp(1);
  96. return sprintf(buf, "%d\n", value);
  97. }
  98. static int create_sysfs_cputemp_files(struct kobject *kobj)
  99. {
  100. int ret;
  101. ret = sysfs_create_files(kobj, hwmon_cputemp1);
  102. if (ret)
  103. goto sysfs_create_temp1_fail;
  104. if (loongson_sysconf.nr_cpus <= loongson_sysconf.cores_per_package)
  105. return 0;
  106. ret = sysfs_create_files(kobj, hwmon_cputemp2);
  107. if (ret)
  108. goto sysfs_create_temp2_fail;
  109. return 0;
  110. sysfs_create_temp2_fail:
  111. sysfs_remove_files(kobj, hwmon_cputemp1);
  112. sysfs_create_temp1_fail:
  113. return -1;
  114. }
  115. static void remove_sysfs_cputemp_files(struct kobject *kobj)
  116. {
  117. sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp1);
  118. if (loongson_sysconf.nr_cpus > loongson_sysconf.cores_per_package)
  119. sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp2);
  120. }
  121. #define CPU_THERMAL_THRESHOLD 90000
  122. static struct delayed_work thermal_work;
  123. static void do_thermal_timer(struct work_struct *work)
  124. {
  125. int value = loongson3_cpu_temp(0);
  126. if (value <= 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 = -ENOMEM;
  138. pr_err("hwmon_device_register fail!\n");
  139. goto fail_hwmon_device_register;
  140. }
  141. ret = sysfs_create_group(&cpu_hwmon_dev->kobj,
  142. &cpu_hwmon_attribute_group);
  143. if (ret) {
  144. pr_err("fail to create loongson hwmon!\n");
  145. goto fail_sysfs_create_group_hwmon;
  146. }
  147. ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
  148. if (ret) {
  149. pr_err("fail to create cpu temperature interface!\n");
  150. goto fail_create_sysfs_cputemp_files;
  151. }
  152. INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
  153. schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
  154. return ret;
  155. fail_create_sysfs_cputemp_files:
  156. sysfs_remove_group(&cpu_hwmon_dev->kobj,
  157. &cpu_hwmon_attribute_group);
  158. fail_sysfs_create_group_hwmon:
  159. hwmon_device_unregister(cpu_hwmon_dev);
  160. fail_hwmon_device_register:
  161. return ret;
  162. }
  163. static void __exit loongson_hwmon_exit(void)
  164. {
  165. cancel_delayed_work_sync(&thermal_work);
  166. remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
  167. sysfs_remove_group(&cpu_hwmon_dev->kobj,
  168. &cpu_hwmon_attribute_group);
  169. hwmon_device_unregister(cpu_hwmon_dev);
  170. }
  171. module_init(loongson_hwmon_init);
  172. module_exit(loongson_hwmon_exit);
  173. MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>");
  174. MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>");
  175. MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
  176. MODULE_LICENSE("GPL");