[U-Boot] [PATCH 05/11] Exynos542x: Add workaround for exynos iROM errata

Simon Glass sjg at chromium.org
Wed Jan 28 05:10:08 CET 2015


Hi Akshay,

On 15 January 2015 at 06:42, Akshay Saraswat <akshay.s at 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 at samsung.com>
> Signed-off-by: Akshay Saraswat <akshay.s at 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 at chromium.org>

Tested on snow, pit, pi
Tested-by: Simon Glass <sjg at chromium.org>

Regards,
Simon


More information about the U-Boot mailing list