summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristine Gharzuzi <chrisg@marvell.com>2018-04-05 19:38:08 +0300
committerKostya Porotchkin <kostap@marvell.com>2018-04-26 17:23:05 +0300
commitb33b8976e4251d2f546cfa3195d8cea7a9822e60 (patch)
tree3a1a2483d1142298248164caafd2f20919a6a861
parent4f7f5f8c7ca53bd5d83a54df6d5c2d18c8e0bbeb (diff)
a8k-p: pm: poweroff unused CPUs at early boot stage
- bootloader flow uses only CPU0 and CPU1-7 (per AP) remain in reset (not powered off) which leads to more unnecessary power consuming and in some cases might increase SoC temperature. - this patch powers off CPU1-7 in early BLE stage, and powered back on during Linux PSCI calls to a8k_pwr_domain_on. Change-Id: Id2415b9703b5033fa77fda00d1fec22e830610a5 Signed-off-by: Christine Gharzuzi <chrisg@marvell.com> Reviewed-on: http://vgitil04.il.marvell.com:8080/52915 Tested-by: iSoC Platform CI <ykjenk@marvell.com> Reviewed-by: Kostya Porotchkin <kostap@marvell.com>
-rw-r--r--include/plat/marvell/a8k-p/common/plat_marvell.h1
-rw-r--r--plat/marvell/a8k-p/common/include/a8kp_plat_def.h1
-rw-r--r--plat/marvell/a8k-p/common/plat_ble_setup.c6
-rw-r--r--plat/marvell/a8k-p/common/plat_pm.c198
4 files changed, 204 insertions, 2 deletions
diff --git a/include/plat/marvell/a8k-p/common/plat_marvell.h b/include/plat/marvell/a8k-p/common/plat_marvell.h
index a56a1805..7711a7df 100644
--- a/include/plat/marvell/a8k-p/common/plat_marvell.h
+++ b/include/plat/marvell/a8k-p/common/plat_marvell.h
@@ -109,4 +109,5 @@ void ble_prepare_exit(void);
int jtag_init_ihb_dual_ap(void);
int gic600_multi_chip_init(void);
+int plat_marvell_early_cpu_powerdown(int ap_count);
#endif /* __PLAT_MARVELL_H__ */
diff --git a/plat/marvell/a8k-p/common/include/a8kp_plat_def.h b/plat/marvell/a8k-p/common/include/a8kp_plat_def.h
index dde6617a..591042e8 100644
--- a/plat/marvell/a8k-p/common/include/a8kp_plat_def.h
+++ b/plat/marvell/a8k-p/common/include/a8kp_plat_def.h
@@ -54,6 +54,7 @@
#define MVEBU_AP_UART_BASE(ap) (MVEBU_REGS_BASE_AP(ap) + 0x512000)
#define MVEBU_MRI_XBAR_BASE(ap) (MVEBU_REGS_BASE_AP(ap) + 0x6A0000)
#define MVEBU_AR_RFU_BASE(ap) (MVEBU_REGS_BASE_AP(ap) + 0x6F0000)
+#define MVEBU_AP_PWRC_BASE(ap) (MVEBU_REGS_BASE_AP(ap) + 0x680000)
#define MVEBU_IO_WIN_BASE(ap) (MVEBU_AR_RFU_BASE(ap))
#define MVEBU_IO_WIN_GCR_OFFSET (0xF0)
#define MVEBU_IO_WIN_MAX_WINS (11)
diff --git a/plat/marvell/a8k-p/common/plat_ble_setup.c b/plat/marvell/a8k-p/common/plat_ble_setup.c
index 7e8266fa..e217dbb9 100644
--- a/plat/marvell/a8k-p/common/plat_ble_setup.c
+++ b/plat/marvell/a8k-p/common/plat_ble_setup.c
@@ -40,14 +40,18 @@ static void ble_read_cpu_freq(void)
int ble_plat_setup(int *skip)
{
int ret = 0;
+ int ap_count = ap810_get_ap_count();
#if !PALLADIUM
/* SW WA for AP link bring-up over JTAG connection */
- if ((ap810_get_ap_count() != 1) &&
+ if ((ap_count != 1) &&
(ap810_rev_id_get(MVEBU_AP0) == MVEBU_AP810_REV_ID_A0))
jtag_init_ihb_dual_ap();
#endif
+ /* Power down unused CPUs */
+ plat_marvell_early_cpu_powerdown(ap_count);
+
ble_read_cpu_freq();
/* Initialize the enumeration algorithm in BLE stage to
diff --git a/plat/marvell/a8k-p/common/plat_pm.c b/plat/marvell/a8k-p/common/plat_pm.c
index a5a9350b..972ec91d 100644
--- a/plat/marvell/a8k-p/common/plat_pm.c
+++ b/plat/marvell/a8k-p/common/plat_pm.c
@@ -11,16 +11,101 @@
#include <debug.h>
#include <plat_marvell.h>
#include <plat_private.h>
+#include <delay_timer.h>
#define MVEBU_CCU_RVBAR(ap, clus, cpu) (MVEBU_REGS_BASE_AP(ap) + 0x1800 + \
(0x400 * clus) + 0x240 + (cpu * 0x4))
#define MVEBU_CCU_PRCR(ap, clus, cpu) (MVEBU_REGS_BASE_AP(ap) + 0x1800 + \
(0x400 * clus) + 0x250 + (cpu * 0x4))
+#define MVEBU_CCU_PRCR_OFFSET 0
+#define MVEBU_CCU_PRCR_MASK (0x1 << MVEBU_CCU_PRCR_OFFSET)
+
#define MVEBU_RFU_GLOBL_SW_RST 0x184
+#define PWRC_CPUN_CR_REG(ap_id, cpu_id) (MVEBU_AP_PWRC_BASE(ap_id) + (cpu_id * 0x10))
+#define PWRC_CPUN_CR_PWR_DN_RQ_OFFSET 1
+#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 0
+#define PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK (0x1 << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET)
+
+/* power switch fingers */
+#define PWRC_LDO_CR0_REG(ap_id) (MVEBU_AP_PWRC_BASE(ap_id) + 0x100)
+#define PWRC_LDO_CR0_OFFSET 16
+#define PWRC_LDO_CR0_MASK (0xff << PWRC_LDO_CR0_OFFSET)
+#define PWRC_LDO_CR0_VAL 0xfd
+
+#define REG_WR_VALIDATE_TIMEOUT (2000)
+
int ap_init_status[PLAT_MARVELL_NORTHB_COUNT];
+/*
+ * Power down CPU:
+ * Used to reduce power consumption, and avoid SoC unnecessary temperature rise.
+ */
+static int plat_marvell_cpu_powerdown(int ap_id, int clust_id, int cpu_id)
+{
+ uint32_t reg_val;
+ int exit_loop;
+
+ INFO("Powering down CPU%d\n", cpu_id);
+
+ /* 1. Isolation enable */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(ap_id, cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_ISO_ENABLE_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, cpu_id), reg_val);
+
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ /* 2. Read and check Isolation enabled - verify bit set to 1 */
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(ap_id, 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(ap_id, cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_PWR_DN_RQ_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, 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(ap_id, 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(ap_id, cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_LDO_BYPASS_RDY_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, cpu_id), reg_val);
+
+ /* 6. Assert CPU POR reset */
+ reg_val = mmio_read_32(MVEBU_CCU_PRCR(ap_id, clust_id, cpu_id));
+ reg_val &= ~MVEBU_CCU_PRCR_MASK;
+ mmio_write_32(MVEBU_CCU_PRCR(ap_id, clust_id, 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(MVEBU_CCU_PRCR(ap_id, clust_id, cpu_id));
+ exit_loop--;
+ } while (reg_val & MVEBU_CCU_PRCR_MASK && exit_loop > 0);
+
+ if (exit_loop <= 0)
+ goto cpu_poweroff_error;
+
+ return 0;
+
+cpu_poweroff_error:
+ ERROR("ERROR: Can't power down CPU%d\n" , cpu_id);
+ return -1;
+}
+
static int plat_marvell_cpu_on(u_register_t mpidr)
{
int ap_id, clust_id, cpu_id;
@@ -95,13 +180,124 @@ static void a8kp_cpu_standby(plat_local_state_t cpu_state)
panic();
}
+/*
+ * Power down CPUs 1-7 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(int ap_count)
+{
+ int cpu_id, ap_id, ret;
+ int cpu_count = PLAT_MARVELL_CLUSTER_CORE_COUNT * PLAT_MARVELL_CLUSTER_PER_NB;
+
+ /* powedown all CPU's except CPU0 is AP0 */
+ for (ap_id = 0 ; ap_id < ap_count ; ap_id++) {
+ for (cpu_id = (ap_id == 0) ? 1 : 0; cpu_id < cpu_count; cpu_id++) {
+ ret = plat_marvell_cpu_powerdown(ap_id, cpu_id /
+ PLAT_MARVELL_CLUSTER_CORE_COUNT,
+ cpu_id);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Power up CPU - part of Linux boot stage
+ */
+static int plat_marvell_cpu_powerup(u_register_t mpidr)
+{
+ uint32_t reg_val;
+ int cpu_id = MPIDR_CPU_ID_GET(mpidr), cluster = MPIDR_CLUSTER_ID_GET(mpidr);
+ int ap_id = MPIDR_AP_ID_GET(mpidr);
+ int exit_loop;
+
+ /* calculate absolute CPU ID */
+ cpu_id = cluster * PLAT_MARVELL_CLUSTER_CORE_COUNT + cpu_id;
+
+ INFO("Powering on CPU%d\n", cpu_id);
+
+ /* Activate 2 power switch fingers */
+ reg_val = mmio_read_32(PWRC_LDO_CR0_REG(ap_id));
+ reg_val &= ~(PWRC_LDO_CR0_MASK);
+ reg_val |= (PWRC_LDO_CR0_VAL << PWRC_LDO_CR0_OFFSET);
+ mmio_write_32(PWRC_LDO_CR0_REG(ap_id), reg_val);
+
+ udelay(100);
+
+ /* 1. Switch CPU power ON */
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(ap_id, cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_PWR_DN_RQ_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, 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(ap_id, cpu_id));
+ reg_val |= 0x1 << PWRC_CPUN_CR_LDO_BYPASS_RDY_OFFSET;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, cpu_id), reg_val);
+
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ /* 4. Read & Validate power ready - used in order to generate 16 Host CPU cycles */
+ do {
+ reg_val = mmio_read_32(PWRC_CPUN_CR_REG(ap_id, 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(ap_id, cpu_id));
+ reg_val &= ~PWRC_CPUN_CR_ISO_ENABLE_MASK;
+ mmio_write_32(PWRC_CPUN_CR_REG(ap_id, 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(ap_id, 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(MVEBU_CCU_PRCR(ap_id, cluster, cpu_id));
+ reg_val |= 0x1 << MVEBU_CCU_PRCR_OFFSET;
+ mmio_write_32(MVEBU_CCU_PRCR(ap_id, cluster, cpu_id), reg_val);
+
+ /* 8. Read & Validate CPU POR reset */
+ exit_loop = REG_WR_VALIDATE_TIMEOUT;
+ do {
+ reg_val = mmio_read_32(MVEBU_CCU_PRCR(ap_id, cluster, cpu_id));
+ exit_loop--;
+ } while (!(reg_val & (0x1 << MVEBU_CCU_PRCR_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;
+}
+
/*******************************************************************************
* A8Kp handler called when a power domain is about to be turned on. The
* mpidr determines the CPU to be turned on.
******************************************************************************/
static int a8kp_pwr_domain_on(u_register_t mpidr)
{
- /* TODO: check if cpu_powerup is needed for AP810 */
+ /* Power up CPU (CPUs 1-7 are powered off at start of BLE) */
+ plat_marvell_cpu_powerup(mpidr);
/* proprietary CPU ON exection flow */
plat_marvell_cpu_on(mpidr);