/* * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include "sq_scpi.h" #include /* 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; }