synquacer: Enable PSCI framework
authorSumit Garg <sumit.garg@linaro.org>
Fri, 15 Jun 2018 09:59:02 +0000 (15:29 +0530)
committerSumit Garg <sumit.garg@linaro.org>
Thu, 21 Jun 2018 06:02:54 +0000 (11:32 +0530)
PSCI framework uses SCPI driver to communicate to SCP firmware for
various power management operations. Following PSCI operations are
supported:
- CPU ON
- CPU OFF
- CPU STANDBY
- SYSTEM RESET
- SYSTEM OFF

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
plat/socionext/synquacer/include/platform_def.h
plat/socionext/synquacer/sq_psci.c [new file with mode: 0644]

index e339d855fe0ab6b42467f2c8628e944e6d82e3fc..3e1664223a1e4cc59da99eccee0bfa553abdd4e1 100644 (file)
 #define PLATFORM_CORE_COUNT            (PLAT_CLUSTER_COUNT *   \
                                         PLAT_MAX_CORES_PER_CLUSTER)
 
+#define PLAT_MAX_PWR_LVL               1
+#define PLAT_MAX_RET_STATE             1
+#define PLAT_MAX_OFF_STATE             2
+
+#define SQ_LOCAL_STATE_RUN             0
+#define SQ_LOCAL_STATE_RET             1
+#define SQ_LOCAL_STATE_OFF             2
+
 #define CACHE_WRITEBACK_SHIFT          6
 #define CACHE_WRITEBACK_GRANULE                (1 << CACHE_WRITEBACK_SHIFT)
 
@@ -68,4 +76,6 @@
 #define PLAT_SQ_GICD_BASE              0x30000000
 #define PLAT_SQ_GICR_BASE              0x30400000
 
+#define PLAT_SQ_GPIO_BASE              0x51000000
+
 #endif /* __PLATFORM_DEF_H__ */
