diff options
author | Christine Gharzuzi <chrisg@marvell.com> | 2018-04-05 19:38:08 +0300 |
---|---|---|
committer | Kostya Porotchkin <kostap@marvell.com> | 2018-04-26 17:23:05 +0300 |
commit | b33b8976e4251d2f546cfa3195d8cea7a9822e60 (patch) | |
tree | 3a1a2483d1142298248164caafd2f20919a6a861 | |
parent | 4f7f5f8c7ca53bd5d83a54df6d5c2d18c8e0bbeb (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.h | 1 | ||||
-rw-r--r-- | plat/marvell/a8k-p/common/include/a8kp_plat_def.h | 1 | ||||
-rw-r--r-- | plat/marvell/a8k-p/common/plat_ble_setup.c | 6 | ||||
-rw-r--r-- | plat/marvell/a8k-p/common/plat_pm.c | 198 |
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); |