mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 07:34:31 +00:00
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>
This commit is contained in:
parent
a389531439
commit
67a0652c47
3 changed files with 200 additions and 16 deletions
|
@ -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"
|
||||
|
||||
|
@ -45,6 +46,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
|
||||
|
|
128
arch/arm/cpu/armv7/exynos/sec_boot.S
Normal file
128
arch/arm/cpu/armv7/exynos/sec_boot.S
Normal file
|
@ -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…
Reference in a new issue