@@ -340,3 +340,16 @@ Description: POWERNV CPUFreq driver's frequency throttle stats directory and
'policyX/throttle_stats' directory and all the attributes are same as
the /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats directory and
attributes which give the frequency throttle information of the chip.
+
+What: /sys/devices/system/cpu/cpuX/identification/
+ /sys/devices/system/cpu/cpuX/identification/midr
+ /sys/devices/system/cpu/cpuX/identification/revidr
+Date: June 2016
+Contact: Linux ARM Kernel Mailing list <linux-arm-kernel@lists.infradead.org>
+ Linux Kernel mailing list <linux-kernel@vger.kernel.org>
+Description: ARM64 CPU identification registers
+ 'identification' directory exposes the CPU ID registers for
+ identifying model and revision of the CPU.
+ - midr : This file gives contents of Main ID Register (MIDR_EL1).
+ - revidr : This file gives contents of the Revision ID register
+ (REVIDR_EL1).
@@ -29,6 +29,7 @@ struct cpuinfo_arm64 {
u32 reg_cntfrq;
u32 reg_dczid;
u32 reg_midr;
+ u32 reg_revidr;
u64 reg_id_aa64dfr0;
u64 reg_id_aa64dfr1;
@@ -212,6 +212,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
info->reg_ctr = read_cpuid_cachetype();
info->reg_dczid = read_cpuid(DCZID_EL0);
info->reg_midr = read_cpuid_id();
+ info->reg_revidr = read_cpuid(REVIDR_EL1);
info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1);
info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1);
@@ -264,3 +265,82 @@ void __init cpuinfo_store_boot_cpu(void)
boot_cpu_data = *info;
init_cpu_features(&boot_cpu_data);
}
+
+/*
+ * Both MIDR_EL1 and REVIDR_EL1 are 32bit registers. However, per C5.1.1,
+ * "Principles of the System instruction class encoding" in ARM DDI 0487A.i,
+ * when a system register is escribed as 32-bit, this only means that the
+ * upper 32 bits are RES0, not that they will never be made use of. To avoid
+ * changing the ABI for the future, the values are exported as 64bit values.
+ */
+#define CPUINFO_ATTR_RO(_name) \
+ static ssize_t show_##_name (struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+ { \
+ struct cpuinfo_arm64 *info = &per_cpu(cpu_data, dev->id); \
+ if (!cpu_present(dev->id)) \
+ return -ENODEV; \
+ \
+ if (info->reg_midr) \
+ return sprintf(buf, "0x%016x\n", info->reg_##_name); \
+ else \
+ return 0; \
+ } \
+ static DEVICE_ATTR(_name, 0444, show_##_name, NULL)
+
+CPUINFO_ATTR_RO(midr);
+CPUINFO_ATTR_RO(revidr);
+
+static struct attribute *cpuregs_attrs[] = {
+ &dev_attr_midr.attr,
+ &dev_attr_revidr.attr,
+ NULL
+};
+
+static struct attribute_group cpuregs_attr_group = {
+ .attrs = cpuregs_attrs,
+ .name = "identification"
+};
+
+static int __init cpuinfo_regs_init(void)
+{
+ int cpu, finalcpu, ret;
+ struct device *dev;
+
+ cpu_hotplug_disable();
+
+ for_each_present_cpu(cpu) {
+ dev = get_cpu_device(cpu);
+
+ if (!dev) {
+ ret = -ENODEV;
+ break;
+ }
+
+ ret = sysfs_create_group(&dev->kobj, &cpuregs_attr_group);
+ if (ret)
+ break;
+ }
+
+ if (!ret)
+ goto out;
+ /*
+ * We were unable to put down sysfs groups for all the CPUs, revert
+ * all the groups we have placed down s.t. none are visible.
+ * Otherwise we could give a misleading picture of what's present.
+ */
+ finalcpu = cpu;
+ for_each_present_cpu(cpu) {
+ if (cpu == finalcpu)
+ break;
+ dev = get_cpu_device(cpu);
+ if (dev)
+ sysfs_remove_group(&dev->kobj, &cpuregs_attr_group);
+ }
+
+out:
+ cpu_hotplug_enable();
+ return ret;
+}
+
+device_initcall(cpuinfo_regs_init);