
Hi Akshay,
On 15 January 2015 at 06:42, Akshay Saraswat akshay.s@samsung.com wrote:
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
arch/arm/cpu/armv7/exynos/Makefile | 2 + arch/arm/cpu/armv7/exynos/lowlevel_init.c | 90 +++++++++++++++---- arch/arm/cpu/armv7/exynos/sec_boot.S | 145 ++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 arch/arm/cpu/armv7/exynos/sec_boot.S
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 3097382..d3c466e 100644 --- a/arch/arm/cpu/armv7/exynos/lowlevel_init.c +++ b/arch/arm/cpu/armv7/exynos/lowlevel_init.c @@ -49,7 +49,7 @@ enum {
- before modifying the ACTLR.SMP bit. This is required during boot before
- MMU has been enabled, or during a specified reset or power down sequence.
*/ -void enable_smp(void) +static void enable_smp(void) { uint32_t temp, val;
@@ -70,7 +70,7 @@ void enable_smp(void)
- Set L2ACTLR[7] to reissue any memory transaction in the L2 that has been
- stalled for 1024 cycles to verify that its hazard condition still exists.
*/ -void set_l2cache(void) +static void set_l2cache(void) { uint32_t val;
@@ -89,6 +89,62 @@ void set_l2cache(void) }
/*
- Power up secondary CPUs.
- */
+static void secondary_cpu_start(void) +{
enable_smp();
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(RST_FLAG_REG);
if (reg_val != 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();
set_l2cache();
/* 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.
*/ @@ -118,29 +174,25 @@ static void power_down_core(void) */ static void secondary_cores_configure(void) {
uint32_t core_id;
/* Setup L2 cache */
set_l2cache();
/* Clear secondary boot iRAM base */
writel(0x0, (CONFIG_EXYNOS_RELOCATE_CODE_BASE + 0x1C));
/* Store jump address for power down of secondary cores */
/* set lowpower flag and address */
writel(RST_FLAG_VAL, CONFIG_LOWPOWER_FLAG);
writel((uint32_t)&low_power_start, CONFIG_LOWPOWER_ADDR);
writel(RST_FLAG_VAL, RST_FLAG_REG);
/* 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 != CORE_COUNT; core_id++) {
while ((readl(ARM_CORE0_STATUS
+ (core_id * CORE_CONFIG_OFFSET))
& 0xff) != 0x0) {
isb();
sev();
}
isb();
}
}
+extern void relocate_wait_code(void); #endif
int do_lowlevel_init(void) @@ -151,6 +203,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..e818cf1 --- /dev/null +++ b/arch/arm/cpu/armv7/exynos/sec_boot.S @@ -0,0 +1,145 @@ +/*
- Lowlevel setup for EXYNOS5
- Copyright (C) 2013 Samsung Electronics
- See file CREDITS for list of people who contributed to this
- project.
Can we please use SPDX header here?
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#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
-- 1.9.1
Reviewed-by: Simon Glass sjg@chromium.org
Tested on snow, pit, pi Tested-by: Simon Glass sjg@chromium.org
Regards, Simon