MIPS: Loongson-3: Add CPU Hwmon platform driver

This add CPU Hwmon (temperature sensor) platform driver for Loongson-3.

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Cc: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Fuxin Zhang <zhangfx@lemote.com>
Cc: Zhangjin Wu <wuzhangjin@gmail.com>
Patchwork: https://patchwork.linux-mips.org/patch/9617/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Huacai Chen 2015-03-29 10:54:09 +08:00 committed by Ralf Baechle
parent f14ceff755
commit 64f09aa967
7 changed files with 251 additions and 0 deletions

View File

@ -255,6 +255,10 @@ static inline void do_perfcnt_IRQ(void)
extern u64 loongson_chipcfg[MAX_PACKAGES]; extern u64 loongson_chipcfg[MAX_PACKAGES];
#define LOONGSON_CHIPCFG(id) (*(volatile u32 *)(loongson_chipcfg[id])) #define LOONGSON_CHIPCFG(id) (*(volatile u32 *)(loongson_chipcfg[id]))
/* Chip Temperature registor of each physical cpu package, PRid >= Loongson-3A */
extern u64 loongson_chiptemp[MAX_PACKAGES];
#define LOONGSON_CHIPTEMP(id) (*(volatile u32 *)(loongson_chiptemp[id]))
/* Freq Control register of each physical cpu package, PRid >= Loongson-3B */ /* Freq Control register of each physical cpu package, PRid >= Loongson-3B */
extern u64 loongson_freqctrl[MAX_PACKAGES]; extern u64 loongson_freqctrl[MAX_PACKAGES];
#define LOONGSON_FREQCTRL(id) (*(volatile u32 *)(loongson_freqctrl[id])) #define LOONGSON_FREQCTRL(id) (*(volatile u32 *)(loongson_freqctrl[id]))

View File

