diff mbox

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

Message ID 1421329328-18070-4-git-send-email-akshay.s@samsung.com
State Changes Requested
Delegated to: Minkyu Kang
Headers show

Commit Message

Akshay Saraswat Jan. 15, 2015, 1:42 p.m. UTC
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

Comments

Simon Glass Jan. 28, 2015, 4:10 a.m. UTC | #1
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
diff mbox

Patch

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.
+ *
+ * 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