summaryrefslogtreecommitdiffstats
path: root/plat/hisilicon/hikey960/drivers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 17:43:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 17:43:51 +0000
commitbe58c81aff4cd4c0ccf43dbd7998da4a6a08c03b (patch)
tree779c248fb61c83f65d1f0dc867f2053d76b4e03a /plat/hisilicon/hikey960/drivers
parentInitial commit. (diff)
downloadarm-trusted-firmware-be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b.tar.xz
arm-trusted-firmware-be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b.zip
Adding upstream version 2.10.0+dfsg.upstream/2.10.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plat/hisilicon/hikey960/drivers')
-rw-r--r--plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c206
-rw-r--r--plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c417
-rw-r--r--plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.h57
3 files changed, 680 insertions, 0 deletions
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 0000000..a6a4949
--- /dev/null
+++ b/plat/hisilicon/hikey960/drivers/ipc/hisi_ipc.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.h>
+
+#include <hi3660.h>
+#include <hisi_ipc.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 0000000..91d8033
--- /dev/null
+++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.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 (0x7u << 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(void)
+{
+ unsigned int val1;
+ unsigned int val2;
+
+ val1 = mmio_read_32(CPUIDLE_FLAG_REG(0));
+ val1 &= AP_SUSPEND_FLAG;
+
+ val2 = mmio_read_32(CPUIDLE_FLAG_REG(1));
+ val2 &= AP_SUSPEND_FLAG;
+
+ val1 |= val2;
+ return (val1 != 0);
+}
+
+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 &= ~(0x3U << ((2 * cluster) + 28));
+ val |= (value << (2 * cluster));
+ 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;
+}
+
+static int check_hotplug(unsigned int cluster, unsigned int boot_flag)
+{
+ unsigned int mask = 0xF;
+
+ if (hisi_test_ap_suspend_flag() ||
+ ((boot_flag & mask) == mask))
+ return 0;
+
+ return 1;
+}
+
+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 ||
+ check_hotplug(cluster, boot_flag))
+ 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);
+}
+
+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 0000000..e0cb381
--- /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(void);
+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 */