linux/drivers/platform/mips/cpu_hwmon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2#include <linux/err.h>
   3#include <linux/module.h>
   4#include <linux/reboot.h>
   5#include <linux/jiffies.h>
   6#include <linux/hwmon.h>
   7#include <linux/hwmon-sysfs.h>
   8
   9#include <loongson.h>
  10#include <boot_param.h>
  11#include <loongson_hwmon.h>
  12#include <loongson_regs.h>
  13
  14static int csr_temp_enable;
  15
  16/*
  17 * Loongson-3 series cpu has two sensors inside,
  18 * each of them from 0 to 255,
  19 * if more than 127, that is dangerous.
  20 * here only provide sensor1 data, because it always hot than sensor0
  21 */
  22int loongson3_cpu_temp(int cpu)
  23{
  24        u32 reg, prid_rev;
  25
  26        if (csr_temp_enable) {
  27                reg = (csr_readl(LOONGSON_CSR_CPUTEMP) & 0xff);
  28                goto out;
  29        }
  30
  31        reg = LOONGSON_CHIPTEMP(cpu);
  32        prid_rev = read_c0_prid() & PRID_REV_MASK;
  33
  34        switch (prid_rev) {
  35        case PRID_REV_LOONGSON3A_R1:
  36                reg = (reg >> 8) & 0xff;
  37                break;
  38        case PRID_REV_LOONGSON3B_R1:
  39        case PRID_REV_LOONGSON3B_R2:
  40        case PRID_REV_LOONGSON3A_R2_0:
  41        case PRID_REV_LOONGSON3A_R2_1:
  42                reg = ((reg >> 8) & 0xff) - 100;
  43                break;
  44        case PRID_REV_LOONGSON3A_R3_0:
  45        case PRID_REV_LOONGSON3A_R3_1:
  46        default:
  47                reg = (reg & 0xffff) * 731 / 0x4000 - 273;
  48                break;
  49        }
  50
  51out:
  52        return (int)reg * 1000;
  53}
  54
  55static int nr_packages;
  56static struct device *cpu_hwmon_dev;
  57
  58static SENSOR_DEVICE_ATTR(name, 0444, NULL, NULL, 0);
  59
  60static struct attribute *cpu_hwmon_attributes[] = {
  61        &sensor_dev_attr_name.dev_attr.attr,
  62        NULL
  63};
  64
  65/* Hwmon device attribute group */
  66static struct attribute_group cpu_hwmon_attribute_group = {
  67        .attrs = cpu_hwmon_attributes,
  68};
  69
  70static ssize_t get_cpu_temp(struct device *dev,
  71                        struct device_attribute *attr, char *buf);
  72static ssize_t cpu_temp_label(struct device *dev,
  73                        struct device_attribute *attr, char *buf);
  74
  75static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1);
  76static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL, 1);
  77static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2);
  78static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL, 2);
  79static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3);
  80static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL, 3);
  81static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4);
  82static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL, 4);
  83
  84static const struct attribute *hwmon_cputemp[4][3] = {
  85        {
  86                &sensor_dev_attr_temp1_input.dev_attr.attr,
  87                &sensor_dev_attr_temp1_label.dev_attr.attr,
  88                NULL
  89        },
  90        {
  91                &sensor_dev_attr_temp2_input.dev_attr.attr,
  92                &sensor_dev_attr_temp2_label.dev_attr.attr,
  93                NULL
  94        },
  95        {
  96                &sensor_dev_attr_temp3_input.dev_attr.attr,
  97                &sensor_dev_attr_temp3_label.dev_attr.attr,
  98                NULL
  99        },
 100        {
 101                &sensor_dev_attr_temp4_input.dev_attr.attr,
 102                &sensor_dev_attr_temp4_label.dev_attr.attr,
 103                NULL
 104        }
 105};
 106
 107static ssize_t cpu_temp_label(struct device *dev,
 108                        struct device_attribute *attr, char *buf)
 109{
 110        int id = (to_sensor_dev_attr(attr))->index - 1;
 111
 112        return sprintf(buf, "CPU %d Temperature\n", id);
 113}
 114
 115static ssize_t get_cpu_temp(struct device *dev,
 116                        struct device_attribute *attr, char *buf)
 117{
 118        int id = (to_sensor_dev_attr(attr))->index - 1;
 119        int value = loongson3_cpu_temp(id);
 120
 121        return sprintf(buf, "%d\n", value);
 122}
 123
 124static int create_sysfs_cputemp_files(struct kobject *kobj)
 125{
 126        int i, ret = 0;
 127
 128        for (i = 0; i < nr_packages; i++)
 129                ret = sysfs_create_files(kobj, hwmon_cputemp[i]);
 130
 131        return ret;
 132}
 133
 134static void remove_sysfs_cputemp_files(struct kobject *kobj)
 135{
 136        int i;
 137
 138        for (i = 0; i < nr_packages; i++)
 139                sysfs_remove_files(kobj, hwmon_cputemp[i]);
 140}
 141
 142#define CPU_THERMAL_THRESHOLD 90000
 143static struct delayed_work thermal_work;
 144
 145static void do_thermal_timer(struct work_struct *work)
 146{
 147        int i, value;
 148
 149        for (i = 0; i < nr_packages; i++) {
 150                value = loongson3_cpu_temp(i);
 151                if (value > CPU_THERMAL_THRESHOLD) {
 152                        pr_emerg("Power off due to high temp: %d\n", value);
 153                        orderly_poweroff(true);
 154                }
 155        }
 156
 157        schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
 158}
 159
 160static int __init loongson_hwmon_init(void)
 161{
 162        int ret;
 163
 164        pr_info("Loongson Hwmon Enter...\n");
 165
 166        if (cpu_has_csr())
 167                csr_temp_enable = csr_readl(LOONGSON_CSR_FEATURES) &
 168                                  LOONGSON_CSRF_TEMP;
 169
 170        cpu_hwmon_dev = hwmon_device_register_with_info(NULL, "cpu_hwmon", NULL, NULL, NULL);
 171        if (IS_ERR(cpu_hwmon_dev)) {
 172                ret = PTR_ERR(cpu_hwmon_dev);
 173                pr_err("hwmon_device_register fail!\n");
 174                goto fail_hwmon_device_register;
 175        }
 176
 177        nr_packages = loongson_sysconf.nr_cpus /
 178                loongson_sysconf.cores_per_package;
 179
 180        ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
 181        if (ret) {
 182                pr_err("fail to create cpu temperature interface!\n");
 183                goto fail_create_sysfs_cputemp_files;
 184        }
 185
 186        INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
 187        schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
 188
 189        return ret;
 190
 191fail_create_sysfs_cputemp_files:
 192        sysfs_remove_group(&cpu_hwmon_dev->kobj,
 193                                &cpu_hwmon_attribute_group);
 194        hwmon_device_unregister(cpu_hwmon_dev);
 195
 196fail_hwmon_device_register:
 197        return ret;
 198}
 199
 200static void __exit loongson_hwmon_exit(void)
 201{
 202        cancel_delayed_work_sync(&thermal_work);
 203        remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
 204        sysfs_remove_group(&cpu_hwmon_dev->kobj,
 205                                &cpu_hwmon_attribute_group);
 206        hwmon_device_unregister(cpu_hwmon_dev);
 207}
 208
 209module_init(loongson_hwmon_init);
 210module_exit(loongson_hwmon_exit);
 211
 212MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>");
 213MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>");
 214MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
 215MODULE_LICENSE("GPL");
 216