[POWERPC] Distinguish POWER6 partition modes and tell userspace
[linux-2.6-block.git] / arch / powerpc / kernel / prom.c
index 16d29d16b96f90b900c66885f679607976a02411..c18dbe77fdc29863eb5a4f3e57c2f5f62e0f93dc 100644 (file)
@@ -538,35 +538,31 @@ static struct ibm_pa_feature {
        {CPU_FTR_REAL_LE, PPC_FEATURE_TRUE_LE, 5, 0, 0},
 };
 
-static void __init check_cpu_pa_features(unsigned long node)
+static void __init scan_features(unsigned long node, unsigned char *ftrs,
+                                unsigned long tablelen,
+                                struct ibm_pa_feature *fp,
+                                unsigned long ft_size)
 {
-       unsigned char *pa_ftrs;
-       unsigned long len, tablelen, i, bit;
-
-       pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen);
-       if (pa_ftrs == NULL)
-               return;
+       unsigned long i, len, bit;
 
        /* find descriptor with type == 0 */
        for (;;) {
                if (tablelen < 3)
                        return;
-               len = 2 + pa_ftrs[0];
+               len = 2 + ftrs[0];
                if (tablelen < len)
                        return;         /* descriptor 0 not found */
-               if (pa_ftrs[1] == 0)
+               if (ftrs[1] == 0)
                        break;
                tablelen -= len;
-               pa_ftrs += len;
+               ftrs += len;
        }
 
        /* loop over bits we know about */
-       for (i = 0; i < ARRAY_SIZE(ibm_pa_features); ++i) {
-               struct ibm_pa_feature *fp = &ibm_pa_features[i];
-
-               if (fp->pabyte >= pa_ftrs[0])
+       for (i = 0; i < ft_size; ++i, ++fp) {
+               if (fp->pabyte >= ftrs[0])
                        continue;
-               bit = (pa_ftrs[2 + fp->pabyte] >> (7 - fp->pabit)) & 1;
+               bit = (ftrs[2 + fp->pabyte] >> (7 - fp->pabit)) & 1;
                if (bit ^ fp->invert) {
                        cur_cpu_spec->cpu_features |= fp->cpu_features;
                        cur_cpu_spec->cpu_user_features |= fp->cpu_user_ftrs;
@@ -577,16 +573,59 @@ static void __init check_cpu_pa_features(unsigned long node)
        }
 }
 
+static void __init check_cpu_pa_features(unsigned long node)
+{
+       unsigned char *pa_ftrs;
+       unsigned long tablelen;
+
+       pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen);
+       if (pa_ftrs == NULL)
+               return;
+
+       scan_features(node, pa_ftrs, tablelen,
+                     ibm_pa_features, ARRAY_SIZE(ibm_pa_features));
+}
+
+static struct feature_property {
+       const char *name;
+       u32 min_value;
+       unsigned long cpu_feature;
+       unsigned long cpu_user_ftr;
+} feature_properties[] __initdata = {
+#ifdef CONFIG_ALTIVEC
+       {"altivec", 0, CPU_FTR_ALTIVEC, PPC_FEATURE_HAS_ALTIVEC},
+       {"ibm,vmx", 1, CPU_FTR_ALTIVEC, PPC_FEATURE_HAS_ALTIVEC},
+#endif /* CONFIG_ALTIVEC */
+#ifdef CONFIG_PPC64
+       {"ibm,dfp", 1, 0, PPC_FEATURE_HAS_DFP},
+       {"ibm,purr", 1, CPU_FTR_PURR, 0},
+       {"ibm,spurr", 1, CPU_FTR_SPURR, 0},
+#endif /* CONFIG_PPC64 */
+};
+
+static void __init check_cpu_feature_properties(unsigned long node)
+{
+       unsigned long i;
+       struct feature_property *fp = feature_properties;
+       const u32 *prop;
+
+       for (i = 0; i < ARRAY_SIZE(feature_properties); ++i, ++fp) {
+               prop = of_get_flat_dt_prop(node, fp->name, NULL);
+               if (prop && *prop >= fp->min_value) {
+                       cur_cpu_spec->cpu_features |= fp->cpu_feature;
+                       cur_cpu_spec->cpu_user_features |= fp->cpu_user_ftr;
+               }
+       }
+}
+
 static int __init early_init_dt_scan_cpus(unsigned long node,
                                          const char *uname, int depth,
                                          void *data)
 {
        static int logical_cpuid = 0;
        char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-#ifdef CONFIG_ALTIVEC
-       u32 *prop;
-#endif
-       u32 *intserv;
+       const u32 *prop;
+       const u32 *intserv;
        int i, nthreads;
        unsigned long len;
        int found = 0;
@@ -643,24 +682,27 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                        intserv[i]);
                boot_cpuid = logical_cpuid;
                set_hard_smp_processor_id(boot_cpuid, intserv[i]);
-       }
 
-#ifdef CONFIG_ALTIVEC
-       /* Check if we have a VMX and eventually update CPU features */
-       prop = (u32 *)of_get_flat_dt_prop(node, "ibm,vmx", NULL);
-       if (prop && (*prop) > 0) {
-               cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
-               cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
-       }
-
-       /* Same goes for Apple's "altivec" property */
-       prop = (u32 *)of_get_flat_dt_prop(node, "altivec", NULL);
-       if (prop) {
-               cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
-               cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
+               /*
+                * PAPR defines "logical" PVR values for cpus that
+                * meet various levels of the architecture:
+                * 0x0f000001   Architecture version 2.04
+                * 0x0f000002   Architecture version 2.05
+                * If the cpu-version property in the cpu node contains
+                * such a value, we call identify_cpu again with the
+                * logical PVR value in order to use the cpu feature
+                * bits appropriate for the architecture level.
+                *
+                * A POWER6 partition in "POWER6 architected" mode
+                * uses the 0x0f000002 PVR value; in POWER5+ mode
+                * it uses 0x0f000001.
+                */
+               prop = of_get_flat_dt_prop(node, "cpu-version", NULL);
+               if (prop && (*prop & 0xff000000) == 0x0f000000)
+                       identify_cpu(0, *prop);
        }
-#endif /* CONFIG_ALTIVEC */
 
+       check_cpu_feature_properties(node);
        check_cpu_pa_features(node);
 
 #ifdef CONFIG_PPC_PSERIES