@ -29,6 +29,7 @@ struct efi_memory_map_loongson *loongson_memmap;
struct loongson_system_configuration loongson_sysconf; struct loongson_system_configuration loongson_sysconf;
u64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180}; u64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180};
u64 loongson_chiptemp[MAX_PACKAGES];
u64 loongson_freqctrl[MAX_PACKAGES]; u64 loongson_freqctrl[MAX_PACKAGES];
unsigned long long smp_group[4]; unsigned long long smp_group[4];
@ -97,6 +98,10 @@ void __init prom_init_env(void)
loongson_chipcfg[1] = 0x900010001fe00180; loongson_chipcfg[1] = 0x900010001fe00180;
loongson_chipcfg[2] = 0x900020001fe00180; loongson_chipcfg[2] = 0x900020001fe00180;
loongson_chipcfg[3] = 0x900030001fe00180; loongson_chipcfg[3] = 0x900030001fe00180;
loongson_chiptemp[0] = 0x900000001fe0019c;
loongson_chiptemp[1] = 0x900010001fe0019c;
loongson_chiptemp[2] = 0x900020001fe0019c;
loongson_chiptemp[3] = 0x900030001fe0019c;
loongson_sysconf.ht_control_base = 0x90000EFDFB000000; loongson_sysconf.ht_control_base = 0x90000EFDFB000000;
loongson_sysconf.workarounds = WORKAROUND_CPUFREQ; loongson_sysconf.workarounds = WORKAROUND_CPUFREQ;
} else if (ecpu->cputype == Loongson_3B) { } else if (ecpu->cputype == Loongson_3B) {
@ -110,6 +115,10 @@ void __init prom_init_env(void)
loongson_chipcfg[1] = 0x900020001fe00180; loongson_chipcfg[1] = 0x900020001fe00180;
loongson_chipcfg[2] = 0x900040001fe00180; loongson_chipcfg[2] = 0x900040001fe00180;
loongson_chipcfg[3] = 0x900060001fe00180; loongson_chipcfg[3] = 0x900060001fe00180;
loongson_chiptemp[0] = 0x900000001fe0019c;
loongson_chiptemp[1] = 0x900020001fe0019c;
loongson_chiptemp[2] = 0x900040001fe0019c;
loongson_chiptemp[3] = 0x900060001fe0019c;
loongson_freqctrl[0] = 0x900000001fe001d0; loongson_freqctrl[0] = 0x900000001fe001d0;
loongson_freqctrl[1] = 0x900020001fe001d0; loongson_freqctrl[1] = 0x900020001fe001d0;
loongson_freqctrl[2] = 0x900040001fe001d0; loongson_freqctrl[2] = 0x900040001fe001d0;

View File

@ -1,6 +1,9 @@
if X86 if X86
source "drivers/platform/x86/Kconfig" source "drivers/platform/x86/Kconfig"
endif endif
if MIPS
source "drivers/platform/mips/Kconfig"
endif
if GOLDFISH if GOLDFISH
source "drivers/platform/goldfish/Kconfig" source "drivers/platform/goldfish/Kconfig"
endif endif

View File

@ -3,6 +3,7 @@
# #
obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_MIPS) += mips/
obj-$(CONFIG_OLPC) += olpc/ obj-$(CONFIG_OLPC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/

View File

@ -0,0 +1,26 @@
#
# MIPS Platform Specific Drivers
#
menuconfig MIPS_PLATFORM_DEVICES
bool "MIPS Platform Specific Device Drivers"
default y
help
Say Y here to get to see options for device drivers of various
MIPS platforms, including vendor-specific netbook/laptop/desktop
extension and hardware monitor drivers. This option itself does
not add any kernel code.
If you say N, all options in this submenu will be skipped and disabled.
if MIPS_PLATFORM_DEVICES
config CPU_HWMON
tristate "Loongson CPU HWMon Driver"
depends on LOONGSON_MACH3X
select HWMON
default y
help
Loongson-3A/3B CPU Hwmon (temperature sensor) driver.
endif # MIPS_PLATFORM_DEVICES

View File

@ -0,0 +1 @@
obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o

View File

@ -0,0 +1,207 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <loongson.h>
#include <boot_param.h>
#include <loongson_hwmon.h>
/*
* Loongson-3 series cpu has two sensors inside,
* each of them from 0 to 255,
* if more than 127, that is dangerous.
* here only provide sensor1 data, because it always hot than sensor0
*/
int loongson3_cpu_temp(int cpu)
{
u32 reg;
reg = LOONGSON_CHIPTEMP(cpu);
if (loongson_sysconf.cputype == Loongson_3A)
reg = (reg >> 8) & 0xff;
else if (loongson_sysconf.cputype == Loongson_3B)
reg = ((reg >> 8) & 0xff) - 100;
return (int)reg * 1000;
}
static struct device *cpu_hwmon_dev;
static ssize_t get_hwmon_name(struct device *dev,
struct device_attribute *attr, char *buf);
static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0);
static struct attribute *cpu_hwmon_attributes[] = {
&sensor_dev_attr_name.dev_attr.attr,
NULL
};
/* Hwmon device attribute group */
static struct attribute_group cpu_hwmon_attribute_group = {
.attrs = cpu_hwmon_attributes,
};
/* Hwmon device get name */
static ssize_t get_hwmon_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "cpu-hwmon\n");
}
static ssize_t get_cpu0_temp(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t get_cpu1_temp(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t cpu0_temp_label(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t cpu1_temp_label(struct device *dev,
struct device_attribute *attr, char *buf);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2);
static const struct attribute *hwmon_cputemp1[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_label.dev_attr.attr,
NULL
};
static const struct attribute *hwmon_cputemp2[] = {
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_label.dev_attr.attr,
NULL
};
static ssize_t cpu0_temp_label(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "CPU 0 Temprature\n");
}
static ssize_t cpu1_temp_label(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "CPU 1 Temprature\n");
}
static ssize_t get_cpu0_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
int value = loongson3_cpu_temp(0);
return sprintf(buf, "%d\n", value);
}
static ssize_t get_cpu1_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
int value = loongson3_cpu_temp(1);
return sprintf(buf, "%d\n", value);
}
static int create_sysfs_cputemp_files(struct kobject *kobj)
{
int ret;
ret = sysfs_create_files(kobj, hwmon_cputemp1);
if (ret)
goto sysfs_create_temp1_fail;
if (loongson_sysconf.nr_cpus <= loongson_sysconf.cores_per_package)
return 0;
ret = sysfs_create_files(kobj, hwmon_cputemp2);
if (ret)
goto sysfs_create_temp2_fail;
return 0;
sysfs_create_temp2_fail:
sysfs_remove_files(kobj, hwmon_cputemp1);
sysfs_create_temp1_fail:
return -1;
}
static void remove_sysfs_cputemp_files(struct kobject *kobj)
{
sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp1);
if (loongson_sysconf.nr_cpus > loongson_sysconf.cores_per_package)
sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp2);
}
#define CPU_THERMAL_THRESHOLD 90000
static struct delayed_work thermal_work;
static void do_thermal_timer(struct work_struct *work)
{
int value = loongson3_cpu_temp(0);
if (value <= CPU_THERMAL_THRESHOLD)
schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
else
orderly_poweroff(true);
}
static int __init loongson_hwmon_init(void)
{
int ret;
pr_info("Loongson Hwmon Enter...\n");
cpu_hwmon_dev = hwmon_device_register(NULL);
if (IS_ERR(cpu_hwmon_dev)) {
ret = -ENOMEM;
pr_err("hwmon_device_register fail!\n");
goto fail_hwmon_device_register;
}
ret = sysfs_create_group(&cpu_hwmon_dev->kobj,
&cpu_hwmon_attribute_group);
if (ret) {
pr_err("fail to create loongson hwmon!\n");
goto fail_sysfs_create_group_hwmon;
}
ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
if (ret) {
pr_err("fail to create cpu temprature interface!\n");
goto fail_create_sysfs_cputemp_files;
}
INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer);
schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000));
return ret;
fail_create_sysfs_cputemp_files:
sysfs_remove_group(&cpu_hwmon_dev->kobj,
&cpu_hwmon_attribute_group);
fail_sysfs_create_group_hwmon:
hwmon_device_unregister(cpu_hwmon_dev);
fail_hwmon_device_register:
return ret;
}
static void __exit loongson_hwmon_exit(void)
{
cancel_delayed_work_sync(&thermal_work);
remove_sysfs_cputemp_files(&cpu_hwmon_dev->kobj);
sysfs_remove_group(&cpu_hwmon_dev->kobj,
&cpu_hwmon_attribute_group);
hwmon_device_unregister(cpu_hwmon_dev);
}
module_init(loongson_hwmon_init);
module_exit(loongson_hwmon_exit);
MODULE_AUTHOR("Yu Xiang <xiangy@lemote.com>");
MODULE_AUTHOR("Huacai Chen <chenhc@lemote.com>");
MODULE_DESCRIPTION("Loongson CPU Hwmon driver");
MODULE_LICENSE("GPL");