diff options
| -rw-r--r-- | plat/hisilicon/hikey960/aarch64/hikey960_helpers.S | 53 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c | 204 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c | 395 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h | 57 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/hi3660_mailbox.c | 166 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/hikey960_bl31_setup.c | 129 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/hikey960_pm.c | 305 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/hikey960_private.h | 4 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/hikey960_topology.c | 64 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/include/hisi_ipc.h | 24 | ||||
| -rw-r--r-- | plat/hisilicon/hikey960/platform.mk | 13 | 
11 files changed, 1414 insertions, 0 deletions
| diff --git a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S index 5a0d8b04..2e24416d 100644 --- a/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S +++ b/plat/hisilicon/hikey960/aarch64/hikey960_helpers.S @@ -6,6 +6,8 @@  #include <arch.h>  #include <asm_macros.S> +#include <cortex_a53.h> +#include <cortex_a73.h>  #include "../hikey960_def.h"  	.globl	plat_my_core_pos @@ -14,6 +16,10 @@  	.globl	plat_crash_console_putc  	.globl	plat_report_exception  	.globl	plat_reset_handler +	.globl	set_retention_ticks +	.globl	clr_retention_ticks +	.globl	clr_ex +	.globl	nop  func plat_my_core_pos  	mrs	x0, mpidr_el1 @@ -132,6 +138,53 @@ func plat_reset_handler  	ret  endfunc plat_reset_handler +	/* ----------------------------------------------------- +	 * void set_retention_ticks(unsigned int val); +	 * Clobber list : x0 +	 * ----------------------------------------------------- +	 */ +func set_retention_ticks +	mrs	x0, CPUECTLR_EL1 +	bic	x0, x0, #CPUECTLR_CPU_RET_CTRL_MASK +	orr	x0, x0, #RETENTION_ENTRY_TICKS_8 +	msr	CPUECTLR_EL1, x0 +	isb +	dsb	sy +	ret +endfunc set_retention_ticks + +	/* ----------------------------------------------------- +	 * void clr_retention_ticks(unsigned int val); +	 * Clobber list : x0 +	 * ----------------------------------------------------- +	 */ +func clr_retention_ticks +	mrs	x0, CPUECTLR_EL1 +	bic	x0, x0, #CPUECTLR_CPU_RET_CTRL_MASK +	msr	CPUECTLR_EL1, x0 +	isb +	dsb	sy +	ret +endfunc clr_retention_ticks + +	/* ----------------------------------------------------- +	 * void clrex(void); +	 * ----------------------------------------------------- +	 */ +func clr_ex +	clrex +	ret +endfunc clr_ex + +	/* ----------------------------------------------------- +	 * void nop(void); +	 * ----------------------------------------------------- +	 */ +func nop +	nop +	ret +endfunc nop +  .section .rodata.rev_err_str, "aS"  plat_err_str:  	.asciz "\nPlatform exception reporting:" diff --git a/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c b/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c new file mode 100644 index 00000000..8ce1e4ff --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c @@ -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; +} diff --git a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c new file mode 100644 index 00000000..f82144a4 --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c @@ -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); +} diff --git a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h new file mode 100644 index 00000000..a4d887fb --- /dev/null +++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h @@ -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__ */ diff --git a/plat/hisilicon/hikey960/hi3660_mailbox.c b/plat/hisilicon/hikey960/hi3660_mailbox.c new file mode 100644 index 00000000..aa12932b --- /dev/null +++ b/plat/hisilicon/hikey960/hi3660_mailbox.c @@ -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; +} diff --git a/plat/hisilicon/hikey960/hikey960_bl31_setup.c b/plat/hisilicon/hikey960/hikey960_bl31_setup.c new file mode 100644 index 00000000..41c591b1 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_bl31_setup.c @@ -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) +{ +} diff --git a/plat/hisilicon/hikey960/hikey960_pm.c b/plat/hisilicon/hikey960/hikey960_pm.c new file mode 100644 index 00000000..257299e8 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_pm.c @@ -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; +} diff --git a/plat/hisilicon/hikey960/hikey960_private.h b/plat/hisilicon/hikey960/hikey960_private.h index 7e95f81d..8f2a842e 100644 --- a/plat/hisilicon/hikey960/hikey960_private.h +++ b/plat/hisilicon/hikey960/hikey960_private.h @@ -26,5 +26,9 @@ void hikey960_init_mmu_el3(unsigned long total_base,  			unsigned long coh_limit);  void hikey960_io_setup(void);  int hikey960_read_boardid(unsigned int *id); +void set_retention_ticks(unsigned int val); +void clr_retention_ticks(unsigned int val); +void clr_ex(void); +void nop(void);  #endif /* __HIKEY960_PRIVATE_H__ */ diff --git a/plat/hisilicon/hikey960/hikey960_topology.c b/plat/hisilicon/hikey960/hikey960_topology.c new file mode 100644 index 00000000..33637246 --- /dev/null +++ b/plat/hisilicon/hikey960/hikey960_topology.c @@ -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)); +} diff --git a/plat/hisilicon/hikey960/include/hisi_ipc.h b/plat/hisilicon/hikey960/include/hisi_ipc.h new file mode 100644 index 00000000..9dda1a57 --- /dev/null +++ b/plat/hisilicon/hikey960/include/hisi_ipc.h @@ -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__ */ diff --git a/plat/hisilicon/hikey960/platform.mk b/plat/hisilicon/hikey960/platform.mk index bb16dfea..145eee0e 100644 --- a/plat/hisilicon/hikey960/platform.mk +++ b/plat/hisilicon/hikey960/platform.mk @@ -50,3 +50,16 @@ BL2_SOURCES		+=	drivers/io/io_block.c			\  				plat/hisilicon/hikey960/hikey960_bl2_setup.c \  				plat/hisilicon/hikey960/hikey960_io_storage.c \  				plat/hisilicon/hikey960/hikey960_mcu_load.c + +BL31_SOURCES		+=	drivers/arm/cci/cci.c			\ +				lib/cpus/aarch64/cortex_a53.S           \ +				lib/cpus/aarch64/cortex_a72.S		\ +				lib/cpus/aarch64/cortex_a73.S		\ +				plat/common/aarch64/plat_psci_common.c  \ +				plat/hisilicon/hikey960/aarch64/hikey960_helpers.S \ +				plat/hisilicon/hikey960/hikey960_bl31_setup.c \ +				plat/hisilicon/hikey960/hikey960_pm.c	\ +				plat/hisilicon/hikey960/hikey960_topology.c \ +				plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c \ +				plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c \ +				${HIKEY960_GIC_SOURCES} | 
