[coreboot-gerrit] New patch to review for coreboot: cfa1311 armv7/exynos5420: Configure CPU cores for kernel to enable SMP.

Isaac Christensen (isaac.christensen@se-eng.com) gerrit at coreboot.org
Tue Jul 29 21:58:51 CEST 2014


Isaac Christensen (isaac.christensen at se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6405

-gerrit

commit cfa131147fc87846e0d8957c427ecb7c8ade3e82
Author: Hung-Te Lin <hungte at chromium.org>
Date:   Tue Aug 6 20:37:55 2013 +0800

    armv7/exynos5420: Configure CPU cores for kernel to enable SMP.
    
    The SMP on Exynos 5420 requires setting a special page and entry wrappers in
    firmware side (SRAM) so kernel can start cores (and to switch clusters).
    
    Change-Id: I77ca98bb6cff5b13e95dd29228e4536302f0aee9
    Signed-off-by: Hung-Te Lin <hungte at chromium.org>
    Reviewed-on: https://gerrit.chromium.org/gerrit/64770
    Reviewed-by: Ronald G. Minnich <rminnich at chromium.org>
    (cherry picked from commit 4a11c7ab78cc0811df0f88763b0af8b9f24e5433)
    Signed-off-by: Isaac Christensen <isaac.christensen at se-eng.com>
---
 src/arch/armv7/include/arch/cpu.h       |   6 +
 src/cpu/samsung/exynos5420/Kconfig      |   9 +-
 src/cpu/samsung/exynos5420/Makefile.inc |   1 +
 src/cpu/samsung/exynos5420/cpu.h        |   6 +-
 src/cpu/samsung/exynos5420/smp.c        | 305 ++++++++++++++++++++++++++++++++
 src/mainboard/google/pit/romstage.c     |   1 +
 6 files changed, 324 insertions(+), 4 deletions(-)

diff --git a/src/arch/armv7/include/arch/cpu.h b/src/arch/armv7/include/arch/cpu.h
index 2613025..ec37a96 100644
--- a/src/arch/armv7/include/arch/cpu.h
+++ b/src/arch/armv7/include/arch/cpu.h
@@ -98,4 +98,10 @@ inline static void sev(void)
 	asm volatile ("sev");
 }
 
+/* puts CPU into SVC32 mode and disable interrupts. */
+inline static void set_svc32_mode(void)
+{
+	asm volatile("msr cpsr_c, %0" :: "r"(0x13 | 0xc0));
+}
+
 #endif /* __ARCH_CPU_H__ */
diff --git a/src/cpu/samsung/exynos5420/Kconfig b/src/cpu/samsung/exynos5420/Kconfig
index d7adf6c..6066040 100644
--- a/src/cpu/samsung/exynos5420/Kconfig
+++ b/src/cpu/samsung/exynos5420/Kconfig
@@ -46,6 +46,11 @@ config CBFS_ROM_OFFSET
 # 0x0202_4400: variable length bootblock checksum header.
 # 0x0202_4410: bootblock, assume up to 32KB in size
 # 0x0203_0000: romstage, assume up to 128KB in size.
+# 0x0206_0000: cache for CBFS data.
+# 0x0207_3000: shared (with kernel) page for cpu & secondary core states.
+#              the shared data is currently only <0x50 bytes so we can share
+#              this page with stack.
+# 0x0207_3100: stack bottom
 # 0x0207_4000: stack pointer
 
 config BOOTBLOCK_BASE
@@ -71,11 +76,11 @@ config STACK_TOP
 
 config STACK_BOTTOM
 	hex
-	default 0x02073000
+	default 0x02073100
 
 config STACK_SIZE
 	hex
-	default 0x1000
+	default 0x0f00
 
 # TODO We may probably move this to board-specific implementation files instead
 # of KConfig values.
diff --git a/src/cpu/samsung/exynos5420/Makefile.inc b/src/cpu/samsung/exynos5420/Makefile.inc
index 279e803..3191627 100644
--- a/src/cpu/samsung/exynos5420/Makefile.inc
+++ b/src/cpu/samsung/exynos5420/Makefile.inc
@@ -17,6 +17,7 @@ bootblock-y += gpio.c
 bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += timer.c
 
 romstage-y += spi.c
+romstage-y += smp.c
 romstage-y += clock.c
 romstage-y += clock_init.c
 romstage-y += pinmux.c  # required by s3c24x0_i2c and uart.
