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 8#include <loongson.h> 9#include <boot_param.h> 10#include <loongson_hwmon.h> 11 12/* 13 * Loongson-3 series cpu has two sensors inside, 14 * each of them from 0 to 255, 15 * if more than 127, that is dangerous. 16 * here only provide sensor1 data, because it always hot than sensor0 17 */ 18int loongson3_cpu_temp(int cpu) 19{ 20 u32 reg; 21 22 reg = LOONGSON_CHIPTEMP(cpu); 23 if (loongson_sysconf.cputype == Loongson_3A) 24 reg = (reg >> 8) & 0xff; 25 else if (loongson_sysconf.cputype == Loongson_3B) 26 reg = ((reg >> 8) & 0xff) - 100; 27 28 return (int)reg * 1000; 29} 30 31static struct device *cpu_hwmon_dev; 32 33static ssize_t get_hwmon_name(struct device *dev, 34 struct device_attribute *attr, char *buf); 35static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); 36 37static struct attribute *cpu_hwmon_attributes[] = { 38 &sensor_dev_attr_name.dev_attr.attr, 39 NULL 40}; 41 42/* Hwmon device attribute group */ 43static struct attribute_group cpu_hwmon_attribute_group = { 44 .attrs = cpu_hwmon_attributes, 45}; 46 47/* Hwmon device get name */ 48static ssize_t get_hwmon_name(struct device *dev, 49 struct device_attribute *attr, char *buf) 50{ 51 return sprintf(buf, "cpu-hwmon\n"); 52} 53 54static ssize_t get_cpu0_temp(struct device *dev, 55 struct device_attribute *attr, char *buf); 56static ssize_t get_cpu1_temp(struct device *dev, 57 struct device_attribute *attr, char *buf); 58static ssize_t cpu0_temp_label(struct device *dev, 59 struct device_attribute *attr, char *buf); 60static ssize_t cpu1_temp_label(struct device *dev, 61 struct device_attribute *attr, char *buf); 62 63static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1); 64static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1); 65static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2); 66static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2); 67 68static const struct attribute *hwmon_cputemp1[] = { 69 &sensor_dev_attr_temp1_input.dev_attr.attr, 70 &sensor_dev_attr_temp1_label.dev_attr.attr, 71 NULL 72}; 73 74static const struct attribute *hwmon_cputemp2[] = { 75 &sensor_dev_attr_temp2_input.dev_attr.attr, 76 &sensor_dev_attr_temp2_label.dev_attr.attr, 77 NULL 78}; 79 80static ssize_t cpu0_temp_label(struct device *dev, 81 struct device_attribute *attr, char *buf) 82{ 83 return sprintf(buf, "CPU 0 Temprature\n"); 84} 85 86static ssize_t cpu1_temp_label(struct device *dev, 87 struct device_attribute *attr, char *buf) 88{ 89 return sprintf(buf, "CPU 1 Temprature\n"); 90} 91 92static ssize_t get_cpu0_temp(struct device *dev, 93 struct device_attribute *attr, char *buf) 94{ 95 int value = loongson3_cpu_temp(0); 96 return sprintf(buf, "%d\n", value); 97} 98 99static ssize_t get_cpu1_temp(struct device *dev, 100 struct device_attribute *attr, char *buf) 101{ 102 int value = loongson3_cpu_temp(1); 103 return sprintf(buf, "%d\n", value); 104} 105 106static int create_sysfs_cputemp_files(struct kobject *kobj) 107{ 108 int ret; 109 110 ret = sysfs_create_files(kobj, hwmon_cputemp1); 111 if (ret) 112 goto sysfs_create_temp1_fail; 113 114 if (loongson_sysconf.nr_cpus <= loongson_sysconf.cores_per_package) 115 return 0; 116 117 ret = sysfs_create_files(kobj, hwmon_cputemp2); 118 if (ret) 119 goto sysfs_create_temp2_fail; 120 121 return 0; 122 123sysfs_create_temp2_fail: 124 sysfs_remove_files(kobj, hwmon_cputemp1); 125 126sysfs_create_temp1_fail: 127 return -1; 128} 129 130static void remove_sysfs_cputemp_files(struct kobject *kobj) 131{ 132 sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp1); 133 134 if (loongson_sysconf.nr_cpus > loongson_sysconf.cores_per_package) 135 sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp2); 136} 137 138#define CPU_THERMAL_THRESHOLD 90000 139static struct delayed_work thermal_work; 140 141static void do_thermal_timer(struct work_struct *work) 142{ 143 int value = loongson3_cpu_temp(0); 144 if (value <= CPU_THERMAL_THRESHOLD) 145 schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000)); 146 else 147 orderly_poweroff(true); 148} 149 150static int __init loongson_hwmon_init(void) 151{ 152 int ret; 153 154 pr_info("Loongson Hwmon Enter...\n"); 155 156 cpu_hwmon_dev = hwmon_device_register(NULL); 157 if (IS_ERR(cpu_hwmon_dev)) { 158 ret = -ENOMEM; 159 pr_err("hwmon_device_register fail!\n"); 160 goto fail_hwmon_device_register; 161 } 162 163 ret = sysfs_create_group(&cpu_hwmon_dev->kobj, 164 &cpu_hwmon_attribute_group); 165 if (ret) { 166 pr_err("fail to create loongson hwmon!\n"); 167 goto fail_sysfs_create_group_hwmon; 168 } 169 170 ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj); 171 if (ret) { 172 pr_err("fail to create cpu temprature interface!\n"); 173 goto fail_create_sysfs_cputemp_files; 174 } 175 176 INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); 177 schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); 178 179 return ret; 180 181fail_create_sysfs_cputemp_files: 182 sysfs_remove_group(&cpu_hwmon_dev->kobj, 183 &cpu_hwmon_attribute_group); 184 185fail_sysfs_create_group_hwmon: 186 hwmon_device_unregister(cpu_hwmon_dev); 187 188fail_hwmon_device_register: 189 return ret; 190} 191 192static void __exit loongson_hwmon_exit(void) 193{ 194 cancel_delayed_work_sync(&thermal_work); 195 remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj); 196 sysfs_remove_group(&cpu_hwmon_dev->kobj, 197 &cpu_hwmon_attribute_group); 198 hwmon_device_unregister(cpu_hwmon_dev); 199} 200 201module_init(loongson_hwmon_init); 202module_exit(loongson_hwmon_exit); 203 204MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>"); 205MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>"); 206MODULE_DESCRIPTION("Loongson CPU Hwmon driver"); 207MODULE_LICENSE("GPL"); 208