1 /*
2 * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdbool.h>
8
9 #include <arch.h>
10 #include <arch_helpers.h>
11 #include <common/debug.h>
12 #include <drivers/delay_timer.h>
13 #include <lib/mmio.h>
14 #include <lib/psci/psci.h>
15
16 #include <dram.h>
17 #include <gpc.h>
18 #include <imx8m_psci.h>
19 #include <plat_imx8.h>
20
imx_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)21 int imx_validate_power_state(unsigned int power_state,
22 psci_power_state_t *req_state)
23 {
24 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
25 int pwr_type = psci_get_pstate_type(power_state);
26 int state_id = psci_get_pstate_id(power_state);
27
28 if (pwr_lvl > PLAT_MAX_PWR_LVL)
29 return PSCI_E_INVALID_PARAMS;
30
31 if (pwr_type == PSTATE_TYPE_STANDBY) {
32 CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
33 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
34 }
35
36 if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) {
37 CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE;
38 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
39 }
40
41 return PSCI_E_SUCCESS;
42 }
43
imx_pwr_domain_off(const psci_power_state_t * target_state)44 void imx_pwr_domain_off(const psci_power_state_t *target_state)
45 {
46 uint64_t mpidr = read_mpidr_el1();
47 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
48
49 plat_gic_cpuif_disable();
50 imx_set_cpu_pwr_off(core_id);
51
52 /*
53 * TODO: Find out why this is still
54 * needed in order not to break suspend
55 */
56 udelay(50);
57 }
58
imx_domain_suspend(const psci_power_state_t * target_state)59 void imx_domain_suspend(const psci_power_state_t *target_state)
60 {
61 uint64_t base_addr = BL31_START;
62 uint64_t mpidr = read_mpidr_el1();
63 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
64
65 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
66 /* disable the cpu interface */
67 plat_gic_cpuif_disable();
68 imx_set_cpu_secure_entry(core_id, base_addr);
69 imx_set_cpu_lpm(core_id, true);
70 } else {
71 dsb();
72 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
73 isb();
74 }
75
76 if (is_local_state_off(CLUSTER_PWR_STATE(target_state)))
77 imx_set_cluster_powerdown(core_id, CLUSTER_PWR_STATE(target_state));
78 else
79 imx_set_cluster_standby(true);
80
81 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) {
82 imx_set_sys_lpm(core_id, true);
83 dram_enter_retention();
84 imx_anamix_override(true);
85 }
86 }
87
imx_domain_suspend_finish(const psci_power_state_t * target_state)88 void imx_domain_suspend_finish(const psci_power_state_t *target_state)
89 {
90 uint64_t mpidr = read_mpidr_el1();
91 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
92
93 /* check the system level status */
94 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) {
95 imx_anamix_override(false);
96 dram_exit_retention();
97 imx_set_sys_lpm(core_id, false);
98 imx_clear_rbc_count();
99 }
100
101 /* check the cluster level power status */
102 if (is_local_state_off(CLUSTER_PWR_STATE(target_state)))
103 imx_set_cluster_powerdown(core_id, PSCI_LOCAL_STATE_RUN);
104 else
105 imx_set_cluster_standby(false);
106
107 /* check the core level power status */
108 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
109 /* mark this core as awake by masking IRQ0 */
110 imx_gpc_set_a53_core_awake(core_id);
111 /* clear the core lpm setting */
112 imx_set_cpu_lpm(core_id, false);
113 /* enable the gic cpu interface */
114 plat_gic_cpuif_enable();
115 } else {
116 write_scr_el3(read_scr_el3() & (~0x4));
117 isb();
118 }
119 }
120
imx_get_sys_suspend_power_state(psci_power_state_t * req_state)121 void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
122 {
123 unsigned int i;
124
125 for (i = IMX_PWR_LVL0; i < PLAT_MAX_PWR_LVL; i++)
126 req_state->pwr_domain_state[i] = PLAT_STOP_OFF_STATE;
127
128 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE;
129 }
130
131 static const plat_psci_ops_t imx_plat_psci_ops = {
132 .pwr_domain_on = imx_pwr_domain_on,
133 .pwr_domain_on_finish = imx_pwr_domain_on_finish,
134 .pwr_domain_off = imx_pwr_domain_off,
135 .validate_ns_entrypoint = imx_validate_ns_entrypoint,
136 .validate_power_state = imx_validate_power_state,
137 .cpu_standby = imx_cpu_standby,
138 .pwr_domain_suspend = imx_domain_suspend,
139 .pwr_domain_suspend_finish = imx_domain_suspend_finish,
140 .pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi,
141 .get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
142 .system_reset = imx_system_reset,
143 .system_reset2 = imx_system_reset2,
144 .system_off = imx_system_off,
145 };
146
147 /* export the platform specific psci ops */
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)148 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
149 const plat_psci_ops_t **psci_ops)
150 {
151 imx_mailbox_init(sec_entrypoint);
152 /* sec_entrypoint is used for warm reset */
153 *psci_ops = &imx_plat_psci_ops;
154
155 return 0;
156 }
157