diff options
author | Akshay Saraswat <akshay.s@samsung.com> | 2015-02-20 13:27:15 +0530 |
---|---|---|
committer | Minkyu Kang <mk7.kang@samsung.com> | 2015-02-28 18:03:46 +0900 |
commit | 67a0652c47ec568ea274f5ff0303c9bba8ceddbf (patch) | |
tree | 5e5324b6a3bf55684f0c2a8de4c129d95fb4c714 /arch/arm/cpu | |
parent | a389531439a7d5cea2829054edcf438dc76e79a9 (diff) | |
download | u-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/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/exynos/lowlevel_init.c | 86 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/exynos/sec_boot.S | 128 |
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 |