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 */ 18 int loongson3_cpu_temp(int cpu) 19 { 20 u32 reg; 21 22 reg = LOONGSON_CHIPTEMP(cpu); 23 if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) 24 reg = (reg >> 8) & 0xff; 25 else 26 reg = ((reg >> 8) & 0xff) - 100; 27 28 return (int)reg * 1000; 29 } 30 31 static struct device *cpu_hwmon_dev; 32 33 static ssize_t get_hwmon_name(struct device *dev, 34 struct device_attribute *attr, char *buf); 35 static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); 36 37 static struct attribute *cpu_hwmon_attributes[] = { 38 &sensor_dev_attr_name.dev_attr.attr, 39 NULL 40 }; 41 42 /* Hwmon device attribute group */ 43 static struct attribute_group cpu_hwmon_attribute_group = { 44 .attrs = cpu_hwmon_attributes, 45 }; 46 47 /* Hwmon device get name */ 48 static 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 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 63 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1); 64 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1); 65 static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2); 66 static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2); 67 68 static 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 74 static 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 80 static ssize_t cpu0_temp_label(struct device *dev, 81 struct device_attribute *attr, char *buf) 82 { 83 return sprintf(buf, "CPU 0 Temperature\n"); 84 } 85 86 static ssize_t cpu1_temp_label(struct device *dev, 87 struct device_attribute *attr, char *buf) 88 { 89 return sprintf(buf, "CPU 1 Temperature\n"); 90 } 91 92 static 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 99 static 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 106 static 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 123 sysfs_create_temp2_fail: 124 sysfs_remove_files(kobj, hwmon_cputemp1); 125 126 sysfs_create_temp1_fail: 127 return -1; 128 } 129 130 static 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 139 static struct delayed_work thermal_work; 140 141 static 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 150 static 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 temperature 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 181 fail_create_sysfs_cputemp_files: 182 sysfs_remove_group(&cpu_hwmon_dev->kobj, 183 &cpu_hwmon_attribute_group); 184 185 fail_sysfs_create_group_hwmon: 186 hwmon_device_unregister(cpu_hwmon_dev); 187 188 fail_hwmon_device_register: 189 return ret; 190 } 191 192 static 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 201 module_init(loongson_hwmon_init); 202 module_exit(loongson_hwmon_exit); 203 204 MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>"); 205 MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>"); 206 MODULE_DESCRIPTION("Loongson CPU Hwmon driver"); 207 MODULE_LICENSE("GPL"); 208