|
@@ -626,3 +626,109 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
|
|
|
|
|
|
return err ? -EFAULT : 0;
|
|
return err ? -EFAULT : 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * VFP hardware can lose all context when a CPU goes offline.
|
|
|
|
+ * As we will be running in SMP mode with CPU hotplug, we will save the
|
|
|
|
+ * hardware state at every thread switch. We clear our held state when
|
|
|
|
+ * a CPU has been killed, indicating that the VFP hardware doesn't contain
|
|
|
|
+ * a threads VFP state. When a CPU starts up, we re-enable access to the
|
|
|
|
+ * VFP hardware.
|
|
|
|
+ *
|
|
|
|
+ * Both CPU_DYING and CPU_STARTING are called on the CPU which
|
|
|
|
+ * is being offlined/onlined.
|
|
|
|
+ */
|
|
|
|
+static int vfp_hotplug(struct notifier_block *b, unsigned long action,
|
|
|
|
+ void *hcpu)
|
|
|
|
+{
|
|
|
|
+ if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
|
|
|
|
+ vfp_force_reload((long)hcpu, current_thread_info());
|
|
|
|
+ } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
|
|
|
|
+ vfp_enable(NULL);
|
|
|
|
+ return NOTIFY_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * VFP support code initialisation.
|
|
|
|
+ */
|
|
|
|
+static int __init vfp_init(void)
|
|
|
|
+{
|
|
|
|
+ unsigned int vfpsid;
|
|
|
|
+ unsigned int cpu_arch = cpu_architecture();
|
|
|
|
+
|
|
|
|
+ if (cpu_arch >= CPU_ARCH_ARMv6)
|
|
|
|
+ on_each_cpu(vfp_enable, NULL, 1);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * First check that there is a VFP that we can use.
|
|
|
|
+ * The handler is already setup to just log calls, so
|
|
|
|
+ * we just need to read the VFPSID register.
|
|
|
|
+ */
|
|
|
|
+ vfp_vector = vfp_testing_entry;
|
|
|
|
+ barrier();
|
|
|
|
+ vfpsid = fmrx(FPSID);
|
|
|
|
+ barrier();
|
|
|
|
+ vfp_vector = vfp_null_entry;
|
|
|
|
+
|
|
|
|
+ pr_info("VFP support v0.3: ");
|
|
|
|
+ if (VFP_arch)
|
|
|
|
+ pr_cont("not present\n");
|
|
|
|
+ else if (vfpsid & FPSID_NODOUBLE) {
|
|
|
|
+ pr_cont("no double precision support\n");
|
|
|
|
+ } else {
|
|
|
|
+ hotcpu_notifier(vfp_hotplug, 0);
|
|
|
|
+
|
|
|
|
+ VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */
|
|
|
|
+ pr_cont("implementor %02x architecture %d part %02x variant %x rev %x\n",
|
|
|
|
+ (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT,
|
|
|
|
+ (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT,
|
|
|
|
+ (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT,
|
|
|
|
+ (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,
|
|
|
|
+ (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);
|
|
|
|
+
|
|
|
|
+ vfp_vector = vfp_support_entry;
|
|
|
|
+
|
|
|
|
+ thread_register_notifier(&vfp_notifier_block);
|
|
|
|
+ vfp_pm_init();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We detected VFP, and the support code is
|
|
|
|
+ * in place; report VFP support to userspace.
|
|
|
|
+ */
|
|
|
|
+ elf_hwcap |= HWCAP_VFP;
|
|
|
|
+#ifdef CONFIG_VFPv3
|
|
|
|
+ if (VFP_arch >= 2) {
|
|
|
|
+ elf_hwcap |= HWCAP_VFPv3;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check for VFPv3 D16 and VFPv4 D16. CPUs in
|
|
|
|
+ * this configuration only have 16 x 64bit
|
|
|
|
+ * registers.
|
|
|
|
+ */
|
|
|
|
+ if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK)) == 1)
|
|
|
|
+ elf_hwcap |= HWCAP_VFPv3D16; /* also v4-D16 */
|
|
|
|
+ else
|
|
|
|
+ elf_hwcap |= HWCAP_VFPD32;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ /*
|
|
|
|
+ * Check for the presence of the Advanced SIMD
|
|
|
|
+ * load/store instructions, integer and single
|
|
|
|
+ * precision floating point operations. Only check
|
|
|
|
+ * for NEON if the hardware has the MVFR registers.
|
|
|
|
+ */
|
|
|
|
+ if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) {
|
|
|
|
+#ifdef CONFIG_NEON
|
|
|
|
+ if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100)
|
|
|
|
+ elf_hwcap |= HWCAP_NEON;
|
|
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_VFPv3
|
|
|
|
+ if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000)
|
|
|
|
+ elf_hwcap |= HWCAP_VFPv4;
|
|
|
|
+#endif
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+late_initcall(vfp_init);
|