Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: exynos: Fix imprecise abort during Exynos5422 suspend to RAM

Suspend to RAM on Odroid XU3/XU4/HC1 family (Exynos5422) causes
imprecise abort:

PM: Syncing filesystems ... done.
Freezing user space processes ... (elapsed 0.003 seconds) done.
OOM killer disabled.
Freezing remaining freezable tasks ... (elapsed 0.003 seconds) done.
wake enabled for irq 139
Disabling non-boot CPUs ...
IRQ51 no longer affine to CPU1
IRQ52 no longer affine to CPU2
IRQ53 no longer affine to CPU3
IRQ54 no longer affine to CPU4
IRQ55 no longer affine to CPU5
IRQ56 no longer affine to CPU6
cpu cpu4: Dropping the link to regulator.40
IRQ57 no longer affine to CPU7
Unhandled fault: external abort on non-linefetch (0x1008) at 0xf081a028
Internal error: : 1008 [#1] PREEMPT SMP ARM

with last call trace in exynos_suspend_enter().

The abort is caused by writing to register in secure part of sysram.
Boards booted under secure firmware (e.g. Hardkernel Odroid boards)
should access non-secure sysram.

Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>

+26 -6
+1
Documentation/arm/Samsung/Bootloader-interface.txt
··· 26 26 0x20 0xfcba0d10 (Magic cookie) AFTR 27 27 0x24 exynos_cpu_resume_ns AFTR 28 28 0x28 + 4*cpu 0x8 (Magic cookie, Exynos3250) AFTR 29 + 0x28 0x0 or last value during resume (Exynos542x) System suspend 29 30 30 31 31 32 2. Secure mode
+1
arch/arm/mach-exynos/common.h
··· 110 110 #define EXYNOS_SLEEP_MAGIC 0x00000bad 111 111 #define EXYNOS_AFTR_MAGIC 0xfcba0d10 112 112 113 + bool __init exynos_secure_firmware_available(void); 113 114 void exynos_set_boot_flag(unsigned int cpu, unsigned int mode); 114 115 void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode); 115 116
+11 -3
arch/arm/mach-exynos/firmware.c
··· 185 185 exynos_smc(SMC_CMD_L2X0SETUP2, regs->pwr_ctrl, regs->aux_ctrl, 0); 186 186 } 187 187 188 - void __init exynos_firmware_init(void) 188 + bool __init exynos_secure_firmware_available(void) 189 189 { 190 190 struct device_node *nd; 191 191 const __be32 *addr; ··· 193 193 nd = of_find_compatible_node(NULL, NULL, 194 194 "samsung,secure-firmware"); 195 195 if (!nd) 196 - return; 196 + return false; 197 197 198 198 addr = of_get_address(nd, 0, NULL, NULL); 199 199 if (!addr) { 200 200 pr_err("%s: No address specified.\n", __func__); 201 - return; 201 + return false; 202 202 } 203 + 204 + return true; 205 + } 206 + 207 + void __init exynos_firmware_init(void) 208 + { 209 + if (!exynos_secure_firmware_available()) 210 + return; 203 211 204 212 pr_info("Running under secure firmware.\n"); 205 213
+13 -3
arch/arm/mach-exynos/suspend.c
··· 63 63 struct exynos_pm_state { 64 64 int cpu_state; 65 65 unsigned int pmu_spare3; 66 + void __iomem *sysram_base; 66 67 }; 67 68 68 69 static const struct exynos_pm_data *pm_data __ro_after_init; ··· 262 261 unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 263 262 unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 264 263 265 - writel_relaxed(0x0, sysram_base_addr + EXYNOS5420_CPU_STATE); 264 + writel_relaxed(0x0, pm_state.sysram_base + EXYNOS5420_CPU_STATE); 266 265 267 266 if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) { 268 267 mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume); ··· 334 333 * needs to restore it back in case, the primary cpu fails to 335 334 * suspend for any reason. 336 335 */ 337 - pm_state.cpu_state = readl_relaxed(sysram_base_addr + 336 + pm_state.cpu_state = readl_relaxed(pm_state.sysram_base + 338 337 EXYNOS5420_CPU_STATE); 339 338 340 339 exynos_pm_enter_sleep_mode(); ··· 454 453 455 454 /* Restore the sysram cpu state register */ 456 455 writel_relaxed(pm_state.cpu_state, 457 - sysram_base_addr + EXYNOS5420_CPU_STATE); 456 + pm_state.sysram_base + EXYNOS5420_CPU_STATE); 458 457 459 458 pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL, 460 459 S5P_CENTRAL_SEQ_OPTION); ··· 659 658 660 659 register_syscore_ops(&exynos_pm_syscore_ops); 661 660 suspend_set_ops(&exynos_suspend_ops); 661 + 662 + /* 663 + * Applicable as of now only to Exynos542x. If booted under secure 664 + * firmware, the non-secure region of sysram should be used. 665 + */ 666 + if (exynos_secure_firmware_available()) 667 + pm_state.sysram_base = sysram_ns_base_addr; 668 + else 669 + pm_state.sysram_base = sysram_base_addr; 662 670 }