@@ -548,6 +548,13 @@ u32 dt_property_get_cell(const struct dt_property *prop, u32 index)
return fdt32_to_cpu(((const u32 *)prop->prop)[index]);
}
+void dt_property_set_cell(struct dt_property *prop, u32 index, u32 val)
+{
+ assert(prop->len >= (index+1)*sizeof(u32));
+ /* Always aligned, so this works. */
+ ((u32 *)prop->prop)[index] = cpu_to_fdt32(val);
+}
+
/* First child of this node. */
struct dt_node *dt_first(const struct dt_node *root)
{
@@ -703,6 +703,8 @@ static void per_thread_sanity_checks(void)
/* Called from head.S, thus no prototype. */
void main_cpu_entry(const void *fdt);
+extern void mambo_add_cpu_features(struct dt_node *root);
+
void __noreturn __nomcount main_cpu_entry(const void *fdt)
{
/*
@@ -774,6 +776,7 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
abort();
} else {
dt_expand(fdt);
+ mambo_add_cpu_features(dt_root);
}
/* Now that we have a full devicetree, verify that we aren't on fire. */
@@ -21,6 +21,305 @@
#include "hdata.h"
+/* Table to set up the /cpus/features dt */
+#define USABLE_PR (1U << 0)
+#define USABLE_OS (1U << 1)
+#define USABLE_HV (1U << 2)
+
+#define HV_SUPPORT_NONE 0
+#define HV_SUPPORT_CUSTOM 1
+#define HV_SUPPORT_HFSCR 2
+
+#define OS_SUPPORT_NONE 0
+#define OS_SUPPORT_CUSTOM 1
+#define OS_SUPPORT_FSCR 2
+
+#define CPU_P8 0x1
+#define CPU_P9_DD1 0x2
+#define CPU_P9 0x4
+
+#define CPU_ALL (CPU_P8|CPU_P9_DD1|CPU_P9)
+
+#define ISA_BASE 0
+#define ISA_V207 2070
+#define ISA_V3 3000
+
+struct cpu_feature {
+ const char *name;
+ uint32_t cpus_supported;
+ uint32_t isa;
+ uint32_t usable_mask;
+ uint32_t hv_support;
+ uint32_t os_support;
+ uint32_t hfscr_bit_nr;
+ uint32_t fscr_bit_nr;
+ uint32_t hwcap_bit_nr;
+ const char *dependencies_names; /* space-delimited names */
+};
+
+/*
+ * The base (or NULL) cpu feature set is the CPU features available
+ * when no child nodes of the /cpus/features node exist. The base feature
+ * set is POWER8 (ISA v2.07) less the following features:
+ *
+ * mmu-hash, big-endian, little-endian, smt, strong-access-ordering,
+ * come-from-address-register, PMU-POWER8, PCR-POWER8, idle-nap, subcore,
+ * floating-point, vector, data-stream-control-register,
+ * branch-history-rolling-buffer, transactional-memory, event-based-branch,
+ * target-address-register, processor-control-facility, control-register.
+ */
+
+static const struct cpu_feature cpu_features_table[] = {
+ { "big-endian", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "little-endian", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "hypervisor", CPU_ALL,
+ ISA_BASE, USABLE_HV, HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+ -1, -1, -1,
+ NULL, },
+
+ { "mmu-hash", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "smt", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, 17,
+ NULL, },
+
+ { "strong-access-ordering", CPU_P8|CPU_P9,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "come-from-address-register", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "machine-check-exception-POWER8", CPU_P8,
+ ISA_V207, USABLE_HV, HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+ -1, -1, -1,
+ NULL, },
+ { "performance-monitor-POWER8", CPU_P8,
+ ISA_V207, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "vrmasd", CPU_P8,
+ ISA_V207, USABLE_HV, HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+ -1, -1, -1,
+ NULL, },
+
+ { "idle-nap", CPU_P8,
+ ISA_V207, USABLE_HV, HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+ -1, -1, -1,
+ NULL, },
+
+ { "subcore", CPU_P8,
+ ISA_V207, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+
+ { "floating-point", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 63, -1, 4,
+ NULL, },
+ { "vector", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 62, -1, -1 /* 3 and 24 */,
+ "floating-point", },
+ { "decimal-floating-point", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, 21,
+ "floating-point", },
+ { "data-stream-control-register", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 61, -1, 34,
+ NULL, },
+
+ { "branch-history-rolling-buffer", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 59, -1, -1,
+ NULL, },
+ { "transactional-memory", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 58, -1, 33,
+ NULL, },
+ { "event-based-branch", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 56, 56, 35,
+ NULL, },
+ { "target-address-register", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ 55, 55, 37,
+ NULL, },
+ { "control-register", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+ { "processor-control-facility", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+ { "processor-utilization-of-resources-register", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+ { "initiate-coprocessor-store-word", CPU_ALL,
+ ISA_BASE, USABLE_HV|USABLE_OS, HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+ -1, -1, -1,
+ NULL, },
+};
+
+static void add_cpu_feature_nodeps(struct dt_node *features, const struct cpu_feature *f)
+{
+ struct dt_node *feature;
+
+ feature = dt_new(features, f->name);
+ assert(feature);
+
+ dt_add_property_cells(feature, "isa", f->isa);
+ dt_add_property_cells(feature, "usable-mask", f->usable_mask);
+
+ if (f->usable_mask & USABLE_HV) {
+ if (f->hv_support != HV_SUPPORT_NONE) {
+ dt_add_property_cells(feature, "hv-support", f->hv_support);
+ if (f->hfscr_bit_nr != -1)
+ dt_add_property_cells(feature, "hfscr-bit-nr", f->hfscr_bit_nr);
+ } else {
+ assert(f->hfscr_bit_nr == -1);
+ }
+ }
+
+ if (f->usable_mask & USABLE_OS) {
+ if (f->os_support != OS_SUPPORT_NONE) {
+ dt_add_property_cells(feature, "os-support", f->os_support);
+ if (f->fscr_bit_nr != -1)
+ dt_add_property_cells(feature, "fscr-bit-nr", f->fscr_bit_nr);
+ } else {
+ assert(f->fscr_bit_nr == -1);
+ }
+ }
+
+ if (f->usable_mask & USABLE_PR) {
+ if (f->hwcap_bit_nr != -1)
+ dt_add_property_cells(feature, "hwcap-bit-nr", f->hwcap_bit_nr);
+ }
+
+ if (f->dependencies_names)
+ dt_add_property(feature, "dependencies", NULL, 0);
+}
+
+static void add_cpu_features(struct dt_node *cpus, uint32_t cpu_feature_cpu)
+{
+ struct dt_node *features;
+ struct dt_node *feature;
+ int i;
+
+ features = dt_new(cpus, "features");
+ assert(features);
+
+ dt_add_property_string(features, "device_type", "cpu-features");
+
+ for (i = 0; i < ARRAY_SIZE(cpu_features_table); i++) {
+ const struct cpu_feature *f = &cpu_features_table[i];
+
+ if (f->cpus_supported & cpu_feature_cpu)
+ add_cpu_feature_nodeps(features, f);
+ }
+
+ /* dependency construction pass */
+ dt_for_each_node(features, feature) {
+ const struct cpu_feature *f;
+ const char *deps_names;
+ struct dt_property *deps;
+ int nr_deps;
+ int i;
+
+ /* Find features with dependencies */
+
+ deps = __dt_find_property(feature, "dependencies");
+ if (!deps)
+ continue;
+
+ /* Find the matching cpu table */
+ for (i = 0; i < ARRAY_SIZE(cpu_features_table); i++) {
+ f = &cpu_features_table[i];
+ if (!strcmp(f->name, feature->name))
+ break;
+ }
+ assert(f->dependencies_names);
+
+ /*
+ * Count number of depended features and allocate space
+ * for phandles in the property.
+ */
+ deps_names = f->dependencies_names;
+ nr_deps = strcount(deps_names, " ") + 1;
+ dt_resize_property(&deps, nr_deps * sizeof(u32));
+ deps->len = nr_deps * sizeof(u32);
+ printf("resize nr_deps:%d\n", nr_deps);
+
+ /*
+ * For each one, find the depended feature then advance to
+ * next name.
+ */
+ for (i = 0; i < nr_deps; i++) {
+ struct dt_node *dep;
+
+ dt_for_each_node(features, dep) {
+ if (strstarts(deps_names, dep->name))
+ break;
+ }
+
+ printf(" set cell:%d\n", i);
+ dt_property_set_cell(deps, i, dep->phandle);
+
+ /* Advance over the name + delimiter */
+ deps_names += strlen(dep->name) + 1;
+ }
+ }
+}
+
+extern void mambo_add_cpu_features(struct dt_node *root);
+void mambo_add_cpu_features(struct dt_node *root)
+{
+ int version;
+ uint32_t cpu_feature_cpu;
+ struct dt_node *cpus;
+
+ version = mfspr(SPR_PVR);
+ switch(PVR_TYPE(version)) {
+ case PVR_TYPE_P8E:
+ case PVR_TYPE_P8:
+ case PVR_TYPE_P8NVL:
+ cpu_feature_cpu = CPU_P8;
+ break;
+ case PVR_TYPE_P9:
+ if (PVR_VERS_MAJ(version) == 1)
+ cpu_feature_cpu = CPU_P9_DD1;
+ else
+ cpu_feature_cpu = CPU_P9;
+ break;
+ default:
+ cpu_feature_cpu = 0;
+ break;
+ }
+
+ cpus = dt_new_check(root, "cpus");
+
+ add_cpu_features(cpus, cpu_feature_cpu);
+}
+
struct dt_node * add_core_common(struct dt_node *cpus,
const struct sppcia_cpu_cache *cache,
const struct sppaca_cpu_timebase *tb,
@@ -29,6 +328,7 @@ struct dt_node * add_core_common(struct dt_node *cpus,
const char *name;
struct dt_node *cpu;
uint32_t version;
+ uint32_t cpu_feature_cpu = 0;
uint64_t freq;
const uint8_t pa_features_p7[] = {
6, 0,
@@ -98,6 +398,7 @@ struct dt_node * add_core_common(struct dt_node *cpus,
case PVR_TYPE_P8:
case PVR_TYPE_P8NVL:
name = "PowerPC,POWER8";
+ cpu_feature_cpu = CPU_P8;
pa_features = pa_features_p8;
pa_features_size = sizeof(pa_features_p8);
tlb_congruence = 512;
@@ -105,9 +406,11 @@ struct dt_node * add_core_common(struct dt_node *cpus,
case PVR_TYPE_P9:
name = "PowerPC,POWER9";
if (PVR_VERS_MAJ(version) == 1) {
+ cpu_feature_cpu = CPU_P9_DD1;
pa_features = pa_features_p9_dd1;
pa_features_size = sizeof(pa_features_p9_dd1);
} else {
+ cpu_feature_cpu = CPU_P9;
pa_features = pa_features_p9_dd2;
pa_features_size = sizeof(pa_features_p9_dd2);
}
@@ -119,6 +422,8 @@ struct dt_node * add_core_common(struct dt_node *cpus,
pa_features = NULL;
}
+ add_cpu_features(cpus, cpu_feature_cpu);
+
cpu = dt_new_addr(cpus, name, int_server);
assert(cpu);
dt_add_property_string(cpu, "device_type", "cpu");
@@ -125,6 +125,7 @@ void dt_check_del_prop(struct dt_node *node, const char *name);
/* Warning: moves *prop! */
void dt_resize_property(struct dt_property **prop, size_t len);
+void dt_property_set_cell(struct dt_property *prop, u32 index, u32 val);
u32 dt_property_get_cell(const struct dt_property *prop, u32 index);
/* First child of this node. */