summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmri Itach <omrii@marvell.com>2017-06-27 11:24:50 +0300
committerKostya Porotchkin <kostap@marvell.com>2017-07-10 11:52:34 +0300
commitb8857ff89a6804250e27f7c1dcf1454a3eaf47c8 (patch)
tree2a4056924ef47d8335c737007b72133dd46247bd
parentd77aeb0e88718f84489e813355127060c33f935d (diff)
ap806: pm: poweroff unused CPUs at early boot stage
The entire boot flow is performed via CPU0, while CPUs1-3 remain unused untill Linux is booted. During this stage, CPU1-3 are kept in reset (not powered off), hence still consuming current and leading to increased SoC temperatures. To avoid unnecessary current consumption, and mainly to reduce SoC temperatures, unused CPUs are powered off (CPU1 in dual-core system, and CPU1-3 in quad-core systems), at early BLE stage, prior to DDR initialization, and powered back on during Linux PSCI calls to a8k_pwr_domain_on, before getting these CPUs out of reset. This patch was tested on Armada 8040 MACCHIATObin, and it leads to ~10-15% reduction of current consumption and SoC temperature in U-Boot. Change-Id: Ic1baeaa89f2806125d8347badcf5b30c13583162 Signed-off-by: Omri Itach <omrii@marvell.com> Reviewed-on: http://vgitil04.il.marvell.com:8080/40869 Reviewed-by: Hanna Hawa <hannah@marvell.com> Tested-by: iSoC Platform CI <ykjenk@marvell.com> Reviewed-by: Kostya Porotchkin <kostap@marvell.com>
-rw-r--r--include/plat/marvell/a8k/common/plat_marvell.h1
-rw-r--r--plat/marvell/a8k/common/a8k_common.mk1
-rw-r--r--plat/marvell/a8k/common/plat_ble_setup.c3
-rw-r--r--plat/marvell/a8k/common/plat_pm.c209
4 files changed, 214 insertions, 0 deletions
diff --git a/include/plat/marvell/a8k/common/plat_marvell.h b/include/plat/marvell/a8k/common/plat_marvell.h
index d6280ed1..dfc1db19 100644
--- a/include/plat/marvell/a8k/common/plat_marvell.h
+++ b/include/plat/marvell/a8k/common/plat_marvell.h
@@ -127,4 +127,5 @@ void marvell_bl1_setup_mpps(void);
const mmap_region_t *plat_marvell_get_mmap(void);
+int plat_marvell_early_cpu_powerdown(void);
#endif /* __PLAT_MARVELL_H__ */
diff --git a/plat/marvell/a8k/common/a8k_common.mk b/plat/marvell/a8k/common/a8k_common.mk
index 4ab86dc9..b9df51c9 100644
--- a/plat/marvell/a8k/common/a8k_common.mk
+++ b/plat/marvell/a8k/common/a8k_common.mk
@@ -66,6 +66,7 @@ BLE_SOURCES := plat/marvell/common/sys_info.c \
plat/marvell/a8k/common/plat_ble_setup.c \
$(MARVELL_DRV_BASE)/mochi/cp110_setup.c \
$(MARVELL_DRV_BASE)/i2c/a8k_i2c.c \
+ $(PLAT_COMMON_BASE)/plat_pm.c \
$(BLE_PORTING_SOURCES)
ifeq (${PCI_EP_SUPPORT}, 1)
BLE_SOURCES += plat/marvell/common/pci_ep_setup.c \
diff --git a/plat/marvell/a8k/common/plat_ble_setup.c b/plat/marvell/a8k/common/plat_ble_setup.c
index 7213934b..9a14265d 100644
--- a/plat/marvell/a8k/common/plat_ble_setup.c
+++ b/plat/marvell/a8k/common/plat_ble_setup.c
@@ -364,6 +364,9 @@ int ble_plat_setup(int *skip)
int ret;
struct dram_config *cfg;
+ /* Power down unused CPUs */
+ plat_marvell_early_cpu_powerdown();
+
/*
* Save the current CCU configuration and make required changes:
* - Allow access to DRAM larger than 4GB
diff --git a/plat/marvell/a8k/common/plat_pm.c b/plat/marvell/a8k/common/plat_pm.c
index b5535f38..2b155d68 100644
--- a/plat/marvell/a8k/common/plat_pm.c
+++ b/plat/marvell/a8k/common/plat_pm.c
@@ -36,6 +36,7 @@
#include <gicv2.h>
#include <mmio.h>
#include <debug.h>
+#include <delay_timer.h>
#ifdef SCP_IMAGE
#include <bakery_lock.h>
@@ -57,6 +58,211 @@
DEFINE_BAKERY_LOCK(pm_sys_lock);
#endif
+
+/* AP806 CPU power down /power up definitions */
+enum CPU_ID {
+ CPU0,
+ CPU1,
+ CPU2,
+ CPU3
+};
+
+#define CPUS_PER_CLUSTER 2
+#define REG_WR_VALIDATE_TIMEOUT (2000)
+
+#define FEATURE_DISABLE_STATUS_REG (MVEBU_REGS_BASE + 0x6F8230)
+#define FEATURE_DISABLE_STATUS_CPU_CLUSTER_OFFSET 4
+#define FEATURE_DISABLE_STATUS_CPU_CLUSTER_MASK (0x1 << FEATURE_DISABLE_STATUS_CPU_CLUSTER_OFFSET)
+
+#define PWRC_CPUN_CR_REG(cpu_id) (MVEBU_REGS_BASE + 0x680000 + (cpu_id * 0x10))
+#define PWRC_CPUN_CR_PWR_DN_RQ_OFFSET 0
+#define PWRC_CPUN_CR_PWR_DN_RQ_MASK (0x1 << PWRC_CPUN_CR_PWR_DN_RQ_OFFSET)
+#define PWRC_CPUN_CR_ISO_ENABLE_OFFSET 16
+#define PWRC_CPUN_CR_ISO_ENABLE_MASK (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET)
+#define PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET 31
+#define PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK (0x1 << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET)
+
+#define CCU_B_PRCRN_REG(cpu_id) (MVEBU_REGS_BASE + 0x1A50 + \
+ ((cpu_id / 2) * (0x400)) + ((cpu_id % 2) * 4))
+#define CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET 0
+#define CCU_B_PRCRN_CPUPORESET_STATIC_MASK (0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET)
+
+/*
+ * Power down CPU:
+ * Used to reduce power consumption, and avoid SoC unnecessary temperature rise.
+ */
+int plat_marvell_cpu_powerdown(int cpu_id)
+{
+ uint32_t reg_val;
+ int exit_loop = REG_WR_VALIDATE_TIMEOUT;
+
+ INFO("Powering down CPU%d\n", cpu_id);
+
+ /* 1. Isolation enable */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 2. Read and check Isolation enabled - verify bit set to 1 */
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ exit_loop--;
+ } while (!(reg_val & (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET)) && exit_loop > 0);
+
+ /* 3. Switch off CPU power */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_PWR_DN_RQ_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 4. Read and check Switch Off - verify bit set to 0 */
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ exit_loop--;
+ } while (reg_val & PWRC_CPUN_CR_PWR_DN_RQ_MASK && exit_loop > 0);
+
+ if (exit_loop <= 0)
+ goto cpu_poweroff_error;
+
+ /* 5. De-Assert power ready */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 6. Assert CPU POR reset */
+ reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id));
+ reg_val &= ~CCU_B_PRCRN_CPUPORESET_STATIC_MASK;
+ mmio_write_32(CCU_B_PRCRN_REG(cpu_id), reg_val);
+
+ /* 7. Read and poll on Validate the CPU is out of reset */
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ do {
+ reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id));
+ exit_loop--;
+ } while (reg_val & CCU_B_PRCRN_CPUPORESET_STATIC_MASK && exit_loop > 0);
+
+ if (exit_loop <= 0)
+ goto cpu_poweroff_error;
+
+ INFO("Successfully powered down CPU%d\n", cpu_id);
+
+ return 0;
+
+cpu_poweroff_error:
+ ERROR("ERROR: Can't power down CPU%d\n" , cpu_id);
+ return -1;
+}
+
+/*
+ * Power down CPUs 1-3 at early boot stage,
+ * to reduce power consumption and SoC temperature.
+ * This is triggered by BLE prior to DDR initialization.
+ *
+ * Note:
+ * All CPUs will be powered up by plat_marvell_cpu_powerup on Linux boot stage,
+ * which is triggered by PSCI ops (pwr_domain_on).
+ */
+int plat_marvell_early_cpu_powerdown(void)
+{
+ uint32_t cpu_cluster_status = mmio_read_32(FEATURE_DISABLE_STATUS_REG)
+ & FEATURE_DISABLE_STATUS_CPU_CLUSTER_MASK;
+ /* if cpu_cluster_status bit is set, that means we have only single cluster */
+ int cluster_count = cpu_cluster_status ? 1 : 2;
+
+ INFO("Powering off unused CPUs\n");
+
+ /* CPU1 is in AP806 cluster-0, which always exists - so power it down */
+ if (plat_marvell_cpu_powerdown(CPU1) == -1)
+ return -1;
+
+ /*
+ * CPU2-3 are in AP806 2nd cluster (cluster-1), which doesn't exists in dual-core systems.
+ * so need to check if we have dual-core (single cluster) or quad-code (2 clusters)
+ */
+ if (cluster_count == 2) {
+ /* CPU2-3 are part of 2nd cluster */
+ if (plat_marvell_cpu_powerdown(CPU2) == -1)
+ return -1;
+ if (plat_marvell_cpu_powerdown(CPU3) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Power up CPU - part of Linux boot stage
+ */
+int plat_marvell_cpu_powerup(u_register_t mpidr)
+{
+ uint32_t reg_val;
+ int cpu_id = MPIDR_CPU_GET(mpidr), cluster = MPIDR_CLUSTER_GET(mpidr);
+ int exit_loop = REG_WR_VALIDATE_TIMEOUT;
+
+ /* calculate absolute CPU ID */
+ cpu_id = cluster * CPUS_PER_CLUSTER + cpu_id;
+
+ INFO("Powering on CPU%d\n", cpu_id);
+
+ /* 1. Switch CPU power ON */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_PWR_DN_RQ_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 2. Wait for CPU on, up to 100 uSec: */
+ udelay(100);
+
+ /* 3. Assert power ready */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 4. Read & Validate power ready - used in order to generate 16 Host CPU cycles */
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ exit_loop--;
+ } while (!(reg_val & (0x1 << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET)) && exit_loop > 0);
+
+ if (exit_loop <= 0)
+ goto cpu_poweron_error;
+
+ /* 5. Isolation disable */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_ISO_ENABLE_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(cpu_id), reg_val);
+
+ /* 6. Read and check Isolation enabled - verify bit set to 1 */
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(cpu_id));
+ exit_loop--;
+ } while ((reg_val & (0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET)) && exit_loop > 0);
+
+ /* 7. De Assert CPU POR reset & Core reset */
+ reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id));
+ reg_val |= 0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET;
+ mmio_write_32(CCU_B_PRCRN_REG(cpu_id), reg_val);
+
+ /* 8. Read & Validate CPU POR reset */
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ do {
+ reg_val = mmio_read_32(CCU_B_PRCRN_REG(cpu_id));
+ exit_loop--;
+ } while (!(reg_val & (0x1 << CCU_B_PRCRN_CPUPORESET_STATIC_OFFSET)) && exit_loop > 0);
+
+ if (exit_loop <= 0)
+ goto cpu_poweron_error;
+
+ INFO("Successfully powered on CPU%d\n", cpu_id);
+
+ return 0;
+
+cpu_poweron_error:
+ ERROR("ERROR: Can't power up CPU%d\n" , cpu_id);
+ return -1;
+}
+
+
int plat_marvell_cpu_on(u_register_t mpidr)
{
int cpu_id;
@@ -144,6 +350,9 @@ void a8k_cpu_standby(plat_local_state_t cpu_state)
******************************************************************************/
int a8k_pwr_domain_on(u_register_t mpidr)
{
+ /* Power up CPU (CPUs 1-3 are powered off at start of BLE) */
+ plat_marvell_cpu_powerup(mpidr);
+
#ifdef SCP_IMAGE
unsigned int target = ((mpidr & 0xFF) + (((mpidr >> 8) & 0xFF) * 2));