summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7/exynos
diff options
context:
space:
mode:
authorAkshay Saraswat <akshay.s@samsung.com>2015-02-20 13:27:12 +0530
committerMinkyu Kang <mk7.kang@samsung.com>2015-02-28 18:03:46 +0900
commitac0d98cd557e0939bd0f10ff68e2e648a74bbea6 (patch)
treeff8fee5d3b87261254d1fea5f3c4461b47f984f8 /arch/arm/cpu/armv7/exynos
parent1a8aae9b946d707d5dd2b1616e9c9c80c3daf178 (diff)
downloadu-boot-imx-ac0d98cd557e0939bd0f10ff68e2e648a74bbea6.zip
u-boot-imx-ac0d98cd557e0939bd0f10ff68e2e648a74bbea6.tar.gz
u-boot-imx-ac0d98cd557e0939bd0f10ff68e2e648a74bbea6.tar.bz2
Exynos542x: CPU: Power down all secondary cores
This patch adds code to shutdown secondary cores. When U-boot comes up, all secondary cores appear powered on, which is undesirable and causes side effects while initializing these cores in kernel. Secondary core power down happens in following steps: Step-1: After Exynos power-on, primary core starts executing first. Step-2: In iROM code every core has to check 2 flags i.e. addresses 0x02020028 & 0x02020004. Step-3: Initially 0x02020028 is 0 for all cores and 0x02020004 has a jump address for primary core and 0 for all secondary cores. Step-4: Therefore, primary core follows normal iROM execution and jumps to BL1 eventually, whereas all secondary cores enter WFE. Step-5: When primary core comes into function secondary_cores_configure, it puts pointer to function power_down_core into 0x02020004 and provides DSB and SEV for all cores so that they may come out of WFE and jump to power_down_core function. Step-6: And ultimately because of power_down_core all secondary cores shut-down. Signed-off-by: Kimoon Kim <kimoon.kim@samsung.com> Signed-off-by: Akshay Saraswat <akshay.s@samsung.com> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Diffstat (limited to 'arch/arm/cpu/armv7/exynos')
-rw-r--r--arch/arm/cpu/armv7/exynos/exynos5_setup.h3
-rw-r--r--arch/arm/cpu/armv7/exynos/lowlevel_init.c69
2 files changed, 72 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/exynos/exynos5_setup.h b/arch/arm/cpu/armv7/exynos/exynos5_setup.h
index 2eea48a..9073f50 100644
--- a/arch/arm/cpu/armv7/exynos/exynos5_setup.h
+++ b/arch/arm/cpu/armv7/exynos/exynos5_setup.h
@@ -700,6 +700,9 @@
#define CLK_DIV_CPERI1_VAL NOT_AVAILABLE
#else
+
+#define CPU_CONFIG_STATUS_OFFSET 0x80
+#define CPU_RST_FLAG_VAL 0xFCBA0D10
#define PAD_RETENTION_DRAM_COREBLK_VAL 0x10000000
/* APLL_CON1 */
diff --git a/arch/arm/cpu/armv7/exynos/lowlevel_init.c b/arch/arm/cpu/armv7/exynos/lowlevel_init.c
index 83e1dcf..e36f2fa 100644
--- a/arch/arm/cpu/armv7/exynos/lowlevel_init.c
+++ b/arch/arm/cpu/armv7/exynos/lowlevel_init.c
@@ -31,7 +31,9 @@
#include <asm/arch/tzpc.h>
#include <asm/arch/periph.h>
#include <asm/arch/pinmux.h>
+#include <asm/arch/system.h>
#include "common_setup.h"
+#include "exynos5_setup.h"
/* These are the things we can do during low-level init */
enum {
@@ -42,6 +44,68 @@ enum {
DO_POWER = 1 << 4,
};
+#ifdef CONFIG_EXYNOS5420
+/*
+ * Pointer to this function is stored in iRam which is used
+ * for jump and power down of a specific core.
+ */
+static void power_down_core(void)
+{
+ uint32_t tmp, core_id, core_config;
+
+ /* Get the unique core id */
+ /*
+ * Multiprocessor Affinity Register
+ * [11:8] Cluster ID
+ * [1:0] CPU ID
+ */
+ mrc_mpafr(core_id);
+ tmp = core_id & 0x3;
+ core_id = (core_id >> 6) & ~3;
+ core_id |= tmp;
+ core_id &= 0x3f;
+
+ /* Set the status of the core to low */
+ core_config = (core_id * CPU_CONFIG_STATUS_OFFSET);
+ core_config += EXYNOS5420_CPU_CONFIG_BASE;
+ writel(0x0, core_config);
+
+ /* Core enter WFI */
+ wfi();
+}
+
+/*
+ * Configurations for secondary cores are inapt at this stage.
+ * Reconfigure secondary cores. Shutdown and change the status
+ * of all cores except the primary core.
+ */
+static void secondary_cores_configure(void)
+{
+ uint32_t core_id;
+
+ /* Store jump address for power down of secondary cores */
+ 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();
+ }
+}
+#endif
+
int do_lowlevel_init(void)
{
uint32_t reset_status;
@@ -49,6 +113,11 @@ int do_lowlevel_init(void)
arch_cpu_init();
+#ifdef CONFIG_EXYNOS5420
+ /* Reconfigure secondary cores */
+ secondary_cores_configure();
+#endif
+
reset_status = get_reset_status();
switch (reset_status) {