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>
master
Akshay Saraswat 9 years ago committed by Minkyu Kang
parent a389531439
commit 67a0652c47
  1. 2
      arch/arm/cpu/armv7/exynos/Makefile
  2. 86
      arch/arm/cpu/armv7/exynos/lowlevel_init.c
  3. 128
      arch/arm/cpu/armv7/exynos/sec_boot.S

@ -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

@ -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

@ -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
Loading…
Cancel
Save