diff --git a/plat/socionext/synquacer/sq_psci.c b/plat/socionext/synquacer/sq_psci.c
new file mode 100644 (file)
index 0000000..c327f1d
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <cassert.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <generic_delay_timer.h>
+#include <platform_def.h>
+#include <sq_common.h>
+#include "sq_scpi.h"
+#include <psci.h>
+
+/* Macros to read the SQ power domain state */
+#define SQ_PWR_LVL0    MPIDR_AFFLVL0
+#define SQ_PWR_LVL1    MPIDR_AFFLVL1
+#define SQ_PWR_LVL2    MPIDR_AFFLVL2
+
+#define SQ_CORE_PWR_STATE(state)       (state)->pwr_domain_state[SQ_PWR_LVL0]
+#define SQ_CLUSTER_PWR_STATE(state)    (state)->pwr_domain_state[SQ_PWR_LVL1]
+#define SQ_SYSTEM_PWR_STATE(state)     ((PLAT_MAX_PWR_LVL > SQ_PWR_LVL1) ?\
+                               (state)->pwr_domain_state[SQ_PWR_LVL2] : 0)
+
+uintptr_t sq_sec_entrypoint;
+
+int sq_pwr_domain_on(u_register_t mpidr)
+{
+       /*
+        * SCP takes care of powering up parent power domains so we
+        * only need to care about level 0
+        */
+       scpi_set_sq_power_state(mpidr, scpi_power_on, scpi_power_on,
+                                scpi_power_on);
+
+       return PSCI_E_SUCCESS;
+}
+
+static void sq_pwr_domain_on_finisher_common(
+               const psci_power_state_t *target_state)
+{
+       assert(SQ_CORE_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF);
+
+       /*
+        * Perform the common cluster specific operations i.e enable coherency
+        * if this cluster was off.
+        */
+       if (SQ_CLUSTER_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF)
+               plat_sq_interconnect_enter_coherency();
+}
+
+void sq_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+       /* Assert that the system power domain need not be initialized */
+       assert(SQ_SYSTEM_PWR_STATE(target_state) == SQ_LOCAL_STATE_RUN);
+
+       sq_pwr_domain_on_finisher_common(target_state);
+
+       /* Program the gic per-cpu distributor or re-distributor interface */
+       sq_gic_pcpu_init();
+
+       /* Enable the gic cpu interface */
+       sq_gic_cpuif_enable();
+}
+
+static void sq_power_down_common(const psci_power_state_t *target_state)
+{
+       uint32_t cluster_state = scpi_power_on;
+       uint32_t system_state = scpi_power_on;
+
+       /* Prevent interrupts from spuriously waking up this cpu */
+       sq_gic_cpuif_disable();
+
+       /* Check if power down at system power domain level is requested */
+       if (SQ_SYSTEM_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF)
+               system_state = scpi_power_retention;
+
+       /* Cluster is to be turned off, so disable coherency */
+       if (SQ_CLUSTER_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF) {
+               plat_sq_interconnect_exit_coherency();
+               cluster_state = scpi_power_off;
+       }
+
+       /*
+        * Ask the SCP to power down the appropriate components depending upon
+        * their state.
+        */
+       scpi_set_sq_power_state(read_mpidr_el1(),
+                                scpi_power_off,
+                                cluster_state,
+                                system_state);
+}
+
+void sq_pwr_domain_off(const psci_power_state_t *target_state)
+{
+       sq_power_down_common(target_state);
+}
+
+void __dead2 sq_system_off(void)
+{
+       volatile uint32_t *gpio = (uint32_t *)PLAT_SQ_GPIO_BASE;
+
+       /* set PD[9] high to power off the system */
+       gpio[5] |= 0x2;         /* set output */
+       gpio[1] |= 0x2;         /* set high */
+       dmbst();
+
+       generic_delay_timer_init();
+
+       mdelay(1);
+
+       while (1) {
+               gpio[1] &= ~0x2;        /* set low */
+               dmbst();
+
+               mdelay(1);
+
+               gpio[1] |= 0x2;         /* set high */
+               dmbst();
+
+               mdelay(100);
+       }
+
+       wfi();
+       ERROR("SQ System Off: operation not handled.\n");
+       panic();
+}
+
+void __dead2 sq_system_reset(void)
+{
+       uint32_t response;
+
+       /* Send the system reset request to the SCP */
+       response = scpi_sys_power_state(scpi_system_reboot);
+
+       if (response != SCP_OK) {
+               ERROR("SQ System Reset: SCP error %u.\n", response);
+               panic();
+       }
+       wfi();
+       ERROR("SQ System Reset: operation not handled.\n");
+       panic();
+}
+
+void sq_cpu_standby(plat_local_state_t cpu_state)
+{
+       unsigned int scr;
+
+       assert(cpu_state == SQ_LOCAL_STATE_RET);
+
+       scr = read_scr_el3();
+       /* Enable PhysicalIRQ bit for NS world to wake the CPU */
+       write_scr_el3(scr | SCR_IRQ_BIT);
+       isb();
+       dsb();
+       wfi();
+
+       /*
+        * Restore SCR to the original value, synchronisation of scr_el3 is
+        * done by eret while el3_exit to save some execution cycles.
+        */
+       write_scr_el3(scr);
+}
+
+const plat_psci_ops_t sq_psci_ops = {
+       .pwr_domain_on          = sq_pwr_domain_on,
+       .pwr_domain_off         = sq_pwr_domain_off,
+       .pwr_domain_on_finish   = sq_pwr_domain_on_finish,
+       .cpu_standby            = sq_cpu_standby,
+       .system_off             = sq_system_off,
+       .system_reset           = sq_system_reset,
+};
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+                       const struct plat_psci_ops **psci_ops)
+{
+       sq_sec_entrypoint = sec_entrypoint;
+       flush_dcache_range((uint64_t)&sq_sec_entrypoint,
+                          sizeof(sq_sec_entrypoint));
+
+       *psci_ops = &sq_psci_ops;
+
+       return 0;
+}