diff options
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/arm/kernel/asm-offsets.c | 3 | ||||
| -rw-r--r-- | arch/arm/vfp/vfphw.S | 43 | ||||
| -rw-r--r-- | arch/arm/vfp/vfpmodule.c | 98 |
3 files changed, 89 insertions, 55 deletions
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 927522cfc12e..16baba2e4369 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c | |||
| @@ -59,6 +59,9 @@ int main(void) | |||
| 59 | DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); | 59 | DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); |
| 60 | DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); | 60 | DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); |
| 61 | DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); | 61 | DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); |
| 62 | #ifdef CONFIG_SMP | ||
| 63 | DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu)); | ||
| 64 | #endif | ||
| 62 | #ifdef CONFIG_ARM_THUMBEE | 65 | #ifdef CONFIG_ARM_THUMBEE |
| 63 | DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); | 66 | DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); |
| 64 | #endif | 67 | #endif |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index 404538ae591d..2d30c7f6edd3 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
| @@ -82,19 +82,22 @@ ENTRY(vfp_support_entry) | |||
| 82 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer | 82 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer |
| 83 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled | 83 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled |
| 84 | cmp r4, r10 @ this thread owns the hw context? | 84 | cmp r4, r10 @ this thread owns the hw context? |
| 85 | #ifndef CONFIG_SMP | ||
| 86 | @ For UP, checking that this thread owns the hw context is | ||
| 87 | @ sufficient to determine that the hardware state is valid. | ||
| 85 | beq vfp_hw_state_valid | 88 | beq vfp_hw_state_valid |
| 86 | 89 | ||
| 90 | @ On UP, we lazily save the VFP context. As a different | ||
| 91 | @ thread wants ownership of the VFP hardware, save the old | ||
| 92 | @ state if there was a previous (valid) owner. | ||
| 93 | |||
| 87 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | 94 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending |
| 88 | @ exceptions, so we can get at the | 95 | @ exceptions, so we can get at the |
| 89 | @ rest of it | 96 | @ rest of it |
| 90 | 97 | ||
| 91 | #ifndef CONFIG_SMP | ||
| 92 | @ Save out the current registers to the old thread state | ||
| 93 | @ No need for SMP since this is not done lazily | ||
| 94 | |||
| 95 | DBGSTR1 "save old state %p", r4 | 98 | DBGSTR1 "save old state %p", r4 |
| 96 | cmp r4, #0 | 99 | cmp r4, #0 @ if the vfp_current_hw_state is NULL |
| 97 | beq no_old_VFP_process | 100 | beq vfp_reload_hw @ then the hw state needs reloading |
| 98 | VFPFSTMIA r4, r5 @ save the working registers | 101 | VFPFSTMIA r4, r5 @ save the working registers |
| 99 | VFPFMRX r5, FPSCR @ current status | 102 | VFPFMRX r5, FPSCR @ current status |
| 100 | #ifndef CONFIG_CPU_FEROCEON | 103 | #ifndef CONFIG_CPU_FEROCEON |
| @@ -107,11 +110,33 @@ ENTRY(vfp_support_entry) | |||
| 107 | 1: | 110 | 1: |
| 108 | #endif | 111 | #endif |
| 109 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 | 112 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 |
| 110 | @ and point r4 at the word at the | 113 | vfp_reload_hw: |
| 111 | @ start of the register dump | 114 | |
| 115 | #else | ||
| 116 | @ For SMP, if this thread does not own the hw context, then we | ||
| 117 | @ need to reload it. No need to save the old state as on SMP, | ||
| 118 | @ we always save the state when we switch away from a thread. | ||
| 119 | bne vfp_reload_hw | ||
| 120 | |||
| 121 | @ This thread has ownership of the current hardware context. | ||
| 122 | @ However, it may have been migrated to another CPU, in which | ||
| 123 | @ case the saved state is newer than the hardware context. | ||
| 124 | @ Check this by looking at the CPU number which the state was | ||
| 125 | @ last loaded onto. | ||
| 126 | ldr ip, [r10, #VFP_CPU] | ||
| 127 | teq ip, r11 | ||
| 128 | beq vfp_hw_state_valid | ||
| 129 | |||
| 130 | vfp_reload_hw: | ||
| 131 | @ We're loading this threads state into the VFP hardware. Update | ||
| 132 | @ the CPU number which contains the most up to date VFP context. | ||
| 133 | str r11, [r10, #VFP_CPU] | ||
| 134 | |||
| 135 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | ||
| 136 | @ exceptions, so we can get at the | ||
| 137 | @ rest of it | ||
| 112 | #endif | 138 | #endif |
| 113 | 139 | ||
| 114 | no_old_VFP_process: | ||
| 115 | DBGSTR1 "load state %p", r10 | 140 | DBGSTR1 "load state %p", r10 |
| 116 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer | 141 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer |
| 117 | @ Load the saved state back into the VFP | 142 | @ Load the saved state back into the VFP |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 3640351171b8..08ff93fa533c 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
| @@ -35,18 +35,51 @@ void vfp_null_entry(void); | |||
| 35 | void (*vfp_vector)(void) = vfp_null_entry; | 35 | void (*vfp_vector)(void) = vfp_null_entry; |
| 36 | 36 | ||
| 37 | /* | 37 | /* |
| 38 | * Dual-use variable. | ||
| 39 | * Used in startup: set to non-zero if VFP checks fail | ||
| 40 | * After startup, holds VFP architecture | ||
| 41 | */ | ||
| 42 | unsigned int VFP_arch; | ||
| 43 | |||
| 44 | /* | ||
| 38 | * The pointer to the vfpstate structure of the thread which currently | 45 | * The pointer to the vfpstate structure of the thread which currently |
| 39 | * owns the context held in the VFP hardware, or NULL if the hardware | 46 | * owns the context held in the VFP hardware, or NULL if the hardware |
| 40 | * context is invalid. | 47 | * context is invalid. |
| 48 | * | ||
| 49 | * For UP, this is sufficient to tell which thread owns the VFP context. | ||
| 50 | * However, for SMP, we also need to check the CPU number stored in the | ||
| 51 | * saved state too to catch migrations. | ||
| 41 | */ | 52 | */ |
| 42 | union vfp_state *vfp_current_hw_state[NR_CPUS]; | 53 | union vfp_state *vfp_current_hw_state[NR_CPUS]; |
| 43 | 54 | ||
| 44 | /* | 55 | /* |
| 45 | * Dual-use variable. | 56 | * Is 'thread's most up to date state stored in this CPUs hardware? |
| 46 | * Used in startup: set to non-zero if VFP checks fail | 57 | * Must be called from non-preemptible context. |
| 47 | * After startup, holds VFP architecture | ||
| 48 | */ | 58 | */ |
| 49 | unsigned int VFP_arch; | 59 | static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread) |
| 60 | { | ||
| 61 | #ifdef CONFIG_SMP | ||
| 62 | if (thread->vfpstate.hard.cpu != cpu) | ||
| 63 | return false; | ||
| 64 | #endif | ||
| 65 | return vfp_current_hw_state[cpu] == &thread->vfpstate; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* | ||
| 69 | * Force a reload of the VFP context from the thread structure. We do | ||
| 70 | * this by ensuring that access to the VFP hardware is disabled, and | ||
| 71 | * clear last_VFP_context. Must be called from non-preemptible context. | ||
| 72 | */ | ||
| 73 | static void vfp_force_reload(unsigned int cpu, struct thread_info *thread) | ||
| 74 | { | ||
| 75 | if (vfp_state_in_hw(cpu, thread)) { | ||
| 76 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||
| 77 | vfp_current_hw_state[cpu] = NULL; | ||
| 78 | } | ||
| 79 | #ifdef CONFIG_SMP | ||
| 80 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 81 | #endif | ||
| 82 | } | ||
| 50 | 83 | ||
| 51 | /* | 84 | /* |
| 52 | * Per-thread VFP initialization. | 85 | * Per-thread VFP initialization. |
| @@ -60,6 +93,9 @@ static void vfp_thread_flush(struct thread_info *thread) | |||
| 60 | 93 | ||
| 61 | vfp->hard.fpexc = FPEXC_EN; | 94 | vfp->hard.fpexc = FPEXC_EN; |
| 62 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | 95 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; |
| 96 | #ifdef CONFIG_SMP | ||
| 97 | vfp->hard.cpu = NR_CPUS; | ||
| 98 | #endif | ||
| 63 | 99 | ||
| 64 | /* | 100 | /* |
| 65 | * Disable VFP to ensure we initialize it first. We must ensure | 101 | * Disable VFP to ensure we initialize it first. We must ensure |
| @@ -90,6 +126,9 @@ static void vfp_thread_copy(struct thread_info *thread) | |||
| 90 | 126 | ||
| 91 | vfp_sync_hwstate(parent); | 127 | vfp_sync_hwstate(parent); |
| 92 | thread->vfpstate = parent->vfpstate; | 128 | thread->vfpstate = parent->vfpstate; |
| 129 | #ifdef CONFIG_SMP | ||
| 130 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 131 | #endif | ||
| 93 | } | 132 | } |
| 94 | 133 | ||
| 95 | /* | 134 | /* |
| @@ -135,17 +174,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
| 135 | * case the thread migrates to a different CPU. The | 174 | * case the thread migrates to a different CPU. The |
| 136 | * restoring is done lazily. | 175 | * restoring is done lazily. |
| 137 | */ | 176 | */ |
| 138 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) { | 177 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) |
| 139 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); | 178 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); |
| 140 | vfp_current_hw_state[cpu]->hard.cpu = cpu; | ||
| 141 | } | ||
| 142 | /* | ||
| 143 | * Thread migration, just force the reloading of the | ||
| 144 | * state on the new CPU in case the VFP registers | ||
| 145 | * contain stale data. | ||
| 146 | */ | ||
| 147 | if (thread->vfpstate.hard.cpu != cpu) | ||
| 148 | vfp_current_hw_state[cpu] = NULL; | ||
| 149 | #endif | 179 | #endif |
| 150 | 180 | ||
| 151 | /* | 181 | /* |
| @@ -449,15 +479,15 @@ static void vfp_pm_init(void) | |||
| 449 | static inline void vfp_pm_init(void) { } | 479 | static inline void vfp_pm_init(void) { } |
| 450 | #endif /* CONFIG_PM */ | 480 | #endif /* CONFIG_PM */ |
| 451 | 481 | ||
| 482 | /* | ||
| 483 | * Ensure that the VFP state stored in 'thread->vfpstate' is up to date | ||
| 484 | * with the hardware state. | ||
| 485 | */ | ||
| 452 | void vfp_sync_hwstate(struct thread_info *thread) | 486 | void vfp_sync_hwstate(struct thread_info *thread) |
| 453 | { | 487 | { |
| 454 | unsigned int cpu = get_cpu(); | 488 | unsigned int cpu = get_cpu(); |
| 455 | 489 | ||
| 456 | /* | 490 | if (vfp_state_in_hw(cpu, thread)) { |
| 457 | * If the thread we're interested in is the current owner of the | ||
| 458 | * hardware VFP state, then we need to save its state. | ||
| 459 | */ | ||
| 460 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { | ||
| 461 | u32 fpexc = fmrx(FPEXC); | 491 | u32 fpexc = fmrx(FPEXC); |
| 462 | 492 | ||
| 463 | /* | 493 | /* |
| @@ -471,36 +501,13 @@ void vfp_sync_hwstate(struct thread_info *thread) | |||
| 471 | put_cpu(); | 501 | put_cpu(); |
| 472 | } | 502 | } |
| 473 | 503 | ||
| 504 | /* Ensure that the thread reloads the hardware VFP state on the next use. */ | ||
| 474 | void vfp_flush_hwstate(struct thread_info *thread) | 505 | void vfp_flush_hwstate(struct thread_info *thread) |
| 475 | { | 506 | { |
| 476 | unsigned int cpu = get_cpu(); | 507 | unsigned int cpu = get_cpu(); |
| 477 | 508 | ||
| 478 | /* | 509 | vfp_force_reload(cpu, thread); |
| 479 | * If the thread we're interested in is the current owner of the | ||
| 480 | * hardware VFP state, then we need to save its state. | ||
| 481 | */ | ||
| 482 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { | ||
| 483 | u32 fpexc = fmrx(FPEXC); | ||
| 484 | 510 | ||
| 485 | fmxr(FPEXC, fpexc & ~FPEXC_EN); | ||
| 486 | |||
| 487 | /* | ||
| 488 | * Set the context to NULL to force a reload the next time | ||
| 489 | * the thread uses the VFP. | ||
| 490 | */ | ||
| 491 | vfp_current_hw_state[cpu] = NULL; | ||
| 492 | } | ||
| 493 | |||
| 494 | #ifdef CONFIG_SMP | ||
| 495 | /* | ||
| 496 | * For SMP we still have to take care of the case where the thread | ||
| 497 | * migrates to another CPU and then back to the original CPU on which | ||
| 498 | * the last VFP user is still the same thread. Mark the thread VFP | ||
| 499 | * state as belonging to a non-existent CPU so that the saved one will | ||
| 500 | * be reloaded in the above case. | ||
| 501 | */ | ||
| 502 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 503 | #endif | ||
| 504 | put_cpu(); | 511 | put_cpu(); |
| 505 | } | 512 | } |
| 506 | 513 | ||
| @@ -519,8 +526,7 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action, | |||
| 519 | void *hcpu) | 526 | void *hcpu) |
| 520 | { | 527 | { |
| 521 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { | 528 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { |
| 522 | unsigned int cpu = (long)hcpu; | 529 | vfp_force_reload((long)hcpu, current_thread_info()); |
| 523 | vfp_current_hw_state[cpu] = NULL; | ||
| 524 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) | 530 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) |
| 525 | vfp_enable(NULL); | 531 | vfp_enable(NULL); |
| 526 | return NOTIFY_OK; | 532 | return NOTIFY_OK; |
