cpu_hwmon.c 5.3 KB

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