1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020 Loongson Technology Co., Ltd. 4 */ 5#include <linux/err.h> 6#include <linux/module.h> 7#include <linux/reboot.h> 8#include <linux/jiffies.h> 9#include <linux/hwmon.h> 10#include <linux/hwmon-sysfs.h> 11 12#include <loongson.h> 13 14static int nr_packages; 15static struct device *cpu_hwmon_dev; 16 17static int loongson3_cpu_temp(int cpu) 18{ 19 u32 reg; 20 21 reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff; 22 23 return (int)((s8)reg) * 1000; 24} 25 26static ssize_t cpu_temp_label(struct device *dev, 27 struct device_attribute *attr, char *buf) 28{ 29 int id = (to_sensor_dev_attr(attr))->index - 1; 30 return sprintf(buf, "CPU %d Temperature\n", id); 31} 32 33static ssize_t get_cpu_temp(struct device *dev, 34 struct device_attribute *attr, char *buf) 35{ 36 int id = (to_sensor_dev_attr(attr))->index - 1; 37 int value = loongson3_cpu_temp(id); 38 return sprintf(buf, "%d\n", value); 39} 40 41static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1); 42static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL, 1); 43static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2); 44static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL, 2); 45static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3); 46static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL, 3); 47static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4); 48static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL, 4); 49static SENSOR_DEVICE_ATTR(temp5_input, 0444, get_cpu_temp, NULL, 4); 50static SENSOR_DEVICE_ATTR(temp5_label, 0444, cpu_temp_label, NULL, 4); 51static SENSOR_DEVICE_ATTR(temp6_input, 0444, get_cpu_temp, NULL, 4); 52static SENSOR_DEVICE_ATTR(temp6_label, 0444, cpu_temp_label, NULL, 4); 53static SENSOR_DEVICE_ATTR(temp7_input, 0444, get_cpu_temp, NULL, 4); 54static SENSOR_DEVICE_ATTR(temp7_label, 0444, cpu_temp_label, NULL, 4); 55static SENSOR_DEVICE_ATTR(temp8_input, 0444, get_cpu_temp, NULL, 4); 56static SENSOR_DEVICE_ATTR(temp8_label, 0444, cpu_temp_label, NULL, 4); 57static SENSOR_DEVICE_ATTR(temp9_input, 0444, get_cpu_temp, NULL, 4); 58static SENSOR_DEVICE_ATTR(temp9_label, 0444, cpu_temp_label, NULL, 4); 59static SENSOR_DEVICE_ATTR(temp10_input, 0444, get_cpu_temp, NULL, 4); 60static SENSOR_DEVICE_ATTR(temp10_label, 0444, cpu_temp_label, NULL, 4); 61static SENSOR_DEVICE_ATTR(temp11_input, 0444, get_cpu_temp, NULL, 4); 62static SENSOR_DEVICE_ATTR(temp11_label, 0444, cpu_temp_label, NULL, 4); 63static SENSOR_DEVICE_ATTR(temp12_input, 0444, get_cpu_temp, NULL, 4); 64static SENSOR_DEVICE_ATTR(temp12_label, 0444, cpu_temp_label, NULL, 4); 65static SENSOR_DEVICE_ATTR(temp13_input, 0444, get_cpu_temp, NULL, 4); 66static SENSOR_DEVICE_ATTR(temp13_label, 0444, cpu_temp_label, NULL, 4); 67static SENSOR_DEVICE_ATTR(temp14_input, 0444, get_cpu_temp, NULL, 4); 68static SENSOR_DEVICE_ATTR(temp14_label, 0444, cpu_temp_label, NULL, 4); 69static SENSOR_DEVICE_ATTR(temp15_input, 0444, get_cpu_temp, NULL, 4); 70static SENSOR_DEVICE_ATTR(temp15_label, 0444, cpu_temp_label, NULL, 4); 71static SENSOR_DEVICE_ATTR(temp16_input, 0444, get_cpu_temp, NULL, 4); 72static SENSOR_DEVICE_ATTR(temp16_label, 0444, cpu_temp_label, NULL, 4); 73 74static struct attribute *cpu_hwmon_attributes[] = { 75 &sensor_dev_attr_temp1_input.dev_attr.attr, 76 &sensor_dev_attr_temp1_label.dev_attr.attr, 77 &sensor_dev_attr_temp2_input.dev_attr.attr, 78 &sensor_dev_attr_temp2_label.dev_attr.attr, 79 &sensor_dev_attr_temp3_input.dev_attr.attr, 80 &sensor_dev_attr_temp3_label.dev_attr.attr, 81 &sensor_dev_attr_temp4_input.dev_attr.attr, 82 &sensor_dev_attr_temp4_label.dev_attr.attr, 83 &sensor_dev_attr_temp5_input.dev_attr.attr, 84 &sensor_dev_attr_temp5_label.dev_attr.attr, 85 &sensor_dev_attr_temp6_input.dev_attr.attr, 86 &sensor_dev_attr_temp6_label.dev_attr.attr, 87 &sensor_dev_attr_temp7_input.dev_attr.attr, 88 &sensor_dev_attr_temp7_label.dev_attr.attr, 89 &sensor_dev_attr_temp8_input.dev_attr.attr, 90 &sensor_dev_attr_temp8_label.dev_attr.attr, 91 &sensor_dev_attr_temp9_input.dev_attr.attr, 92 &sensor_dev_attr_temp9_label.dev_attr.attr, 93 &sensor_dev_attr_temp10_input.dev_attr.attr, 94 &sensor_dev_attr_temp10_label.dev_attr.attr, 95 &sensor_dev_attr_temp11_input.dev_attr.attr, 96 &sensor_dev_attr_temp11_label.dev_attr.attr, 97 &sensor_dev_attr_temp12_input.dev_attr.attr, 98 &sensor_dev_attr_temp12_label.dev_attr.attr, 99 &sensor_dev_attr_temp13_input.dev_attr.attr, 100 &sensor_dev_attr_temp13_label.dev_attr.attr, 101 &sensor_dev_attr_temp14_input.dev_attr.attr, 102 &sensor_dev_attr_temp14_label.dev_attr.attr, 103 &sensor_dev_attr_temp15_input.dev_attr.attr, 104 &sensor_dev_attr_temp15_label.dev_attr.attr, 105 &sensor_dev_attr_temp16_input.dev_attr.attr, 106 &sensor_dev_attr_temp16_label.dev_attr.attr, 107 NULL 108}; 109static umode_t cpu_hwmon_is_visible(struct kobject *kobj, 110 struct attribute *attr, int i) 111{ 112 int id = i / 2; 113 114 if (id < nr_packages) 115 return attr->mode; 116 return 0; 117} 118 119static struct attribute_group cpu_hwmon_group = { 120 .attrs = cpu_hwmon_attributes, 121 .is_visible = cpu_hwmon_is_visible, 122}; 123 124static const struct attribute_group *cpu_hwmon_groups[] = { 125 &cpu_hwmon_group, 126 NULL 127}; 128 129static int cpu_initial_threshold = 72000; 130static int cpu_thermal_threshold = 96000; 131module_param(cpu_thermal_threshold, int, 0644); 132MODULE_PARM_DESC(cpu_thermal_threshold, "cpu thermal threshold (96000 (default))"); 133 134static struct delayed_work thermal_work; 135 136static void do_thermal_timer(struct work_struct *work) 137{ 138 int i, value, temp_max = 0; 139 140 for (i=0; i<nr_packages; i++) { 141 value = loongson3_cpu_temp(i); 142 if (value > temp_max) 143 temp_max = value; 144 } 145 146 if (temp_max <= cpu_thermal_threshold) 147 schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000)); 148 else 149 orderly_poweroff(true); 150} 151 152static int __init loongson_hwmon_init(void) 153{ 154 int i, value, temp_max = 0; 155 156 pr_info("Loongson Hwmon Enter...\n"); 157 158 nr_packages = loongson_sysconf.nr_cpus / 159 loongson_sysconf.cores_per_package; 160 161 cpu_hwmon_dev = hwmon_device_register_with_groups(NULL, "cpu_hwmon", 162 NULL, cpu_hwmon_groups); 163 if (IS_ERR(cpu_hwmon_dev)) { 164 pr_err("Hwmon register fail with %ld!\n", PTR_ERR(cpu_hwmon_dev)); 165 return PTR_ERR(cpu_hwmon_dev); 166 } 167 168 for (i = 0; i < nr_packages; i++) { 169 value = loongson3_cpu_temp(i); 170 if (value > temp_max) 171 temp_max = value; 172 } 173 174 pr_info("Initial CPU temperature is %d (highest).\n", temp_max); 175 if (temp_max > cpu_initial_threshold) 176 cpu_thermal_threshold += temp_max - cpu_initial_threshold; 177 178 INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); 179 schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); 180 181 return 0; 182} 183 184static void __exit loongson_hwmon_exit(void) 185{ 186 cancel_delayed_work_sync(&thermal_work); 187 hwmon_device_unregister(cpu_hwmon_dev); 188} 189 190module_init(loongson_hwmon_init); 191module_exit(loongson_hwmon_exit); 192 193MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>"); 194MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>"); 195MODULE_DESCRIPTION("Loongson CPU Hwmon driver"); 196MODULE_LICENSE("GPL"); 197