From: "Steven J. Hill" <sjhill@xxxxxxxx> Allow reading of CP0 config registers via sysfs for each core in the system. The registers will show up in sysfs at the path: /sys/devices/system/cpu/cpuX/configX Hotplugging of CPUs is also supported. Signed-off-by: Steven J. Hill <sjhill@xxxxxxxx> --- arch/mips/kernel/Makefile | 1 + arch/mips/kernel/sysfs.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 arch/mips/kernel/sysfs.c diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index cc5eec6..0c3eb97 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o +obj-y += sysfs.o ifeq ($(CONFIG_CPU_MIPS32), y) # diff --git a/arch/mips/kernel/sysfs.c b/arch/mips/kernel/sysfs.c new file mode 100644 index 0000000..1c02471 --- /dev/null +++ b/arch/mips/kernel/sysfs.c @@ -0,0 +1,118 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/cpu.h> +#include <linux/smp.h> +#include <linux/percpu.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/stat.h> + +#include <asm/page.h> + +#define read_c0_config0() read_c0_config() + +#define __BUILD_CP0_SYSFS(reg) \ +static ssize_t show_config##reg(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int n = snprintf(buf, PAGE_SIZE-2, "%x\n", \ + read_c0_config##reg()); \ + return n; \ +} \ +static DEVICE_ATTR(config##reg, S_IRUGO, show_config##reg, NULL); + +__BUILD_CP0_SYSFS(0) +__BUILD_CP0_SYSFS(1) +__BUILD_CP0_SYSFS(2) +__BUILD_CP0_SYSFS(3) +__BUILD_CP0_SYSFS(4) +__BUILD_CP0_SYSFS(5) +__BUILD_CP0_SYSFS(6) +__BUILD_CP0_SYSFS(7) + +static DEFINE_PER_CPU(unsigned int, nregs); + +#define __CREATE_CONFIG_ENTRY(reg) \ +if (ok) { \ + device_create_file(dev, &dev_attr_config##reg); \ + ok = read_c0_config##reg() & MIPS_CONF_M; \ + per_cpu(nregs, cpu)++; \ +} + +#define __DELETE_CONFIG_ENTRY(reg) \ +if (reg < per_cpu(nregs, cpu)) \ + device_remove_file(dev, &dev_attr_config##reg); + +static void create_cp0_entries(unsigned int cpu) +{ + struct device *dev = get_cpu_device(cpu); + int ok = 1; + + per_cpu(nregs, cpu) = 0; + __CREATE_CONFIG_ENTRY(0); + __CREATE_CONFIG_ENTRY(1); + __CREATE_CONFIG_ENTRY(2); + __CREATE_CONFIG_ENTRY(3); + __CREATE_CONFIG_ENTRY(4); + __CREATE_CONFIG_ENTRY(5); + __CREATE_CONFIG_ENTRY(6); + __CREATE_CONFIG_ENTRY(7); +} + +#ifdef CONFIG_HOTPLUG_CPU +static void delete_cp0_entries(unsigned int cpu) +{ + struct device *dev = get_cpu_device(cpu); + + __DELETE_CONFIG_ENTRY(0); + __DELETE_CONFIG_ENTRY(1); + __DELETE_CONFIG_ENTRY(2); + __DELETE_CONFIG_ENTRY(3); + __DELETE_CONFIG_ENTRY(4); + __DELETE_CONFIG_ENTRY(5); + __DELETE_CONFIG_ENTRY(6); + __DELETE_CONFIG_ENTRY(7); +} +#endif + +static int __cpuinit sysfs_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned int)(long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + create_cp0_entries(cpu); + break; +#ifdef CONFIG_HOTPLUG_CPU + case CPU_DEAD: + case CPU_DEAD_FROZEN: + delete_cp0_entries(cpu); + break; +#endif + } + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata sysfs_cpu_nb = { + .notifier_call = sysfs_cpu_notify, +}; + +static int __init sysfs_mips_config_registers(void) +{ + int i; + + for_each_online_cpu(i) + create_cp0_entries(i); + register_cpu_notifier(&sysfs_cpu_nb); + return 0; +} +subsys_initcall(sysfs_mips_config_registers); -- 1.7.9.5