diff --git a/src/cpu/samsung/exynos5420/cpu.h b/src/cpu/samsung/exynos5420/cpu.h
index 22ef8db..08f315a 100644
--- a/src/cpu/samsung/exynos5420/cpu.h
+++ b/src/cpu/samsung/exynos5420/cpu.h
@@ -224,8 +224,6 @@
 #define EXYNOS5_SPI_NUM_CONTROLLERS	5
 #define EXYNOS_I2C_MAX_CONTROLLERS	8
 
-void exynos5420_config_l2_cache(void);
-
 extern struct tmu_info exynos5420_tmu_info;
 
 /* TODO clean up defines. */
@@ -264,4 +262,8 @@ static inline u32 get_fb_base_kb(void)
 	return RAM_BASE_KB + RAM_SIZE_KB - FB_SIZE_KB;
 }
 
+/* Procedures to setup Exynos5420 CPU */
+void exynos5420_config_l2_cache(void);
+void exynos5420_config_smp(void);
+
 #endif	/* _EXYNOS5420_CPU_H */
diff --git a/src/cpu/samsung/exynos5420/smp.c b/src/cpu/samsung/exynos5420/smp.c
new file mode 100644
index 0000000..392f82d
--- /dev/null
+++ b/src/cpu/samsung/exynos5420/smp.c
@@ -0,0 +1,305 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2013 Google Inc.
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <types.h>
+#include <arch/cpu.h>
+#include <arch/io.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cpu/samsung/exynos5420/cpu.h>
+#include <cpu/samsung/exynos5420/power.h>
+
+/* ACTLR, L2CTLR L2ACTLR constants used in SMP core power up. */
+
+#define ACTLR_SMP					(1 << 6)
+
+#define L2CTLR_ECC_PARITY				(1 << 21)
+#define L2CTLR_DATA_RAM_LATENCY_MASK			(7 << 0)
+#define L2CTLR_TAG_RAM_LATENCY_MASK			(7 << 6)
+#define L2CTLR_DATA_RAM_LATENCY_CYCLES_3		(2 << 0)
+#define L2CTLR_TAG_RAM_LATENCY_CYCLES_3			(2 << 6)
+
+#define L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL	(1 << 3)
+#define L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT		(1 << 7)
+#define L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE	(1 << 27)
+
+/* Part number in CPU ID (MPIDR). */
+#define PART_NUMBER_CORTEX_A15				(0xc0f)
+
+/* State of CPU cores in Exynos 5420. */
+#define CORE_STATE_RESET				(1 << 0)
+#define CORE_STATE_SECONDARY_RESET			(1 << 1)
+#define CORE_STATE_SWITCH_CLUSTER			(1 << 4)
+
+/* The default address to re-power on a code. */
+#define CORE_RESET_INIT_ADDRESS				((void*)0x00000000)
+
+/* Vectors in BL1 (0x02020000 = base of iRAM). */
+#define VECTOR_CORE_SEV_HANDLER			((void*)(intptr_t)0x02020004)
+#define VECTOR_LOW_POWER_FLAG			((void*)(intptr_t)0x02020028)
+#define VECTOR_LOW_POWER_ADDRESS		((void*)(intptr_t)0x0202002C)
+
+/* The data structure for the "CPU state" memory page (shared with kernel)
+ * controlling cores in active cluster. Kernel will put starting address for one
+ * core in "hotplug_address" before power on. Note the address is hard-coded in
+ * kernel (EXYNOS5420_PA_SYSRAM_NS = 0x02073000). */
+volatile struct exynos5420_cpu_states
+{
+	uint32_t _reserved[2];		/* RESV, +0x00 */
+	uint32_t resume_address;	/* REG0, +0x08 */
+	uint32_t resume_flag;		/* REG1, +0x0C */
+	uint32_t _reg2;			/* REG2, +0x10 */
+	uint32_t _reg3;			/* REG3, +0x14 */
+	uint32_t switch_address;	/* REG4, +0x18, cluster switching */
+	uint32_t hotplug_address;	/* REG5, +0x1C, core hotplug */
+	uint32_t _reg6;			/* REG6, +0x20 */
+	uint32_t c2_address;		/* REG7, +0x24, C2 state change */
+
+	/* Managed per core status for active cluster, offset: +0x28~0x38 */
+	uint32_t cpu_states[4];
+
+	/* Managed per core GIC status for active cluster, offset: 0x38~0x48 */
+	uint32_t cpu_gic_states[4];
+} *exynos_cpu_states = (volatile struct exynos5420_cpu_states*)0x02073000;
+
+/* When leaving core handlers and jump to hot-plug address (or cluster
+ * switching), we are not sure if the destination is Thumb or ARM mode.
+ * So a BX command is required.
+ */
+inline static void jump_bx(void *address)
+{
+	asm volatile ("bx %0" : : "r"(address));
+	/* never returns. */
+}
+
+/* Extracts arbitrary bits from a 32-bit unsigned int. */
+inline static uint32_t get_bits(uint32_t value, uint32_t start, uint32_t len)
+{
+	return ((value << (sizeof(value) * 8 - len - start)) >>
+		(sizeof(value) * 8 - len));
+}
+
+/* Waits the referenced address to be ready (non-zero) and then jump into it. */
+static void wait_and_jump(volatile uint32_t* reference)
+{
+	while (!*reference) {
+		wfe();
+	}
+	jump_bx((void*)*reference);
+}
+
+/* Configures L2 Control Register to use 3 cycles for DATA/TAG RAM latency. */
+static void configure_l2ctlr(void)
+{
+   uint32_t val;
+
+   val = read_l2ctlr();
+   val &= ~(L2CTLR_DATA_RAM_LATENCY_MASK | L2CTLR_TAG_RAM_LATENCY_MASK);
+   val |= (L2CTLR_DATA_RAM_LATENCY_CYCLES_3 | L2CTLR_TAG_RAM_LATENCY_CYCLES_3 |
+	   L2CTLR_ECC_PARITY);
+   write_l2ctlr(val);
+}
+
+/* Configures L2 Auxiliary Control Register for Cortex A15. */
+static void configure_l2actlr(void)
+{
+   uint32_t val;
+
+   val = read_l2actlr();
+   val |= (L2ACTLR_DISABLE_CLEAN_EVICT_PUSH_EXTERNAL |
+	   L2ACTLR_ENABLE_HAZARD_DETECT_TIMEOUT |
+	   L2ACTLR_FORCE_L2_LOGIC_CLOCK_ENABLE_ACTIVE);
+   write_l2actlr(val);
+}
+
+/* Initializes the CPU states to reset state. */
+static void init_exynos_cpu_states(void) {
+	memset((void*)exynos_cpu_states, 0, sizeof(*exynos_cpu_states));
+	exynos_cpu_states->cpu_states[0] = CORE_STATE_RESET;
+	exynos_cpu_states->cpu_states[1] = CORE_STATE_SECONDARY_RESET;
+	exynos_cpu_states->cpu_states[2] = CORE_STATE_SECONDARY_RESET;
+	exynos_cpu_states->cpu_states[3] = CORE_STATE_SECONDARY_RESET;
+}
+
+/*
+ * Ensures that the L2 logic has been used within the previous 256 cycles
+ * 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.
+ */
+static void enable_smp(void)
+{
+	uint32_t actlr, val;
+
+	/* Enable SMP mode */
+	actlr = read_actlr();
+	actlr |= ACTLR_SMP;
+
+	/* Dummy read to assure L2 access */
+	val = readl((void*)INF_REG_BASE);
+	val &= 0;
+	actlr |= val;
+
+	write_actlr(actlr);
+	dsb();
+	isb();
+}
+
+/* Starts the core and jumps to correct location by its state. */
+static void core_start_execution(void)
+{
+	u32 cpu_id, cpu_state;
+	struct exynos5_power *power = samsung_get_base_power();
+
+	enable_smp();
+	set_svc32_mode();
+
+	cpu_id = read_mpidr() & 0x3;  /* up to 4 processors for one cluster. */
+	cpu_state = exynos_cpu_states->cpu_states[cpu_id];
+
+	if (cpu_state & CORE_STATE_SWITCH_CLUSTER) {
+		wait_and_jump(&exynos_cpu_states->switch_address);
+		/* never returns. */
+	}
+
+	/* Standard Exynos suspend/resume. */
+	if (power->inform1) {
+		power->inform1 = 0;
+		jump_bx((void*)power->inform0);
+		/* never returns. */
+	}
+
+	if (cpu_state & CORE_STATE_RESET) {
+		/* For Reset, U-Boot jumps to its starting address;
+		 * on Coreboot, seems ok to ignore for now. */
+	}
+	wait_and_jump(&exynos_cpu_states->hotplug_address);
+	/* never returns. */
+}
+
+/* The entry point for hotplug-in and cluster switching. */
+static void low_power_start(void)
+{
+	uint32_t sctlr, reg_val;
+
+	/* On warm reset, because iRAM is not cleared, all cores will enter
+	 * low_power_start, not the initial address. So we need to check reset
+	 * status again, and jump to 0x0 in that case. */
+	reg_val = readl((void*)RST_FLAG_REG);
+	if (reg_val != RST_FLAG_VAL) {
+		writel(0x0, VECTOR_LOW_POWER_FLAG);
+		jump_bx(CORE_RESET_INIT_ADDRESS);
+		/* restart cpu execution and never returns. */
+	}
+
+	/* Workaround for iROM EVT1.  A7 core execution may flow into incorrect
+	 * path, bypassing first jump address and makes final jump address 0x0,
+	 * so we try to make any core set again low_power_start address, if that
+	 * becomes zero. */
+	reg_val = readl(VECTOR_CORE_SEV_HANDLER);
+	if (reg_val != (intptr_t)low_power_start) {
+		writel((intptr_t)low_power_start, VECTOR_CORE_SEV_HANDLER);
+		dsb();
+		/* ask all cores to power on again. */
+		sev();
+	}
+
+	set_svc32_mode();
+
+	/* Whenever a Cortex A-15 core powers on, iROM resets its L2 cache
+	 * so we need to configure again. */
+	if (get_bits(read_midr(), 4, 12) == PART_NUMBER_CORTEX_A15) {
+		configure_l2ctlr();
+		configure_l2actlr();
+	}
+
+	/* Invalidate L1 & TLB */
+	tlbiall();
+	iciallu();
+
+	/* Disable MMU stuff and caches */
+	sctlr = read_sctlr();
+	sctlr &= ~(SCTLR_V | SCTLR_M | SCTLR_C);
+	sctlr |= (SCTLR_I | SCTLR_Z | SCTLR_A);
+	write_sctlr(sctlr);
+
+	core_start_execution();
+	/* The core should not return. But in order to prevent unexpected
+	 * errors, a WFI command will help to put CPU back to idle state. */
+	wfi();
+}
+
+/* Callback to shutdown a core, safe to be set as hot-plug address. */
+static void power_down_core(void)
+{
+	uint32_t mpidr, core_id;
+
+	/* MPIDR: 0~2=ID, 8~11=cluster. On Exynos 5420, cluster will be only 0
+	 * or 1. */
+	mpidr = read_mpidr();
+	core_id = get_bits(mpidr, 0, 2) | (get_bits(mpidr, 8, 4) << 2);
+
+	/* Set the status of the core to low.
+	 * S5E5420A User Manual, 8.8.1.202, ARM_CORE0_CONFIGURATION, two bits to
+	 * control power state in each power down level.
+	 */
+	writel(0x0, (void*)(ARM_CORE0_CONFIG + core_id * CORE_CONFIG_OFFSET));
+
+	/* S5E5420A User Manual, 8.4.2.5, after ARM_CORE*_CONFIGURATION has been
+	 * set to zero, PMU will detect and wait for WFI then run power-down
+	 * sequence. */
+	wfi();
+}
+
+/* Configures the CPU states shard memory page and then shutdown all cores. */
+static void configure_secondary_cores(void)
+{
+	configure_l2ctlr();
+
+	/* Currently we use power_down_core as callback for each core to
+	 * shutdown itself, but it is also ok to directly set ARM_CORE*_CONFIG
+	 * to zero by CPU0 because every secondary cores should be already in
+	 * WFI state (in bootblock). The power_down_core will be more helpful
+	 * when we want to use SMP inside firmware. */
+
+	/* Clear boot reg (hotplug address) in cpu states */
+	writel(0, (void*)&exynos_cpu_states->hotplug_address);
+
+	/* set low_power flag and address */
+	writel((intptr_t)low_power_start, VECTOR_LOW_POWER_ADDRESS);
+	writel(RST_FLAG_VAL, VECTOR_LOW_POWER_FLAG);
+	writel(RST_FLAG_VAL, (void*)RST_FLAG_REG);
+
+	/* On next SEV, shutdown all cores. */
+	writel((intptr_t)power_down_core, VECTOR_CORE_SEV_HANDLER);
+
+	/* Ask all cores in WFE mode to shutdown. */
+	dsb();
+	sev();
+}
+
+/* Configures the SMP cores on Exynos 5420 SOC (and shutdown all secondary
+ * cores) */
+void exynos5420_config_smp(void)
+{
+	init_exynos_cpu_states();
+	configure_secondary_cores();
+}
+
diff --git a/src/mainboard/google/pit/romstage.c b/src/mainboard/google/pit/romstage.c
index 7733eca..7037112 100644
--- a/src/mainboard/google/pit/romstage.c
+++ b/src/mainboard/google/pit/romstage.c
@@ -246,6 +246,7 @@ void main(void)
 	int is_resume = (get_wakeup_state() != IS_NOT_WAKEUP);
 	int power_init_failed;
 
+	exynos5420_config_smp();
 	power_init_failed = setup_power(is_resume);
 
 	/* Clock must be initialized before console_init, otherwise you may need



More information about the coreboot-gerrit mailing list