diff options
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r-- | arch/arm/vfp/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/vfp/vfphw.S | 1 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 82 |
3 files changed, 62 insertions, 25 deletions
diff --git a/arch/arm/vfp/Makefile b/arch/arm/vfp/Makefile index 39f6d8e1af73..6de73aab0195 100644 --- a/arch/arm/vfp/Makefile +++ b/arch/arm/vfp/Makefile | |||
@@ -4,8 +4,8 @@ | |||
4 | # Copyright (C) 2001 ARM Limited | 4 | # Copyright (C) 2001 ARM Limited |
5 | # | 5 | # |
6 | 6 | ||
7 | # EXTRA_CFLAGS := -DDEBUG | 7 | # ccflags-y := -DDEBUG |
8 | # EXTRA_AFLAGS := -DDEBUG | 8 | # asflags-y := -DDEBUG |
9 | 9 | ||
10 | KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp) | 10 | KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp) |
11 | LDFLAGS +=--no-warn-mismatch | 11 | LDFLAGS +=--no-warn-mismatch |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index d66cead97d28..9897dcfc16d6 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
@@ -206,6 +206,7 @@ ENTRY(vfp_save_state) | |||
206 | mov pc, lr | 206 | mov pc, lr |
207 | ENDPROC(vfp_save_state) | 207 | ENDPROC(vfp_save_state) |
208 | 208 | ||
209 | .align | ||
209 | last_VFP_context_address: | 210 | last_VFP_context_address: |
210 | .word last_VFP_context | 211 | .word last_VFP_context |
211 | 212 | ||
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 8063a322c790..f25e7ec89416 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -10,9 +10,12 @@ | |||
10 | */ | 10 | */ |
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/types.h> | 12 | #include <linux/types.h> |
13 | #include <linux/cpu.h> | ||
13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/notifier.h> | ||
14 | #include <linux/signal.h> | 16 | #include <linux/signal.h> |
15 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
18 | #include <linux/smp.h> | ||
16 | #include <linux/init.h> | 19 | #include <linux/init.h> |
17 | 20 | ||
18 | #include <asm/cputype.h> | 21 | #include <asm/cputype.h> |
@@ -75,6 +78,14 @@ static void vfp_thread_exit(struct thread_info *thread) | |||
75 | put_cpu(); | 78 | put_cpu(); |
76 | } | 79 | } |
77 | 80 | ||
81 | static void vfp_thread_copy(struct thread_info *thread) | ||
82 | { | ||
83 | struct thread_info *parent = current_thread_info(); | ||
84 | |||
85 | vfp_sync_hwstate(parent); | ||
86 | thread->vfpstate = parent->vfpstate; | ||
87 | } | ||
88 | |||
78 | /* | 89 | /* |
79 | * When this function is called with the following 'cmd's, the following | 90 | * When this function is called with the following 'cmd's, the following |
80 | * is true while this function is being run: | 91 | * is true while this function is being run: |
@@ -101,12 +112,17 @@ static void vfp_thread_exit(struct thread_info *thread) | |||
101 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | 112 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) |
102 | { | 113 | { |
103 | struct thread_info *thread = v; | 114 | struct thread_info *thread = v; |
115 | u32 fpexc; | ||
116 | #ifdef CONFIG_SMP | ||
117 | unsigned int cpu; | ||
118 | #endif | ||
104 | 119 | ||
105 | if (likely(cmd == THREAD_NOTIFY_SWITCH)) { | 120 | switch (cmd) { |
106 | u32 fpexc = fmrx(FPEXC); | 121 | case THREAD_NOTIFY_SWITCH: |
122 | fpexc = fmrx(FPEXC); | ||
107 | 123 | ||
108 | #ifdef CONFIG_SMP | 124 | #ifdef CONFIG_SMP |
109 | unsigned int cpu = thread->cpu; | 125 | cpu = thread->cpu; |
110 | 126 | ||
111 | /* | 127 | /* |
112 | * On SMP, if VFP is enabled, save the old state in | 128 | * On SMP, if VFP is enabled, save the old state in |
@@ -131,13 +147,20 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
131 | * old state. | 147 | * old state. |
132 | */ | 148 | */ |
133 | fmxr(FPEXC, fpexc & ~FPEXC_EN); | 149 | fmxr(FPEXC, fpexc & ~FPEXC_EN); |
134 | return NOTIFY_DONE; | 150 | break; |
135 | } | ||
136 | 151 | ||
137 | if (cmd == THREAD_NOTIFY_FLUSH) | 152 | case THREAD_NOTIFY_FLUSH: |
138 | vfp_thread_flush(thread); | 153 | vfp_thread_flush(thread); |
139 | else | 154 | break; |
155 | |||
156 | case THREAD_NOTIFY_EXIT: | ||
140 | vfp_thread_exit(thread); | 157 | vfp_thread_exit(thread); |
158 | break; | ||
159 | |||
160 | case THREAD_NOTIFY_COPY: | ||
161 | vfp_thread_copy(thread); | ||
162 | break; | ||
163 | } | ||
141 | 164 | ||
142 | return NOTIFY_DONE; | 165 | return NOTIFY_DONE; |
143 | } | 166 | } |
@@ -150,7 +173,7 @@ static struct notifier_block vfp_notifier_block = { | |||
150 | * Raise a SIGFPE for the current process. | 173 | * Raise a SIGFPE for the current process. |
151 | * sicode describes the signal being raised. | 174 | * sicode describes the signal being raised. |
152 | */ | 175 | */ |
153 | void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) | 176 | static void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) |
154 | { | 177 | { |
155 | siginfo_t info; | 178 | siginfo_t info; |
156 | 179 | ||
@@ -375,9 +398,9 @@ static void vfp_enable(void *unused) | |||
375 | } | 398 | } |
376 | 399 | ||
377 | #ifdef CONFIG_PM | 400 | #ifdef CONFIG_PM |
378 | #include <linux/sysdev.h> | 401 | #include <linux/syscore_ops.h> |
379 | 402 | ||
380 | static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) | 403 | static int vfp_pm_suspend(void) |
381 | { | 404 | { |
382 | struct thread_info *ti = current_thread_info(); | 405 | struct thread_info *ti = current_thread_info(); |
383 | u32 fpexc = fmrx(FPEXC); | 406 | u32 fpexc = fmrx(FPEXC); |
@@ -397,34 +420,25 @@ static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) | |||
397 | return 0; | 420 | return 0; |
398 | } | 421 | } |
399 | 422 | ||
400 | static int vfp_pm_resume(struct sys_device *dev) | 423 | static void vfp_pm_resume(void) |
401 | { | 424 | { |
402 | /* ensure we have access to the vfp */ | 425 | /* ensure we have access to the vfp */ |
403 | vfp_enable(NULL); | 426 | vfp_enable(NULL); |
404 | 427 | ||
405 | /* and disable it to ensure the next usage restores the state */ | 428 | /* and disable it to ensure the next usage restores the state */ |
406 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | 429 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); |
407 | |||
408 | return 0; | ||
409 | } | 430 | } |
410 | 431 | ||
411 | static struct sysdev_class vfp_pm_sysclass = { | 432 | static struct syscore_ops vfp_pm_syscore_ops = { |
412 | .name = "vfp", | ||
413 | .suspend = vfp_pm_suspend, | 433 | .suspend = vfp_pm_suspend, |
414 | .resume = vfp_pm_resume, | 434 | .resume = vfp_pm_resume, |
415 | }; | 435 | }; |
416 | 436 | ||
417 | static struct sys_device vfp_pm_sysdev = { | ||
418 | .cls = &vfp_pm_sysclass, | ||
419 | }; | ||
420 | |||
421 | static void vfp_pm_init(void) | 437 | static void vfp_pm_init(void) |
422 | { | 438 | { |
423 | sysdev_class_register(&vfp_pm_sysclass); | 439 | register_syscore_ops(&vfp_pm_syscore_ops); |
424 | sysdev_register(&vfp_pm_sysdev); | ||
425 | } | 440 | } |
426 | 441 | ||
427 | |||
428 | #else | 442 | #else |
429 | static inline void vfp_pm_init(void) { } | 443 | static inline void vfp_pm_init(void) { } |
430 | #endif /* CONFIG_PM */ | 444 | #endif /* CONFIG_PM */ |
@@ -484,7 +498,27 @@ void vfp_flush_hwstate(struct thread_info *thread) | |||
484 | put_cpu(); | 498 | put_cpu(); |
485 | } | 499 | } |
486 | 500 | ||
487 | #include <linux/smp.h> | 501 | /* |
502 | * VFP hardware can lose all context when a CPU goes offline. | ||
503 | * As we will be running in SMP mode with CPU hotplug, we will save the | ||
504 | * hardware state at every thread switch. We clear our held state when | ||
505 | * a CPU has been killed, indicating that the VFP hardware doesn't contain | ||
506 | * a threads VFP state. When a CPU starts up, we re-enable access to the | ||
507 | * VFP hardware. | ||
508 | * | ||
509 | * Both CPU_DYING and CPU_STARTING are called on the CPU which | ||
510 | * is being offlined/onlined. | ||
511 | */ | ||
512 | static int vfp_hotplug(struct notifier_block *b, unsigned long action, | ||
513 | void *hcpu) | ||
514 | { | ||
515 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { | ||
516 | unsigned int cpu = (long)hcpu; | ||
517 | last_VFP_context[cpu] = NULL; | ||
518 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) | ||
519 | vfp_enable(NULL); | ||
520 | return NOTIFY_OK; | ||
521 | } | ||
488 | 522 | ||
489 | /* | 523 | /* |
490 | * VFP support code initialisation. | 524 | * VFP support code initialisation. |
@@ -514,6 +548,8 @@ static int __init vfp_init(void) | |||
514 | else if (vfpsid & FPSID_NODOUBLE) { | 548 | else if (vfpsid & FPSID_NODOUBLE) { |
515 | printk("no double precision support\n"); | 549 | printk("no double precision support\n"); |
516 | } else { | 550 | } else { |
551 | hotcpu_notifier(vfp_hotplug, 0); | ||
552 | |||
517 | smp_call_function(vfp_enable, NULL, 1); | 553 | smp_call_function(vfp_enable, NULL, 1); |
518 | 554 | ||
519 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ | 555 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ |