summaryrefslogtreecommitdiff
path: root/arch/arm/cpu
diff options
context:
space:
mode:
authorAkshay Saraswat <akshay.s@samsung.com>2015-02-20 13:27:15 +0530
committerMinkyu Kang <mk7.kang@samsung.com>2015-02-28 18:03:46 +0900
commit67a0652c47ec568ea274f5ff0303c9bba8ceddbf (patch)
tree5e5324b6a3bf55684f0c2a8de4c129d95fb4c714 /arch/arm/cpu
parenta389531439a7d5cea2829054edcf438dc76e79a9 (diff)
downloadu-boot-imx-67a0652c47ec568ea274f5ff0303c9bba8ceddbf.zip
u-boot-imx-67a0652c47ec568ea274f5ff0303c9bba8ceddbf.tar.gz
u-boot-imx-67a0652c47ec568ea274f5ff0303c9bba8ceddbf.tar.bz2
Exynos542x: Add workaround for exynos iROM errata
iROM logic provides undesired jump address for CPU2. This patch adds a programmable susbstitute for a part of iROM logic which wakes up cores and provides jump addresses. This patch creates a logic to make all secondary cores jump to a particular address which evades the possibility of CPU2 jumping to wrong address and create undesired results. Logic of the workaround: Step-1: iROM code checks value at address 0x2020028. Step-2: If value is 0xc9cfcfcf, it jumps to the address (0x202000+CPUid*4), else, it continues executing normally. Step-3: Primary core puts secondary cores in WFE and store 0xc9cfcfcf in 0x2020028 and jump address (pointer to function low_power_start) in (0x202000+CPUid*4). Step-4: When secondary cores recieve event signal they jump to this address and continue execution. Signed-off-by: Kimoon Kim <kimoon.kim@samsung.com> Signed-off-by: Akshay Saraswat <akshay.s@samsung.com> Reviewed-by: Simon Glass <sjg@chromium.org> Tested-by: Simon Glass <sjg@chromium.org> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Diffstat (limited to 'arch/arm/cpu')
-rw-r--r--arch/arm/cpu/armv7/exynos/Makefile2
-rw-r--r--arch/arm/cpu/armv7/exynos/lowlevel_init.c86
-rw-r--r--arch/arm/cpu/armv7/exynos/sec_boot.S128
3 files changed, 200 insertions, 16 deletions
diff --git a/arch/arm/cpu/armv7/exynos/Makefile b/arch/arm/cpu/armv7/exynos/Makefile
index e207bd6..8542f89 100644
--- a/arch/arm/cpu/armv7/exynos/Makefile
+++ b/arch/arm/cpu/armv7/exynos/Makefile
@@ -7,6 +7,8 @@
obj-y += clock.o power.o soc.o system.o pinmux.o tzpc.o
+obj-$(CONFIG_EXYNOS5420) += sec_boot.o
+
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_EXYNOS5) += clock_init_exynos5.o
obj-$(CONFIG_EXYNOS5) += dmc_common.o dmc_init_ddr3.o
diff --git a/arch/arm/cpu/armv7/exynos/lowlevel_init.c b/arch/arm/cpu/armv7/exynos/lowlevel_init.c
index e36f2fa..b94e49f 100644
--- a/arch/arm/cpu/armv7/exynos/lowlevel_init.c
+++ b/arch/arm/cpu/armv7/exynos/lowlevel_init.c
@@ -32,6 +32,7 @@
#include <asm/arch/periph.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/system.h>
+#include <asm/armv7.h>
#include "common_setup.h"
#include "exynos5_setup.h"
@@ -46,6 +47,61 @@ enum {
#ifdef CONFIG_EXYNOS5420
/*
+ * Power up secondary CPUs.
+ */
+static void secondary_cpu_start(void)
+{
+ v7_enable_smp(EXYNOS5420_INFORM_BASE);
+ svc32_mode_en();
+ set_pc(CONFIG_EXYNOS_RELOCATE_CODE_BASE);
+}
+
+/*
+ * This is the entry point of hotplug-in and
+ * cluster switching.
+ */
+static void low_power_start(void)
+{
+ uint32_t val, reg_val;
+
+ reg_val = readl(EXYNOS5420_SPARE_BASE);
+ if (reg_val != CPU_RST_FLAG_VAL) {
+ writel(0x0, CONFIG_LOWPOWER_FLAG);
+ set_pc(0x0);
+ }
+
+ reg_val = readl(CONFIG_PHY_IRAM_BASE + 0x4);
+ if (reg_val != (uint32_t)&low_power_start) {
+ /* Store jump address as low_power_start if not present */
+ writel((uint32_t)&low_power_start, CONFIG_PHY_IRAM_BASE + 0x4);
+ dsb();
+ sev();
+ }
+
+ /* Set the CPU to SVC32 mode */
+ svc32_mode_en();
+ v7_enable_l2_hazard_detect();
+
+ /* Invalidate L1 & TLB */
+ val = 0x0;
+ mcr_tlb(val);
+ mcr_icache(val);
+
+ /* Disable MMU stuff and caches */
+ mrc_sctlr(val);
+
+ val &= ~((0x2 << 12) | 0x7);
+ val |= ((0x1 << 12) | (0x8 << 8) | 0x2);
+ mcr_sctlr(val);
+
+ /* CPU state is hotplug or reset */
+ secondary_cpu_start();
+
+ /* Core should not enter into WFI here */
+ wfi();
+}
+
+/*
* Pointer to this function is stored in iRam which is used
* for jump and power down of a specific core.
*/
@@ -81,29 +137,25 @@ static void power_down_core(void)
*/
static void secondary_cores_configure(void)
{
- uint32_t core_id;
+ /* Setup L2 cache */
+ v7_enable_l2_hazard_detect();
- /* Store jump address for power down of secondary cores */
+ /* Clear secondary boot iRAM base */
+ writel(0x0, (CONFIG_EXYNOS_RELOCATE_CODE_BASE + 0x1C));
+
+ /* set lowpower flag and address */
+ writel(CPU_RST_FLAG_VAL, CONFIG_LOWPOWER_FLAG);
+ writel((uint32_t)&low_power_start, CONFIG_LOWPOWER_ADDR);
+ writel(CPU_RST_FLAG_VAL, EXYNOS5420_SPARE_BASE);
+ /* Store jump address for power down */
writel((uint32_t)&power_down_core, CONFIG_PHY_IRAM_BASE + 0x4);
/* Need all core power down check */
dsb();
sev();
-
- /*
- * Power down all cores(secondary) while primary core must
- * wait for all cores to go down.
- */
- for (core_id = 1; core_id != CONFIG_CORE_COUNT; core_id++) {
- while ((readl(EXYNOS5420_CPU_STATUS_BASE
- + (core_id * CPU_CONFIG_STATUS_OFFSET))
- & 0xff) != 0x0) {
- isb();
- sev();
- }
- isb();
- }
}
+
+extern void relocate_wait_code(void);
#endif
int do_lowlevel_init(void)
@@ -114,6 +166,8 @@ int do_lowlevel_init(void)
arch_cpu_init();
#ifdef CONFIG_EXYNOS5420
+ relocate_wait_code();
+
/* Reconfigure secondary cores */
secondary_cores_configure();
#endif
diff --git a/arch/arm/cpu/armv7/exynos/sec_boot.S b/arch/arm/cpu/armv7/exynos/sec_boot.S
new file mode 100644
index 0000000..dfc3455
--- /dev/null
+++ b/arch/arm/cpu/armv7/exynos/sec_boot.S
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics
+ * Akshay Saraswat <akshay.s@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/arch/cpu.h>
+
+ .globl relocate_wait_code
+relocate_wait_code:
+ adr r0, code_base @ r0: source address (start)
+ adr r1, code_end @ r1: source address (end)
+ ldr r2, =0x02073000 @ r2: target address
+1:
+ ldmia r0!, {r3-r6}
+ stmia r2!, {r3-r6}
+ cmp r0, r1
+ blt 1b
+ b code_end
+ .ltorg
+/*
+ * Secondary core waits here until Primary wake it up.
+ * Below code is copied to CONFIG_EXYNOS_RELOCATE_CODE_BASE.
+ * This is a workaround code which is supposed to act as a
+ * substitute/supplement to the iROM code.
+ *
+ * This workaround code is relocated to the address 0x02073000
+ * because that comes out to be the last 4KB of the iRAM
+ * (Base Address - 0x02020000, Limit Address - 0x020740000).
+ *
+ * U-boot and kernel are aware of this code and flags by the simple
+ * fact that we are implementing a workaround in the last 4KB
+ * of the iRAM and we have already defined these flag and address
+ * values in both kernel and U-boot for our use.
+ */
+code_base:
+ b 1f
+/*
+ * These addresses are being used as flags in u-boot and kernel.
+ *
+ * Jump address for resume and flag to check for resume/reset:
+ * Resume address - 0x2073008
+ * Resume flag - 0x207300C
+ *
+ * Jump address for cluster switching:
+ * Switch address - 0x2073018
+ *
+ * Jump address for core hotplug:
+ * Hotplug address - 0x207301C
+ *
+ * Jump address for C2 state (Reserved for future not being used right now):
+ * C2 address - 0x2073024
+ *
+ * Managed per core status for the active cluster:
+ * CPU0 state - 0x2073028
+ * CPU1 state - 0x207302C
+ * CPU2 state - 0x2073030
+ * CPU3 state - 0x2073034
+ *
+ * Managed per core GIC status for the active cluster:
+ * CPU0 gic state - 0x2073038
+ * CPU1 gic state - 0x207303C
+ * CPU2 gic state - 0x2073040
+ * CPU3 gic state - 0x2073044
+ *
+ * Logic of the code:
+ * Step-1: Read current CPU status.
+ * Step-2: If it's a resume then continue, else jump to step 4.
+ * Step-3: Clear inform1 PMU register and jump to inform0 value.
+ * Step-4: If it's a switch, C2 or reset, get the hotplug address.
+ * Step-5: If address is not available, enter WFE.
+ * Step-6: If address is available, jump to that address.
+ */
+ nop @ for backward compatibility
+ .word 0x0 @ REG0: RESUME_ADDR
+ .word 0x0 @ REG1: RESUME_FLAG
+ .word 0x0 @ REG2
+ .word 0x0 @ REG3
+_switch_addr:
+ .word 0x0 @ REG4: SWITCH_ADDR
+_hotplug_addr:
+ .word 0x0 @ REG5: CPU1_BOOT_REG
+ .word 0x0 @ REG6
+_c2_addr:
+ .word 0x0 @ REG7: REG_C2_ADDR
+_cpu_state:
+ .word 0x1 @ CPU0_STATE : RESET
+ .word 0x2 @ CPU1_STATE : SECONDARY RESET
+ .word 0x2 @ CPU2_STATE : SECONDARY RESET
+ .word 0x2 @ CPU3_STATE : SECONDARY RESET
+_gic_state:
+ .word 0x0 @ CPU0 - GICD_IGROUPR0
+ .word 0x0 @ CPU1 - GICD_IGROUPR0
+ .word 0x0 @ CPU2 - GICD_IGROUPR0
+ .word 0x0 @ CPU3 - GICD_IGROUPR0
+1:
+ adr r0, _cpu_state
+ mrc p15, 0, r7, c0, c0, 5 @ read MPIDR
+ and r7, r7, #0xf @ r7 = cpu id
+/* Read the current cpu state */
+ ldr r10, [r0, r7, lsl #2]
+svc_entry:
+ tst r10, #(1 << 4)
+ adrne r0, _switch_addr
+ bne wait_for_addr
+/* Clear INFORM1 */
+ ldr r0, =(0x10040000 + 0x804)
+ ldr r1, [r0]
+ cmp r1, #0x0
+ movne r1, #0x0
+ strne r1, [r0]
+/* Get INFORM0 */
+ ldrne r1, =(0x10040000 + 0x800)
+ ldrne pc, [r1]
+ tst r10, #(1 << 0)
+ ldrne pc, =0x23e00000
+ adr r0, _hotplug_addr
+wait_for_addr:
+ ldr r1, [r0]
+ cmp r1, #0x0
+ bxne r1
+ wfe
+ b wait_for_addr
+ .ltorg
+code_end:
+ mov pc, lr