Browse Source
Support BL31 on HiKey960 platform. Implement PSCI. Signed-off-by: Leo Yan <leo.yan@linaro.org> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>pull/959/head
Haojian Zhuang
8 years ago
11 changed files with 1414 additions and 0 deletions
@ -0,0 +1,204 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <assert.h> |
|||
#include <hi3660.h> |
|||
#include <mmio.h> |
|||
#include <platform.h> |
|||
#include <platform_def.h> |
|||
#include <hisi_ipc.h> |
|||
#include <debug.h> |
|||
|
|||
#include "../../hikey960_private.h" |
|||
|
|||
#define IPC_MBX_SOURCE_REG(m) (IPC_BASE + ((m) << 6)) |
|||
#define IPC_MBX_DSET_REG(m) (IPC_BASE + ((m) << 6) + 0x04) |
|||
#define IPC_MBX_DCLEAR_REG(m) (IPC_BASE + ((m) << 6) + 0x08) |
|||
#define IPC_MBX_DSTATUS_REG(m) (IPC_BASE + ((m) << 6) + 0x0C) |
|||
#define IPC_MBX_MODE_REG(m) (IPC_BASE + ((m) << 6) + 0x10) |
|||
#define IPC_MBX_IMASK_REG(m) (IPC_BASE + ((m) << 6) + 0x14) |
|||
#define IPC_MBX_ICLR_REG(m) (IPC_BASE + ((m) << 6) + 0x18) |
|||
#define IPC_MBX_SEND_REG(m) (IPC_BASE + ((m) << 6) + 0x1C) |
|||
#define IPC_MBX_DATA_REG(m, d) (IPC_BASE + ((m) << 6) + 0x20 + \ |
|||
((d) * 4)) |
|||
#define IPC_CPU_IMST_REG(m) (IPC_BASE + ((m) << 3)) |
|||
#define IPC_LOCK_REG (IPC_BASE + 0xA00) |
|||
#define IPC_ACK_BIT_SHIFT (1 << 7) |
|||
#define IPC_UNLOCK_VALUE (0x1ACCE551) |
|||
|
|||
/*********************************************************
|
|||
*bit[31:24]:0~AP |
|||
*bit[23:16]:0x1~A15, 0x2~A7 |
|||
*bit[15:8]:0~ON, 1~OFF |
|||
*bit[7:0]:0x3 cpu power mode |
|||
*********************************************************/ |
|||
#define IPC_CMD_TYPE(src_obj, cluster_obj, is_off, mode) \ |
|||
((src_obj << 24) | (((cluster_obj) + 1) << 16) | (is_off << 8) | (mode)) |
|||
|
|||
/*********************************************************
|
|||
*bit[15:8]:0~no idle, 1~idle |
|||
*bit[7:0]:cpux |
|||
*********************************************************/ |
|||
|
|||
#define IPC_CMD_PARA(is_idle, cpu) \ |
|||
((is_idle << 8) | (cpu)) |
|||
|
|||
#define IPC_STATE_IDLE 0x10 |
|||
|
|||
enum src_id { |
|||
SRC_IDLE = 0, |
|||
SRC_A15 = 1 << 0, |
|||
SRC_A7 = 1 << 1, |
|||
SRC_IOM3 = 1 << 2, |
|||
SRC_LPM3 = 1 << 3 |
|||
}; |
|||
|
|||
/*lpm3's mailboxs are 13~17*/ |
|||
enum lpm3_mbox_id { |
|||
LPM3_MBX0 = 13, |
|||
LPM3_MBX1, |
|||
LPM3_MBX2, |
|||
LPM3_MBX3, |
|||
LPM3_MBX4, |
|||
}; |
|||
|
|||
static void cpu_relax(void) |
|||
{ |
|||
volatile int i; |
|||
|
|||
for (i = 0; i < 10; i++) |
|||
nop(); |
|||
} |
|||
|
|||
static inline void |
|||
hisi_ipc_clear_ack(enum src_id source, enum lpm3_mbox_id mbox) |
|||
{ |
|||
unsigned int int_status = 0; |
|||
|
|||
do { |
|||
int_status = mmio_read_32(IPC_MBX_MODE_REG(mbox)); |
|||
int_status &= 0xF0; |
|||
cpu_relax(); |
|||
} while (int_status != IPC_ACK_BIT_SHIFT); |
|||
|
|||
mmio_write_32(IPC_MBX_ICLR_REG(mbox), source); |
|||
} |
|||
|
|||
static void |
|||
hisi_ipc_send_cmd_with_ack(enum src_id source, enum lpm3_mbox_id mbox, |
|||
unsigned int cmdtype, unsigned int cmdpara) |
|||
{ |
|||
unsigned int regval; |
|||
unsigned int mask; |
|||
unsigned int state; |
|||
|
|||
mmio_write_32(IPC_LOCK_REG, IPC_UNLOCK_VALUE); |
|||
/* wait for idle and occupy */ |
|||
do { |
|||
state = mmio_read_32(IPC_MBX_MODE_REG(mbox)); |
|||
if (state == IPC_STATE_IDLE) { |
|||
mmio_write_32(IPC_MBX_SOURCE_REG(mbox), source); |
|||
regval = mmio_read_32(IPC_MBX_SOURCE_REG(mbox)); |
|||
if (regval == source) |
|||
break; |
|||
} |
|||
cpu_relax(); |
|||
|
|||
} while (1); |
|||
|
|||
/* auto answer */ |
|||
mmio_write_32(IPC_MBX_MODE_REG(mbox), 0x1); |
|||
|
|||
mask = (~((int)source | SRC_LPM3) & 0x3F); |
|||
/* mask the other cpus */ |
|||
mmio_write_32(IPC_MBX_IMASK_REG(mbox), mask); |
|||
/* set data */ |
|||
mmio_write_32(IPC_MBX_DATA_REG(mbox, 0), cmdtype); |
|||
mmio_write_32(IPC_MBX_DATA_REG(mbox, 1), cmdpara); |
|||
/* send cmd */ |
|||
mmio_write_32(IPC_MBX_SEND_REG(mbox), source); |
|||
/* wait ack and clear */ |
|||
hisi_ipc_clear_ack(source, mbox); |
|||
|
|||
/* release mailbox */ |
|||
mmio_write_32(IPC_MBX_SOURCE_REG(mbox), source); |
|||
} |
|||
|
|||
void hisi_ipc_pm_on_off(unsigned int core, unsigned int cluster, |
|||
enum pm_mode mode) |
|||
{ |
|||
unsigned int cmdtype = 0; |
|||
unsigned int cmdpara = 0; |
|||
enum src_id source = SRC_IDLE; |
|||
enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); |
|||
|
|||
cmdtype = IPC_CMD_TYPE(0, cluster, mode, 0x3); |
|||
cmdpara = IPC_CMD_PARA(0, core); |
|||
source = cluster ? SRC_A7 : SRC_A15; |
|||
hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); |
|||
} |
|||
|
|||
void hisi_ipc_pm_suspend(unsigned int core, unsigned int cluster, |
|||
unsigned int affinity_level) |
|||
{ |
|||
unsigned int cmdtype = 0; |
|||
unsigned int cmdpara = 0; |
|||
enum src_id source = SRC_IDLE; |
|||
enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); |
|||
|
|||
if (affinity_level == 0x3) |
|||
cmdtype = IPC_CMD_TYPE(0, -1, 0x1, 0x3 + affinity_level); |
|||
else |
|||
cmdtype = IPC_CMD_TYPE(0, cluster, 0x1, 0x3 + affinity_level); |
|||
|
|||
cmdpara = IPC_CMD_PARA(1, core); |
|||
source = cluster ? SRC_A7 : SRC_A15; |
|||
hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); |
|||
} |
|||
|
|||
void hisi_ipc_psci_system_off(unsigned int core, unsigned int cluster) |
|||
{ |
|||
unsigned int cmdtype = 0; |
|||
unsigned int cmdpara = 0; |
|||
enum src_id source = SRC_IDLE; |
|||
enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); |
|||
|
|||
cmdtype = IPC_CMD_TYPE(0, (0x10 - 1), 0x1, 0x0); |
|||
cmdpara = IPC_CMD_PARA(0, 0); |
|||
source = cluster ? SRC_A7 : SRC_A15; |
|||
hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); |
|||
} |
|||
|
|||
void hisi_ipc_psci_system_reset(unsigned int core, unsigned int cluster, |
|||
unsigned int cmd_id) |
|||
{ |
|||
unsigned int cmdtype = 0; |
|||
unsigned int cmdpara = 0; |
|||
enum src_id source = SRC_IDLE; |
|||
enum lpm3_mbox_id mailbox = (enum lpm3_mbox_id)(LPM3_MBX0 + core); |
|||
|
|||
cmdtype = IPC_CMD_TYPE(0, (0x10 - 1), 0x0, 0x0); |
|||
cmdpara = cmd_id; |
|||
source = cluster ? SRC_A7 : SRC_A15; |
|||
hisi_ipc_send_cmd_with_ack(source, mailbox, cmdtype, cmdpara); |
|||
} |
|||
|
|||
int hisi_ipc_init(void) |
|||
{ |
|||
int ret = 0; |
|||
enum lpm3_mbox_id i = LPM3_MBX0; |
|||
|
|||
mmio_write_32(IPC_LOCK_REG, IPC_UNLOCK_VALUE); |
|||
for (i = LPM3_MBX0; i <= LPM3_MBX4; i++) { |
|||
mmio_write_32(IPC_MBX_MODE_REG(i), 1); |
|||
mmio_write_32(IPC_MBX_IMASK_REG(i), |
|||
((int)SRC_IOM3 | (int)SRC_A15 | (int)SRC_A7)); |
|||
mmio_write_32(IPC_MBX_ICLR_REG(i), SRC_A7); |
|||
} |
|||
|
|||
return ret; |
|||
} |
@ -0,0 +1,395 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <assert.h> |
|||
#include <mmio.h> |
|||
#include <platform.h> |
|||
#include <platform_def.h> |
|||
#include <../hikey960_def.h> |
|||
#include <hisi_ipc.h> |
|||
#include "hisi_pwrc.h" |
|||
|
|||
|
|||
/* resource lock api */ |
|||
#define RES0_LOCK_BASE (SOC_PCTRL_RESOURCE0_LOCK_ADDR(PCTRL_BASE)) |
|||
#define RES1_LOCK_BASE (SOC_PCTRL_RESOURCE1_LOCK_ADDR(PCTRL_BASE)) |
|||
#define RES2_LOCK_BASE (SOC_PCTRL_RESOURCE2_LOCK_ADDR(PCTRL_BASE)) |
|||
|
|||
#define LOCK_BIT (0x1 << 28) |
|||
#define LOCK_ID_MASK (0x7 << 29) |
|||
#define CPUIDLE_LOCK_ID(core) (0x6 - (core)) |
|||
#define LOCK_UNLOCK_OFFSET 0x4 |
|||
#define LOCK_STAT_OFFSET 0x8 |
|||
|
|||
#define CLUSTER0_CPUS_ONLINE_MASK (0xF << 16) |
|||
#define CLUSTER1_CPUS_ONLINE_MASK (0xF << 20) |
|||
|
|||
/* cpu hotplug flag api */ |
|||
#define SCTRL_BASE (SOC_ACPU_SCTRL_BASE_ADDR) |
|||
#define REG_SCBAKDATA3_OFFSET (SOC_SCTRL_SCBAKDATA3_ADDR(SCTRL_BASE)) |
|||
#define REG_SCBAKDATA8_OFFSET (SOC_SCTRL_SCBAKDATA8_ADDR(SCTRL_BASE)) |
|||
#define REG_SCBAKDATA9_OFFSET (SOC_SCTRL_SCBAKDATA9_ADDR(SCTRL_BASE)) |
|||
|
|||
#define CPUIDLE_FLAG_REG(cluster) \ |
|||
((cluster == 0) ? REG_SCBAKDATA8_OFFSET : \ |
|||
REG_SCBAKDATA9_OFFSET) |
|||
#define CLUSTER_IDLE_BIT BIT(8) |
|||
#define CLUSTER_IDLE_MASK (CLUSTER_IDLE_BIT | 0x0F) |
|||
|
|||
#define AP_SUSPEND_FLAG (1 << 16) |
|||
|
|||
#define CLUSTER_PWDN_IDLE (0<<28) |
|||
#define CLUSTER_PWDN_HOTPLUG (1<<28) |
|||
#define CLUSTER_PWDN_SR (2<<28) |
|||
|
|||
#define CLUSTER0_PDC_OFFSET 0x260 |
|||
#define CLUSTER1_PDC_OFFSET 0x300 |
|||
|
|||
#define PDC_EN_OFFSET 0x0 |
|||
#define PDC_COREPWRINTEN_OFFSET 0x4 |
|||
#define PDC_COREPWRINTSTAT_OFFSET 0x8 |
|||
#define PDC_COREGICMASK_OFFSET 0xc |
|||
#define PDC_COREPOWERUP_OFFSET 0x10 |
|||
#define PDC_COREPOWERDN_OFFSET 0x14 |
|||
#define PDC_COREPOWERSTAT_OFFSET 0x18 |
|||
|
|||
#define PDC_COREPWRSTAT_MASK (0XFFFF) |
|||
|
|||
enum pdc_gic_mask { |
|||
PDC_MASK_GIC_WAKE_IRQ, |
|||
PDC_UNMASK_GIC_WAKE_IRQ |
|||
}; |
|||
|
|||
enum pdc_finish_int_mask { |
|||
PDC_DISABLE_FINISH_INT, |
|||
PDC_ENABLE_FINISH_INT |
|||
}; |
|||
|
|||
static void hisi_resource_lock(unsigned int lockid, unsigned int offset) |
|||
{ |
|||
unsigned int lock_id = (lockid << 29); |
|||
unsigned int lock_val = lock_id | LOCK_BIT; |
|||
unsigned int lock_state; |
|||
|
|||
do { |
|||
mmio_write_32(offset, lock_val); |
|||
lock_state = mmio_read_32(LOCK_STAT_OFFSET + (uintptr_t)offset); |
|||
} while ((lock_state & LOCK_ID_MASK) != lock_id); |
|||
} |
|||
|
|||
static void hisi_resource_unlock(unsigned int lockid, unsigned int offset) |
|||
{ |
|||
unsigned int lock_val = (lockid << 29) | LOCK_BIT; |
|||
|
|||
mmio_write_32((LOCK_UNLOCK_OFFSET + (uintptr_t)offset), lock_val); |
|||
} |
|||
|
|||
|
|||
static void hisi_cpuhotplug_lock(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int lock_id; |
|||
|
|||
lock_id = (cluster << 2) + core; |
|||
|
|||
hisi_resource_lock(lock_id, RES2_LOCK_BASE); |
|||
} |
|||
|
|||
static void hisi_cpuhotplug_unlock(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int lock_id; |
|||
|
|||
lock_id = (cluster << 2) + core; |
|||
|
|||
hisi_resource_unlock(lock_id, RES2_LOCK_BASE); |
|||
} |
|||
|
|||
/* get the resource lock */ |
|||
void hisi_cpuidle_lock(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); |
|||
|
|||
hisi_resource_lock(CPUIDLE_LOCK_ID(core), offset); |
|||
} |
|||
|
|||
/* release the resource lock */ |
|||
void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); |
|||
|
|||
hisi_resource_unlock(CPUIDLE_LOCK_ID(core), offset); |
|||
} |
|||
|
|||
unsigned int hisi_get_cpuidle_flag(unsigned int cluster) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
val = mmio_read_32(CPUIDLE_FLAG_REG(cluster)); |
|||
val &= 0xF; |
|||
|
|||
return val; |
|||
} |
|||
|
|||
void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core) |
|||
{ |
|||
mmio_setbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); |
|||
} |
|||
|
|||
void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core) |
|||
{ |
|||
mmio_clrbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); |
|||
|
|||
} |
|||
|
|||
int hisi_test_ap_suspend_flag(unsigned int cluster) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
val = mmio_read_32(CPUIDLE_FLAG_REG(cluster)); |
|||
val &= AP_SUSPEND_FLAG; |
|||
return !!val; |
|||
} |
|||
|
|||
void hisi_set_cluster_pwdn_flag(unsigned int cluster, |
|||
unsigned int core, unsigned int value) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
hisi_cpuhotplug_lock(cluster, core); |
|||
|
|||
val = mmio_read_32(REG_SCBAKDATA3_OFFSET); |
|||
val = (value << (cluster << 1)) | (val & 0xFFFFFFF); |
|||
mmio_write_32(REG_SCBAKDATA3_OFFSET, val); |
|||
|
|||
hisi_cpuhotplug_unlock(cluster, core); |
|||
} |
|||
|
|||
unsigned int hisi_get_cpu_boot_flag(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
hisi_cpuhotplug_lock(cluster, core); |
|||
val = mmio_read_32(REG_SCBAKDATA3_OFFSET); |
|||
val = val >> (16 + (cluster << 2)); |
|||
val &= 0xF; |
|||
hisi_cpuhotplug_unlock(cluster, core); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
hisi_cpuhotplug_lock(cluster, core); |
|||
val = mmio_read_32(REG_SCBAKDATA3_OFFSET); |
|||
val = val >> (16 + (cluster << 2)); |
|||
val &= 0xF; |
|||
hisi_cpuhotplug_unlock(cluster, core); |
|||
|
|||
if (val) |
|||
return 0; |
|||
else |
|||
return 1; |
|||
} |
|||
|
|||
void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int flag = BIT((cluster<<2) + core + 16); |
|||
|
|||
hisi_cpuhotplug_lock(cluster, core); |
|||
|
|||
mmio_setbits_32(REG_SCBAKDATA3_OFFSET, flag); |
|||
|
|||
hisi_cpuhotplug_unlock(cluster, core); |
|||
} |
|||
|
|||
void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int flag = BIT((cluster<<2) + core + 16); |
|||
|
|||
hisi_cpuhotplug_lock(cluster, core); |
|||
|
|||
mmio_clrbits_32(REG_SCBAKDATA3_OFFSET, flag); |
|||
|
|||
hisi_cpuhotplug_unlock(cluster, core); |
|||
} |
|||
|
|||
int cluster_is_powered_on(unsigned int cluster) |
|||
{ |
|||
unsigned int val = mmio_read_32(REG_SCBAKDATA3_OFFSET); |
|||
int ret; |
|||
|
|||
if (cluster == 0) |
|||
ret = val & CLUSTER0_CPUS_ONLINE_MASK; |
|||
else |
|||
ret = val & CLUSTER1_CPUS_ONLINE_MASK; |
|||
|
|||
return !!ret; |
|||
} |
|||
|
|||
static void *hisi_get_pdc_addr(unsigned int cluster) |
|||
{ |
|||
void *pdc_base_addr; |
|||
uintptr_t addr; |
|||
|
|||
if (cluster == 0) |
|||
addr = SOC_CRGPERIPH_A53_PDCEN_ADDR(CRG_BASE); |
|||
else |
|||
addr = SOC_CRGPERIPH_MAIA_PDCEN_ADDR(CRG_BASE); |
|||
pdc_base_addr = (void *)addr; |
|||
|
|||
return pdc_base_addr; |
|||
} |
|||
|
|||
static unsigned int hisi_get_pdc_stat(unsigned int cluster) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
unsigned int val; |
|||
|
|||
val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPOWERSTAT_OFFSET); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core) |
|||
{ |
|||
unsigned int mask = 0xf << (core * 4); |
|||
unsigned int pdc_stat = hisi_get_pdc_stat(cluster); |
|||
unsigned int boot_flag = hisi_get_cpu_boot_flag(cluster, core); |
|||
unsigned int cpuidle_flag = hisi_get_cpuidle_flag(cluster); |
|||
|
|||
mask = (PDC_COREPWRSTAT_MASK & (~mask)); |
|||
pdc_stat &= mask; |
|||
|
|||
if ((boot_flag ^ cpuidle_flag) || pdc_stat) |
|||
return 0; |
|||
else |
|||
return 1; |
|||
} |
|||
|
|||
void hisi_disable_pdc(unsigned int cluster) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr, 0x0); |
|||
} |
|||
|
|||
void hisi_enable_pdc(unsigned int cluster) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr, 0x1); |
|||
} |
|||
|
|||
static inline void hisi_pdc_set_intmask(void *pdc_base_addr, |
|||
unsigned int core, |
|||
enum pdc_finish_int_mask intmask) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET); |
|||
if (intmask == PDC_ENABLE_FINISH_INT) |
|||
val |= BIT(core); |
|||
else |
|||
val &= ~BIT(core); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, val); |
|||
} |
|||
|
|||
static inline void hisi_pdc_set_gicmask(void *pdc_base_addr, |
|||
unsigned int core, |
|||
enum pdc_gic_mask gicmask) |
|||
{ |
|||
unsigned int val; |
|||
|
|||
val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET); |
|||
if (gicmask == PDC_MASK_GIC_WAKE_IRQ) |
|||
val |= BIT(core); |
|||
else |
|||
val &= ~BIT(core); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET, val); |
|||
} |
|||
|
|||
void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster) |
|||
{ |
|||
int i; |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
for (i = 0; i < 4; i++) |
|||
hisi_pdc_set_gicmask(pdc_base_addr, i, PDC_MASK_GIC_WAKE_IRQ); |
|||
} |
|||
|
|||
static void hisi_pdc_powerup_core(unsigned int cluster, unsigned int core, |
|||
enum pdc_gic_mask gicmask, |
|||
enum pdc_finish_int_mask intmask) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERUP_OFFSET, |
|||
BIT(core)); |
|||
} |
|||
|
|||
static void hisi_pdc_powerdn_core(unsigned int cluster, unsigned int core, |
|||
enum pdc_gic_mask gicmask, |
|||
enum pdc_finish_int_mask intmask) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, |
|||
BIT(core)); |
|||
} |
|||
|
|||
void hisi_powerup_core(unsigned int cluster, unsigned int core) |
|||
{ |
|||
hisi_pdc_powerup_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, |
|||
PDC_DISABLE_FINISH_INT); |
|||
} |
|||
|
|||
void hisi_powerdn_core(unsigned int cluster, unsigned int core) |
|||
{ |
|||
hisi_pdc_powerdn_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, |
|||
PDC_DISABLE_FINISH_INT); |
|||
} |
|||
|
|||
void hisi_powerup_cluster(unsigned int cluster, unsigned int core) |
|||
{ |
|||
hisi_ipc_pm_on_off(core, cluster, PM_ON); |
|||
} |
|||
|
|||
void hisi_powerdn_cluster(unsigned int cluster, unsigned int core) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_HOTPLUG); |
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, |
|||
(0x10001 << core)); |
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, |
|||
BIT(core)); |
|||
} |
|||
|
|||
void hisi_enter_core_idle(unsigned int cluster, unsigned int core) |
|||
{ |
|||
hisi_pdc_powerdn_core(cluster, core, PDC_UNMASK_GIC_WAKE_IRQ, |
|||
PDC_DISABLE_FINISH_INT); |
|||
} |
|||
|
|||
void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core) |
|||
{ |
|||
void *pdc_base_addr = hisi_get_pdc_addr(cluster); |
|||
|
|||
hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_IDLE); |
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, |
|||
(0x10001 << core)); |
|||
mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, |
|||
BIT(core)); |
|||
} |
|||
|
|||
void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core) |
|||
{ |
|||
hisi_ipc_pm_suspend(core, cluster, 0x3); |
|||
} |
@ -0,0 +1,57 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef __HISI_PWRC_H__ |
|||
#define __HISI_PWRC_H__ |
|||
|
|||
#include <hi3660.h> |
|||
#include <hi3660_crg.h> |
|||
|
|||
#define PCTRL_BASE (PCTRL_REG_BASE) |
|||
#define CRG_BASE (CRG_REG_BASE) |
|||
|
|||
#define SOC_CRGPERIPH_A53_PDCEN_ADDR(base) ((base) + (0x260)) |
|||
#define SOC_CRGPERIPH_MAIA_PDCEN_ADDR(base) ((base) + (0x300)) |
|||
|
|||
#define SOC_PCTRL_RESOURCE0_LOCK_ADDR(base) ((base) + (0x400)) |
|||
#define SOC_PCTRL_RESOURCE0_UNLOCK_ADDR(base) ((base) + (0x404)) |
|||
#define SOC_PCTRL_RESOURCE0_LOCK_ST_ADDR(base) ((base) + (0x408)) |
|||
#define SOC_PCTRL_RESOURCE1_LOCK_ADDR(base) ((base) + (0x40C)) |
|||
#define SOC_PCTRL_RESOURCE1_UNLOCK_ADDR(base) ((base) + (0x410)) |
|||
#define SOC_PCTRL_RESOURCE1_LOCK_ST_ADDR(base) ((base) + (0x414)) |
|||
#define SOC_PCTRL_RESOURCE2_LOCK_ADDR(base) ((base) + (0x418)) |
|||
|
|||
#define SOC_SCTRL_SCBAKDATA3_ADDR(base) ((base) + (0x418)) |
|||
#define SOC_SCTRL_SCBAKDATA8_ADDR(base) ((base) + (0x42C)) |
|||
#define SOC_SCTRL_SCBAKDATA9_ADDR(base) ((base) + (0x430)) |
|||
|
|||
#define SOC_ACPU_SCTRL_BASE_ADDR (0xFFF0A000) |
|||
|
|||
void hisi_cpuidle_lock(unsigned int cluster, unsigned int core); |
|||
void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core); |
|||
void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core); |
|||
void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core); |
|||
void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core); |
|||
void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core); |
|||
int cluster_is_powered_on(unsigned int cluster); |
|||
void hisi_enter_core_idle(unsigned int cluster, unsigned int core); |
|||
void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core); |
|||
int hisi_test_ap_suspend_flag(unsigned int cluster); |
|||
void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core); |
|||
|
|||
|
|||
/* pdc api */ |
|||
void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster); |
|||
int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core); |
|||
void hisi_disable_pdc(unsigned int cluster); |
|||
void hisi_enable_pdc(unsigned int cluster); |
|||
void hisi_powerup_core(unsigned int cluster, unsigned int core); |
|||
void hisi_powerdn_core(unsigned int cluster, unsigned int core); |
|||
void hisi_powerup_cluster(unsigned int cluster, unsigned int core); |
|||
void hisi_powerdn_cluster(unsigned int cluster, unsigned int core); |
|||
unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core); |
|||
|
|||
#endif /* __HISI_PWRC_H__ */ |
@ -0,0 +1,166 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
#include <assert.h> |
|||
#include <debug.h> |
|||
#include <errno.h> |
|||
#include <hi3660_mailbox.h> |
|||
#include <mailbox.h> |
|||
#include <mmio.h> |
|||
#include <string.h> |
|||
|
|||
typedef struct hi3660_chan { |
|||
unsigned char src; |
|||
unsigned char dst; |
|||
unsigned char used; |
|||
} hi3660_chan_t; |
|||
|
|||
static hi3660_chan_t chan_map[MBX_MAX_CHANNELS]; |
|||
|
|||
static void hi3660_mbox_check_state(int chan, unsigned int state) |
|||
{ |
|||
unsigned int data; |
|||
|
|||
data = mmio_read_32(MBX_MODE(chan)); |
|||
assert((data & (MBX_MODE_AUTO_ANSWER | MBX_MODE_AUTO_LINK)) == 0); |
|||
|
|||
data &= MBX_MODE_STATE_STATUS_MASK; |
|||
assert(data == state); |
|||
(void)state; |
|||
} |
|||
|
|||
static int hi3660_mbox_send(int chan, void *message, int len) |
|||
{ |
|||
int i; |
|||
unsigned int *buf; |
|||
unsigned int data; |
|||
|
|||
assert((chan >= 0) && (chan < MBX_MAX_CHANNELS) && |
|||
(message != NULL) && (len <= MBX_MAX_DATA_LEN)); |
|||
assert((chan_map[chan].used != 0) && |
|||
(chan_map[chan].src != 0) && |
|||
(chan_map[chan].dst != 0)); |
|||
|
|||
buf = (unsigned int *)message; |
|||
len = ((len + 3) >> 2); /* convert to word count */ |
|||
for (i = 0; i < len; i++) |
|||
mmio_write_32(MBX_DATA0(chan) + (i << 2), *(buf + i)); |
|||
/* send out */ |
|||
mmio_write_32(MBX_SEND(chan), chan_map[chan].src); |
|||
|
|||
do { |
|||
data = mmio_read_32(MBX_ICLR(chan)); |
|||
} while ((data & chan_map[chan].src) == 0); |
|||
/* ack */ |
|||
mmio_write_32(MBX_ICLR(chan), chan_map[chan].src); |
|||
return 0; |
|||
} |
|||
|
|||
static int hi3660_mbox_recv(int chan, void *message, int *len) |
|||
{ |
|||
unsigned int *buf, data; |
|||
int i; |
|||
|
|||
assert((chan >= 0) && (chan < MBX_MAX_CHANNELS) && |
|||
(message != NULL) && (len != NULL)); |
|||
assert((chan_map[chan].used != 0) && |
|||
(chan_map[chan].src != 0) && |
|||
(chan_map[chan].dst != 0)); |
|||
/* wait IPC event */ |
|||
do { |
|||
data = mmio_read_32(MBX_MODE(chan)); |
|||
} while ((data & MBX_MODE_STATE_STATUS_MASK) != MBX_MODE_STATE_DEST); |
|||
/* wait to clear interrupt */ |
|||
do { |
|||
data = mmio_read_32(MBX_ICLR(chan)); |
|||
} while (data == 0); |
|||
do { |
|||
mmio_write_32(MBX_ICLR(chan), chan_map[chan].dst); |
|||
data = mmio_read_32(MBX_ICLR(chan)); |
|||
} while (data); |
|||
|
|||
/* read data from IPC */ |
|||
buf = (unsigned int *)message; |
|||
for (i = 0; i < MBX_MAX_DATA_LEN; i += 4) |
|||
*(buf + (i >> 2)) = mmio_read_32(MBX_DATA0(chan) + i); |
|||
*len = MBX_MAX_DATA_LEN; |
|||
/* ack */ |
|||
mmio_write_32(MBX_SEND(chan), chan_map[chan].dst); |
|||
return 0; |
|||
} |
|||
|
|||
static int hi3660_mbox_request(int chan, int direction) |
|||
{ |
|||
unsigned int data; |
|||
unsigned int src, dst; |
|||
|
|||
assert((chan >= 0) && (chan < MBX_MAX_CHANNELS)); |
|||
|
|||
if (direction == MAILBOX_DIR_TX) { |
|||
src = CPU_A53; |
|||
dst = CPU_LPM3; |
|||
} else if (direction == MAILBOX_DIR_RX) { |
|||
src = CPU_LPM3; |
|||
dst = CPU_A53; |
|||
} else |
|||
assert(0); |
|||
mmio_write_32(MBX_SOURCE(chan), src); |
|||
data = mmio_read_32(MBX_SOURCE(chan)); |
|||
assert(data == src); |
|||
|
|||
/* mask all interrupts */ |
|||
mmio_write_32(MBX_IMASK(chan), CPU_MASK); |
|||
/* unmask interrupt */ |
|||
mmio_write_32(MBX_IMASK(chan), ~(src | dst)); |
|||
|
|||
/* set destination */ |
|||
mmio_write_32(MBX_DCLEAR(chan), (~dst) & CPU_MASK); |
|||
mmio_write_32(MBX_DSET(chan), dst); |
|||
data = mmio_read_32(MBX_DSTATUS(chan)); |
|||
assert((data & dst) != 0); |
|||
|
|||
/* clear auto link & auto answer */ |
|||
data = mmio_read_32(MBX_MODE(chan)); |
|||
data &= ~(MBX_MODE_AUTO_ANSWER | MBX_MODE_AUTO_LINK); |
|||
mmio_write_32(MBX_MODE(chan), data); |
|||
|
|||
hi3660_mbox_check_state(chan, MBX_MODE_STATE_SOURCE); |
|||
chan_map[chan].used = 1; |
|||
chan_map[chan].src = src; |
|||
chan_map[chan].dst = dst; |
|||
return 0; |
|||
} |
|||
|
|||
static void hi3660_mbox_free(int chan) |
|||
{ |
|||
assert((chan >= 0) && (chan < MBX_MAX_CHANNELS)); |
|||
} |
|||
|
|||
static mbox_ops_t hi3660_mbox_ops = { |
|||
.send = hi3660_mbox_send, |
|||
.recv = hi3660_mbox_recv, |
|||
.request = hi3660_mbox_request, |
|||
.free = hi3660_mbox_free, |
|||
}; |
|||
|
|||
int hi3660_mbox_init(mbox_params_t *params) |
|||
{ |
|||
int result; |
|||
unsigned int data; |
|||
|
|||
assert(params != NULL); |
|||
result = mbox_init(&hi3660_mbox_ops, params); |
|||
assert(result == 0); |
|||
memset(&chan_map, 0, sizeof(chan_map)); |
|||
|
|||
/* unlock mailbox */ |
|||
data = mmio_read_32(IPC_LOCK); |
|||
while (data == MBX_IPC_LOCKED) { |
|||
mmio_write_32(IPC_LOCK, MBX_IPC_UNLOCK_MAGIC); |
|||
data = mmio_read_32(IPC_LOCK); |
|||
} |
|||
(void)result; |
|||
return 0; |
|||
} |
@ -0,0 +1,129 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <arm_gic.h> |
|||
#include <assert.h> |
|||
#include <bl_common.h> |
|||
#include <cci.h> |
|||
#include <console.h> |
|||
#include <debug.h> |
|||
#include <errno.h> |
|||
#include <generic_delay_timer.h> |
|||
#include <gicv2.h> |
|||
#include <hi3660.h> |
|||
#include <hisi_ipc.h> |
|||
#include <platform_def.h> |
|||
|
|||
#include "hikey960_def.h" |
|||
#include "hikey960_private.h" |
|||
|
|||
/*
|
|||
* The next 2 constants identify the extents of the code & RO data region. |
|||
* These addresses are used by the MMU setup code and therefore they must be |
|||
* page-aligned. It is the responsibility of the linker script to ensure that |
|||
* __RO_START__ and __RO_END__ linker symbols refer to page-aligned addresses. |
|||
*/ |
|||
#define BL31_RO_BASE (unsigned long)(&__RO_START__) |
|||
#define BL31_RO_LIMIT (unsigned long)(&__RO_END__) |
|||
|
|||
/*
|
|||
* The next 2 constants identify the extents of the coherent memory region. |
|||
* These addresses are used by the MMU setup code and therefore they must be |
|||
* page-aligned. It is the responsibility of the linker script to ensure that |
|||
* __COHERENT_RAM_START__ and __COHERENT_RAM_END__ linker symbols refer to |
|||
* page-aligned addresses. |
|||
*/ |
|||
#define BL31_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__) |
|||
#define BL31_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__) |
|||
|
|||
static entry_point_info_t bl32_ep_info; |
|||
static entry_point_info_t bl33_ep_info; |
|||
|
|||
/******************************************************************************
|
|||
* On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 |
|||
* interrupts. |
|||
*****************************************************************************/ |
|||
const unsigned int g0_interrupt_array[] = { |
|||
IRQ_SEC_PHY_TIMER, |
|||
IRQ_SEC_SGI_0 |
|||
}; |
|||
|
|||
const gicv2_driver_data_t hikey960_gic_data = { |
|||
.gicd_base = GICD_REG_BASE, |
|||
.gicc_base = GICC_REG_BASE, |
|||
.g0_interrupt_num = ARRAY_SIZE(g0_interrupt_array), |
|||
.g0_interrupt_array = g0_interrupt_array, |
|||
}; |
|||
|
|||
static const int cci_map[] = { |
|||
CCI400_SL_IFACE3_CLUSTER_IX, |
|||
CCI400_SL_IFACE4_CLUSTER_IX |
|||
}; |
|||
|
|||
entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) |
|||
{ |
|||
entry_point_info_t *next_image_info; |
|||
|
|||
next_image_info = (type == NON_SECURE) ? &bl33_ep_info : &bl32_ep_info; |
|||
|
|||
/* None of the images on this platform can have 0x0 as the entrypoint */ |
|||
if (next_image_info->pc) |
|||
return next_image_info; |
|||
return NULL; |
|||
} |
|||
|
|||
void bl31_early_platform_setup(bl31_params_t *from_bl2, |
|||
void *plat_params_from_bl2) |
|||
{ |
|||
unsigned int id, uart_base; |
|||
|
|||
generic_delay_timer_init(); |
|||
hikey960_read_boardid(&id); |
|||
if (id == 5300) |
|||
uart_base = PL011_UART5_BASE; |
|||
else |
|||
uart_base = PL011_UART6_BASE; |
|||
|
|||
/* Initialize the console to provide early debug support */ |
|||
console_init(uart_base, PL011_UART_CLK_IN_HZ, PL011_BAUDRATE); |
|||
|
|||
/* Initialize CCI driver */ |
|||
cci_init(CCI400_REG_BASE, cci_map, ARRAY_SIZE(cci_map)); |
|||
cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); |
|||
|
|||
/*
|
|||
* Copy BL3-2 and BL3-3 entry point information. |
|||
* They are stored in Secure RAM, in BL2's address space. |
|||
*/ |
|||
bl32_ep_info = *from_bl2->bl32_ep_info; |
|||
bl33_ep_info = *from_bl2->bl33_ep_info; |
|||
} |
|||
|
|||
void bl31_plat_arch_setup(void) |
|||
{ |
|||
hikey960_init_mmu_el3(BL31_BASE, |
|||
BL31_LIMIT - BL31_BASE, |
|||
BL31_RO_BASE, |
|||
BL31_RO_LIMIT, |
|||
BL31_COHERENT_RAM_BASE, |
|||
BL31_COHERENT_RAM_LIMIT); |
|||
} |
|||
|
|||
void bl31_platform_setup(void) |
|||
{ |
|||
/* Initialize the GIC driver, cpu and distributor interfaces */ |
|||
gicv2_driver_init(&hikey960_gic_data); |
|||
gicv2_distif_init(); |
|||
gicv2_pcpu_distif_init(); |
|||
gicv2_cpuif_enable(); |
|||
|
|||
hisi_ipc_init(); |
|||
} |
|||
|
|||
void bl31_plat_runtime_setup(void) |
|||
{ |
|||
} |
@ -0,0 +1,305 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <assert.h> |
|||
#include <cci.h> |
|||
#include <console.h> |
|||
#include <debug.h> |
|||
#include <gicv2.h> |
|||
#include <hi3660.h> |
|||
#include <hi3660_crg.h> |
|||
#include <mmio.h> |
|||
#include <psci.h> |
|||
#include "drivers/pwrc/hisi_pwrc.h" |
|||
|
|||
#include "hikey960_def.h" |
|||
#include "hikey960_private.h" |
|||
|
|||
#define CORE_PWR_STATE(state) \ |
|||
((state)->pwr_domain_state[MPIDR_AFFLVL0]) |
|||
#define CLUSTER_PWR_STATE(state) \ |
|||
((state)->pwr_domain_state[MPIDR_AFFLVL1]) |
|||
#define SYSTEM_PWR_STATE(state) \ |
|||
((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) |
|||
|
|||
#define DMAC_GLB_REG_SEC 0x694 |
|||
#define AXI_CONF_BASE 0x820 |
|||
|
|||
static uintptr_t hikey960_sec_entrypoint; |
|||
|
|||
static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state) |
|||
{ |
|||
unsigned long scr; |
|||
unsigned int val = 0; |
|||
|
|||
assert(cpu_state == PLAT_MAX_RET_STATE); |
|||
|
|||
scr = read_scr_el3(); |
|||
|
|||
/* Enable Physical IRQ and FIQ to wake the CPU*/ |
|||
write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); |
|||
|
|||
set_retention_ticks(val); |
|||
wfi(); |
|||
clr_retention_ticks(val); |
|||
|
|||
/*
|
|||
* Restore SCR to the original value, synchronisazion of |
|||
* scr_el3 is done by eret while el3_exit to save some |
|||
* execution cycles. |
|||
*/ |
|||
write_scr_el3(scr); |
|||
} |
|||
|
|||
static int hikey960_pwr_domain_on(u_register_t mpidr) |
|||
{ |
|||
unsigned int core = mpidr & MPIDR_CPU_MASK; |
|||
unsigned int cluster = |
|||
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; |
|||
int cluster_stat = cluster_is_powered_on(cluster); |
|||
|
|||
hisi_set_cpu_boot_flag(cluster, core); |
|||
|
|||
mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core), |
|||
hikey960_sec_entrypoint >> 2); |
|||
|
|||
if (cluster_stat) |
|||
hisi_powerup_core(cluster, core); |
|||
else |
|||
hisi_powerup_cluster(cluster, core); |
|||
|
|||
return PSCI_E_SUCCESS; |
|||
} |
|||
|
|||
static void |
|||
hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state) |
|||
{ |
|||
if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) |
|||
cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); |
|||
|
|||
gicv2_pcpu_distif_init(); |
|||
gicv2_cpuif_enable(); |
|||
} |
|||
|
|||
void hikey960_pwr_domain_off(const psci_power_state_t *target_state) |
|||
{ |
|||
unsigned long mpidr = read_mpidr_el1(); |
|||
unsigned int core = mpidr & MPIDR_CPU_MASK; |
|||
unsigned int cluster = |
|||
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; |
|||
|
|||
clr_ex(); |
|||
isb(); |
|||
dsbsy(); |
|||
|
|||
gicv2_cpuif_disable(); |
|||
|
|||
hisi_clear_cpu_boot_flag(cluster, core); |
|||
hisi_powerdn_core(cluster, core); |
|||
|
|||
/* check if any core is powered up */ |
|||
if (hisi_test_pwrdn_allcores(cluster, core)) { |
|||
|
|||
cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); |
|||
|
|||
isb(); |
|||
dsbsy(); |
|||
|
|||
hisi_powerdn_cluster(cluster, core); |
|||
} |
|||
} |
|||
|
|||
static void __dead2 hikey960_system_reset(void) |
|||
{ |
|||
mmio_write_32(SCTRL_SCPEREN1_REG, |
|||
SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS); |
|||
mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef); |
|||
panic(); |
|||
} |
|||
|
|||
int hikey960_validate_power_state(unsigned int power_state, |
|||
psci_power_state_t *req_state) |
|||
{ |
|||
int pstate = psci_get_pstate_type(power_state); |
|||
int pwr_lvl = psci_get_pstate_pwrlvl(power_state); |
|||
int i; |
|||
|
|||
assert(req_state); |
|||
|
|||
if (pwr_lvl > PLAT_MAX_PWR_LVL) |
|||
return PSCI_E_INVALID_PARAMS; |
|||
|
|||
/* Sanity check the requested state */ |
|||
if (pstate == PSTATE_TYPE_STANDBY) { |
|||
/*
|
|||
* It's possible to enter standby only on power level 0 |
|||
* Ignore any other power level. |
|||
*/ |
|||
if (pwr_lvl != MPIDR_AFFLVL0) |
|||
return PSCI_E_INVALID_PARAMS; |
|||
|
|||
req_state->pwr_domain_state[MPIDR_AFFLVL0] = |
|||
PLAT_MAX_RET_STATE; |
|||
} else { |
|||
for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) |
|||
req_state->pwr_domain_state[i] = |
|||
PLAT_MAX_OFF_STATE; |
|||
} |
|||
|
|||
/*
|
|||
* We expect the 'state id' to be zero. |
|||
*/ |
|||
if (psci_get_pstate_id(power_state)) |
|||
return PSCI_E_INVALID_PARAMS; |
|||
|
|||
return PSCI_E_SUCCESS; |
|||
} |
|||
|
|||
static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint) |
|||
{ |
|||
/*
|
|||
* Check if the non secure entrypoint lies within the non |
|||
* secure DRAM. |
|||
*/ |
|||
if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE))) |
|||
return PSCI_E_SUCCESS; |
|||
|
|||
return PSCI_E_INVALID_ADDRESS; |
|||
} |
|||
|
|||
static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state) |
|||
{ |
|||
u_register_t mpidr = read_mpidr_el1(); |
|||
unsigned int core = mpidr & MPIDR_CPU_MASK; |
|||
unsigned int cluster = |
|||
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; |
|||
|
|||
if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) |
|||
return; |
|||
|
|||
if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { |
|||
clr_ex(); |
|||
isb(); |
|||
dsbsy(); |
|||
|
|||
gicv2_cpuif_disable(); |
|||
|
|||
hisi_cpuidle_lock(cluster, core); |
|||
hisi_set_cpuidle_flag(cluster, core); |
|||
hisi_cpuidle_unlock(cluster, core); |
|||
|
|||
mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core), |
|||
hikey960_sec_entrypoint >> 2); |
|||
|
|||
hisi_enter_core_idle(cluster, core); |
|||
} |
|||
|
|||
/* Perform the common cluster specific operations */ |
|||
if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { |
|||
hisi_cpuidle_lock(cluster, core); |
|||
hisi_disable_pdc(cluster); |
|||
|
|||
/* check if any core is powered up */ |
|||
if (hisi_test_pwrdn_allcores(cluster, core)) { |
|||
|
|||
cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); |
|||
|
|||
isb(); |
|||
dsbsy(); |
|||
|
|||
/* mask the pdc wakeup irq, then
|
|||
* enable pdc to power down the core |
|||
*/ |
|||
hisi_pdc_mask_cluster_wakeirq(cluster); |
|||
hisi_enable_pdc(cluster); |
|||
|
|||
hisi_cpuidle_unlock(cluster, core); |
|||
|
|||
/* check the SR flag bit to determine
|
|||
* CLUSTER_IDLE_IPC or AP_SR_IPC to send |
|||
*/ |
|||
if (hisi_test_ap_suspend_flag(cluster)) |
|||
hisi_enter_ap_suspend(cluster, core); |
|||
else |
|||
hisi_enter_cluster_idle(cluster, core); |
|||
} else { |
|||
/* enable pdc */ |
|||
hisi_enable_pdc(cluster); |
|||
hisi_cpuidle_unlock(cluster, core); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void hikey960_sr_dma_reinit(void) |
|||
{ |
|||
unsigned int ctr = 0; |
|||
|
|||
mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3); |
|||
|
|||
/* 1~15 channel is set non_secure */ |
|||
for (ctr = 1; ctr <= 15; ctr++) |
|||
mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40), |
|||
(1 << 6) | (1 << 18)); |
|||
} |
|||
|
|||
static void |
|||
hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state) |
|||
{ |
|||
unsigned long mpidr = read_mpidr_el1(); |
|||
unsigned int cluster = |
|||
(mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; |
|||
|
|||
/* Nothing to be done on waking up from retention from CPU level */ |
|||
if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) |
|||
return; |
|||
|
|||
if (hisi_test_ap_suspend_flag(cluster)) { |
|||
hikey960_sr_dma_reinit(); |
|||
gicv2_cpuif_enable(); |
|||
console_init(PL011_UART6_BASE, PL011_UART_CLK_IN_HZ, |
|||
PL011_BAUDRATE); |
|||
} |
|||
|
|||
hikey960_pwr_domain_on_finish(target_state); |
|||
} |
|||
|
|||
static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state) |
|||
{ |
|||
int i; |
|||
|
|||
for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) |
|||
req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; |
|||
} |
|||
|
|||
static const plat_psci_ops_t hikey960_psci_ops = { |
|||
.cpu_standby = hikey960_pwr_domain_standby, |
|||
.pwr_domain_on = hikey960_pwr_domain_on, |
|||
.pwr_domain_on_finish = hikey960_pwr_domain_on_finish, |
|||
.pwr_domain_off = hikey960_pwr_domain_off, |
|||
.pwr_domain_suspend = hikey960_pwr_domain_suspend, |
|||
.pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish, |
|||
.system_off = NULL, |
|||
.system_reset = hikey960_system_reset, |
|||
.validate_power_state = hikey960_validate_power_state, |
|||
.validate_ns_entrypoint = hikey960_validate_ns_entrypoint, |
|||
.get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state, |
|||
}; |
|||
|
|||
int plat_setup_psci_ops(uintptr_t sec_entrypoint, |
|||
const plat_psci_ops_t **psci_ops) |
|||
{ |
|||
hikey960_sec_entrypoint = sec_entrypoint; |
|||
|
|||
INFO("%s: sec_entrypoint=0x%lx\n", __func__, |
|||
(unsigned long)hikey960_sec_entrypoint); |
|||
|
|||
/*
|
|||
* Initialize PSCI ops struct |
|||
*/ |
|||
*psci_ops = &hikey960_psci_ops; |
|||
return 0; |
|||
} |
@ -0,0 +1,64 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
#include <arch.h> |
|||
#include <platform_def.h> |
|||
#include <psci.h> |
|||
|
|||
/*
|
|||
* The HiKey power domain tree descriptor. The cluster power domains |
|||
* are arranged so that when the PSCI generic code creates the power |
|||
* domain tree, the indices of the CPU power domain nodes it allocates |
|||
* match the linear indices returned by plat_core_pos_by_mpidr(). |
|||
*/ |
|||
const unsigned char hikey960_power_domain_tree_desc[] = { |
|||
/* Number of root nodes */ |
|||
1, |
|||
/* Number of clusters */ |
|||
PLATFORM_CLUSTER_COUNT, |
|||
/* Number of children for the first cluster node */ |
|||
PLATFORM_CORE_COUNT_PER_CLUSTER, |
|||
/* Number of children for the second cluster node */ |
|||
PLATFORM_CORE_COUNT_PER_CLUSTER, |
|||
}; |
|||
|
|||
/*******************************************************************************
|
|||
* This function returns the HiKey topology tree information. |
|||
******************************************************************************/ |
|||
const unsigned char *plat_get_power_domain_tree_desc(void) |
|||
{ |
|||
return hikey960_power_domain_tree_desc; |
|||
} |
|||
|
|||
/*******************************************************************************
|
|||
* This function implements a part of the critical interface between the psci |
|||
* generic layer and the platform that allows the former to query the platform |
|||
* to convert an MPIDR to a unique linear index. An error code (-1) is returned |
|||
* in case the MPIDR is invalid. |
|||
******************************************************************************/ |
|||
int plat_core_pos_by_mpidr(u_register_t mpidr) |
|||
{ |
|||
unsigned int cluster_id, cpu_id; |
|||
|
|||
mpidr &= MPIDR_AFFINITY_MASK; |
|||
|
|||
if (mpidr & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) |
|||
return -1; |
|||
|
|||
cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; |
|||
cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; |
|||
|
|||
if (cluster_id >= PLATFORM_CLUSTER_COUNT) |
|||
return -1; |
|||
|
|||
/*
|
|||
* Validate cpu_id by checking whether it represents a CPU in |
|||
* one of the two clusters present on the platform. |
|||
*/ |
|||
if (cpu_id >= PLATFORM_CORE_COUNT_PER_CLUSTER) |
|||
return -1; |
|||
|
|||
return (cpu_id + (cluster_id * 4)); |
|||
} |
@ -0,0 +1,24 @@ |
|||
/*
|
|||
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef __HISI_IPC_H__ |
|||
#define __HISI_IPC_H__ |
|||
|
|||
enum pm_mode { |
|||
PM_ON = 0, |
|||
PM_OFF, |
|||
}; |
|||
|
|||
void hisi_ipc_pm_on_off(unsigned int core, unsigned int cluster, |
|||
enum pm_mode mode); |
|||
void hisi_ipc_pm_suspend(unsigned int core, unsigned int cluster, |
|||
unsigned int affinity_level); |
|||
void hisi_ipc_psci_system_off(unsigned int core, unsigned int cluster); |
|||
void hisi_ipc_psci_system_reset(unsigned int core, unsigned int cluster, |
|||
unsigned int cmd_id); |
|||
int hisi_ipc_init(void); |
|||
|
|||
#endif /* __HISI_IPC_H__ */ |
Loading…
Reference in new issue