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