Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/rcu-doc-2.6
[pandora-kernel.git] / arch / arm / vfp / vfpmodule.c
index 9f476a1..01599c4 100644 (file)
@@ -266,7 +266,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
                 * on VFP subarch 1.
                 */
                 vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs);
-                return;
+               goto exit;
        }
 
        /*
@@ -297,7 +297,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
         * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1.
         */
        if (fpexc ^ (FPEXC_EX | FPEXC_FP2V))
-               return;
+               goto exit;
 
        /*
         * The barrier() here prevents fpinst2 being read
@@ -310,6 +310,8 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
        exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs);
        if (exceptions)
                vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs);
+ exit:
+       preempt_enable();
 }
 
 static void vfp_enable(void *unused)
@@ -377,6 +379,55 @@ static void vfp_pm_init(void)
 static inline void vfp_pm_init(void) { }
 #endif /* CONFIG_PM */
 
+/*
+ * Synchronise the hardware VFP state of a thread other than current with the
+ * saved one. This function is used by the ptrace mechanism.
+ */
+#ifdef CONFIG_SMP
+void vfp_sync_state(struct thread_info *thread)
+{
+       /*
+        * On SMP systems, the VFP state is automatically saved at every
+        * context switch. We mark the thread VFP state as belonging to a
+        * non-existent CPU so that the saved one will be reloaded when
+        * needed.
+        */
+       thread->vfpstate.hard.cpu = NR_CPUS;
+}
+#else
+void vfp_sync_state(struct thread_info *thread)
+{
+       unsigned int cpu = get_cpu();
+       u32 fpexc = fmrx(FPEXC);
+
+       /*
+        * If VFP is enabled, the previous state was already saved and
+        * last_VFP_context updated.
+        */
+       if (fpexc & FPEXC_EN)
+               goto out;
+
+       if (!last_VFP_context[cpu])
+               goto out;
+
+       /*
+        * Save the last VFP state on this CPU.
+        */
+       fmxr(FPEXC, fpexc | FPEXC_EN);
+       vfp_save_state(last_VFP_context[cpu], fpexc);
+       fmxr(FPEXC, fpexc);
+
+       /*
+        * Set the context to NULL to force a reload the next time the thread
+        * uses the VFP.
+        */
+       last_VFP_context[cpu] = NULL;
+
+out:
+       put_cpu();
+}
+#endif
+
 #include <linux/smp.h>
 
 /*
@@ -427,6 +478,18 @@ static int __init vfp_init(void)
                 * in place; report VFP support to userspace.
                 */
                elf_hwcap |= HWCAP_VFP;
+#ifdef CONFIG_VFPv3
+               if (VFP_arch >= 3) {
+                       elf_hwcap |= HWCAP_VFPv3;
+
+                       /*
+                        * Check for VFPv3 D16. CPUs in this configuration
+                        * only have 16 x 64bit registers.
+                        */
+                       if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK)) == 1)
+                               elf_hwcap |= HWCAP_VFPv3D16;
+               }
+#endif
 #ifdef CONFIG_NEON
                /*
                 * Check for the presence of the Advanced SIMD