summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorScott Williams <scwilliams@nvidia.com>2011-08-09 18:19:47 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:47:03 -0800
commit607d5ec8bb46f95473533f611da1ffc97907d16e (patch)
tree6e924d0fead8f1c5108c88bdde94be40e5890a75 /arch
parent7b6aed43ac4deef93e61e5f76d68099cf2c6540d (diff)
ARM: tegra: power: Workaround PMD corruption by cpu_resume_mmu
The common ARM CPU state suspend/resume code does not work with and external L2 cache controller (like a PL310) enabled. This change fixes corruption of the current PMD by the MMU resume code. cpu_resume_mmu modifies the currently active page tables to add a flat (VA==PA) section mapping of cpu_resume_turn_mmu_on to handle MMU off-to-on transition. It turns off the L1 data cache but it knows nothing of the L2 cache. Since page table walks are L2 cacheable, other CPUs in the system can pick up the corrupted PMD which will eventually result in a kernel panic. The workaround for this is to modify push_ctx_regs to save the current TTB0 and CONTEXID registers in the CPU register context and switch to the private tegra_pgd before saving the rest of the CPU context. The tegra_pgd already has a flat mapping for the code in question, so it can't be damaged by the actions of cpu_resume_mmu. Likewise, pop_ctx_regs is modified to restore the actual TTB0 and CONTEXTID registers when restoring the CPU registers. Change-Id: Ided2b31cbea0b0abb934e64cf056e85e1a3f06ae Signed-off-by: Scott Williams <scwilliams@nvidia.com> Rebase-Id: R3f6ca9c63752430395fdf8375b82794abc9776af
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/sleep.S21
-rw-r--r--arch/arm/mach-tegra/sleep.h41
2 files changed, 39 insertions, 23 deletions
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index d243c05e1081..64ba9c2eadfd 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -402,27 +402,6 @@ tegra_cpu_resume:
* r1 = physical address to jump to with mmu off
*/
ENTRY(tegra_turn_off_mmu)
- /*
- * change page table pointer to tegra_pgd_phys, so that IRAM
- * and MMU shut-off will be mapped virtual == physical
- */
- mrc p15, 0, r2, c2, c0, 0 @ TTB 0
- mov32 r3, ~PAGE_MASK
- and r2, r2, r3
- ldr r3, tegra_pgd_phys_address
- ldr r3, [r3]
- orr r3, r3, r2
- mov r2, #0
- mcr p15, 0, r2, c13, c0, 1 @ reserved context
- isb
- mcr p15, 0, r3, c2, c0, 0 @ TTB 0
- isb
-
- mov r2, #0
- mcr p15, 0, r2, c8, c3, 0 @ invalidate TLB
- mcr p15, 0, r2, c7, c5, 6 @ flush BTAC
- mcr p15, 0, r2, c7, c5, 0 @ flush instruction cache
-
mov32 r3, tegra_shut_off_mmu
add r3, r3, r0
mov r0, r1
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index a77fd08e7df5..fcc6503d7e64 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -125,17 +125,54 @@
.macro push_ctx_regs, tmp1
push_stack_token \tmp1 @ debug check word
stmfd sp!, {r4 - r11, lr}
+ /* Save the current TTB0 and CONTEXTID registers. */
+ mrc p15, 0, r5, c2, c0, 0 @ TTB 0
+ mrc p15, 0, r6, c13, c0, 1 @ CONTEXTID
#if USE_TEGRA_DIAG_REG_SAVE
mrc p15, 0, r4, c15, c0, 1 @ read diagnostic register
- stmfd sp!, {r4}
+ stmfd sp!, {r4-r6}
+#else
+ stmfd sp!, {r5-r6}
#endif
+ /* Switch to the tegra_pgd so that IRAM and the MMU shut-off code
+ will be flat mapped (VA==PA). We also do this because the common
+ ARM CPU state save/restore code doesn't support an external L2
+ cache controller. If the current PGD is left active, the common
+ ARM MMU restore may (and eventually will) damage the currently
+ running page tables by adding a temporary flat section mapping
+ that could be picked up by other CPUs from the L2 cache
+ resulting in a kernel panic. */
+ ldr r6, tegra_pgd_phys_address
+ ldr r6, [r6]
+ mov r7, #0
+ dsb
+ mcr p15, 0, r7, c13, c0, 1 @ CONTEXTID = reserved context
+ isb
+ mcr p15, 0, r6, c2, c0, 0 @ TTB 0
+ isb
+ mcr p15, 0, r7, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r7, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r7, c7, c5, 0 @ flush instruction cache
+ dsb
.endm
.macro pop_ctx_regs, tmp1, tmp2
#if USE_TEGRA_DIAG_REG_SAVE
- ldmfd sp!, {r4}
+ ldmfd sp!, {r4-r6}
mcr p15, 0, r4, c15, c0, 1 @ write diagnostic register
+#else
+ ldmfd sp!, {r5-r6}
#endif
+ dsb
+ mcr p15, 0, r5, c2, c0, 0 @ TTB 0
+ isb
+ mcr p15, 0, r6, c13, c0, 1 @ CONTEXTID = reserved context
+ isb
+ mov r7, #0
+ mcr p15, 0, r7, c8, c3, 0 @ invalidate TLB
+ mcr p15, 0, r7, c7, c5, 6 @ flush BTAC
+ mcr p15, 0, r7, c7, c5, 0 @ flush instruction cache
+ dsb
ldmfd sp!, {r4 - r11, lr}
pop_stack_token \tmp1, \tmp2 @ debug stack debug token
.endm