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>
9 #include <boot_param.h>
10 #include <loongson_hwmon.h>
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
18 int loongson3_cpu_temp(int cpu
)
22 reg
= LOONGSON_CHIPTEMP(cpu
);
23 if ((read_c0_prid() & PRID_REV_MASK
) == PRID_REV_LOONGSON3A_R1
)
24 reg
= (reg
>> 8) & 0xff;
26 reg
= ((reg
>> 8) & 0xff) - 100;
28 return (int)reg
* 1000;
31 static struct device
*cpu_hwmon_dev
;
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);
37 static struct attribute
*cpu_hwmon_attributes
[] = {
38 &sensor_dev_attr_name
.dev_attr
.attr
,
42 /* Hwmon device attribute group */
43 static struct attribute_group cpu_hwmon_attribute_group
= {
44 .attrs
= cpu_hwmon_attributes
,
47 /* Hwmon device get name */
48 static ssize_t
get_hwmon_name(struct device
*dev
,
49 struct device_attribute
*attr
, char *buf
)
51 return sprintf(buf
, "cpu-hwmon\n");
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
);
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);
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
,
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
,
80 static ssize_t
cpu0_temp_label(struct device
*dev
,
81 struct device_attribute
*attr
, char *buf
)
83 return sprintf(buf
, "CPU 0 Temperature\n");
86 static ssize_t
cpu1_temp_label(struct device
*dev
,
87 struct device_attribute
*attr
, char *buf
)
89 return sprintf(buf
, "CPU 1 Temperature\n");
92 static ssize_t
get_cpu0_temp(struct device
*dev
,
93 struct device_attribute
*attr
, char *buf
)
95 int value
= loongson3_cpu_temp(0);
96 return sprintf(buf
, "%d\n", value
);
99 static ssize_t
get_cpu1_temp(struct device
*dev
,
100 struct device_attribute
*attr
, char *buf
)
102 int value
= loongson3_cpu_temp(1);
103 return sprintf(buf
, "%d\n", value
);
106 static int create_sysfs_cputemp_files(struct kobject
*kobj
)
110 ret
= sysfs_create_files(kobj
, hwmon_cputemp1
);
112 goto sysfs_create_temp1_fail
;
114 if (loongson_sysconf
.nr_cpus
<= loongson_sysconf
.cores_per_package
)
117 ret
= sysfs_create_files(kobj
, hwmon_cputemp2
);
119 goto sysfs_create_temp2_fail
;
123 sysfs_create_temp2_fail
:
124 sysfs_remove_files(kobj
, hwmon_cputemp1
);
126 sysfs_create_temp1_fail
:
130 static void remove_sysfs_cputemp_files(struct kobject
*kobj
)
132 sysfs_remove_files(&cpu_hwmon_dev
->kobj
, hwmon_cputemp1
);
134 if (loongson_sysconf
.nr_cpus
> loongson_sysconf
.cores_per_package
)
135 sysfs_remove_files(&cpu_hwmon_dev
->kobj
, hwmon_cputemp2
);
138 #define CPU_THERMAL_THRESHOLD 90000
139 static struct delayed_work thermal_work
;
141 static void do_thermal_timer(struct work_struct
*work
)
143 int value
= loongson3_cpu_temp(0);
144 if (value
<= CPU_THERMAL_THRESHOLD
)
145 schedule_delayed_work(&thermal_work
, msecs_to_jiffies(5000));
147 orderly_poweroff(true);
150 static int __init
loongson_hwmon_init(void)
154 pr_info("Loongson Hwmon Enter...\n");
156 cpu_hwmon_dev
= hwmon_device_register(NULL
);
157 if (IS_ERR(cpu_hwmon_dev
)) {
159 pr_err("hwmon_device_register fail!\n");
160 goto fail_hwmon_device_register
;
163 ret
= sysfs_create_group(&cpu_hwmon_dev
->kobj
,
164 &cpu_hwmon_attribute_group
);
166 pr_err("fail to create loongson hwmon!\n");
167 goto fail_sysfs_create_group_hwmon
;
170 ret
= create_sysfs_cputemp_files(&cpu_hwmon_dev
->kobj
);
172 pr_err("fail to create cpu temperature interface!\n");
173 goto fail_create_sysfs_cputemp_files
;
176 INIT_DEFERRABLE_WORK(&thermal_work
, do_thermal_timer
);
177 schedule_delayed_work(&thermal_work
, msecs_to_jiffies(20000));
181 fail_create_sysfs_cputemp_files
:
182 sysfs_remove_group(&cpu_hwmon_dev
->kobj
,
183 &cpu_hwmon_attribute_group
);
185 fail_sysfs_create_group_hwmon
:
186 hwmon_device_unregister(cpu_hwmon_dev
);
188 fail_hwmon_device_register
:
192 static void __exit
loongson_hwmon_exit(void)
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
);
201 module_init(loongson_hwmon_init
);
202 module_exit(loongson_hwmon_exit
);
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");