1 /*
2  * Copyright (c) 2018, 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 <lib/mmio.h>
13 #include <lib/psci/psci.h>
14 
15 #include <gpc.h>
16 #include <imx8m_psci.h>
17 #include <plat_imx8.h>
18 
imx_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)19 int imx_validate_power_state(unsigned int power_state,
20 			 psci_power_state_t *req_state)
21 {
22 	int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
23 	int pwr_type = psci_get_pstate_type(power_state);
24 	int state_id = psci_get_pstate_id(power_state);
25 
26 	if (pwr_lvl > PLAT_MAX_PWR_LVL)
27 		return PSCI_E_INVALID_PARAMS;
28 
29 	if (pwr_type == PSTATE_TYPE_STANDBY) {
30 		CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
31 		CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
32 	}
33 
34 	if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) {
35 		CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE;
36 		CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
37 	}
38 
39 	return PSCI_E_SUCCESS;
40 }
41 
imx_domain_suspend(const psci_power_state_t * target_state)42 void imx_domain_suspend(const psci_power_state_t *target_state)
43 {
44 	uint64_t base_addr = BL31_BASE;
45 	uint64_t mpidr = read_mpidr_el1();
46 	unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
47 
48 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
49 		/* disable the cpu interface */
50 		plat_gic_cpuif_disable();
51 		imx_set_cpu_secure_entry(core_id, base_addr);
52 		imx_set_cpu_lpm(core_id, true);
53 	} else {
54 		dsb();
55 		write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
56 		isb();
57 	}
58 
59 	if (is_local_state_off(CLUSTER_PWR_STATE(target_state)))
60 		imx_set_cluster_powerdown(core_id, true);
61 	else
62 		imx_set_cluster_standby(true);
63 
64 	if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) {
65 		imx_set_sys_lpm(core_id, true);
66 	}
67 }
68 
imx_domain_suspend_finish(const psci_power_state_t * target_state)69 void imx_domain_suspend_finish(const psci_power_state_t *target_state)
70 {
71 	uint64_t mpidr = read_mpidr_el1();
72 	unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
73 
74 	/* check the system level status */
75 	if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) {
76 		imx_set_sys_lpm(core_id, false);
77 		imx_clear_rbc_count();
78 	}
79 
80 	/* check the cluster level power status */
81 	if (is_local_state_off(CLUSTER_PWR_STATE(target_state)))
82 		imx_set_cluster_powerdown(core_id, false);
83 	else
84 		imx_set_cluster_standby(false);
85 
86 	/* check the core level power status */
87 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
88 		/* clear the core lpm setting */
89 		imx_set_cpu_lpm(core_id, false);
90 		/* enable the gic cpu interface */
91 		plat_gic_cpuif_enable();
92 	} else {
93 		write_scr_el3(read_scr_el3() & (~0x4));
94 		isb();
95 	}
96 }
97 
imx_get_sys_suspend_power_state(psci_power_state_t * req_state)98 void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
99 {
100 	unsigned int i;
101 
102 	for (i = IMX_PWR_LVL0; i < PLAT_MAX_PWR_LVL; i++)
103 		req_state->pwr_domain_state[i] = PLAT_STOP_OFF_STATE;
104 
105 	req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE;
106 }
107 
108 static const plat_psci_ops_t imx_plat_psci_ops = {
109 	.pwr_domain_on = imx_pwr_domain_on,
110 	.pwr_domain_on_finish = imx_pwr_domain_on_finish,
111 	.pwr_domain_off = imx_pwr_domain_off,
112 	.validate_ns_entrypoint = imx_validate_ns_entrypoint,
113 	.validate_power_state = imx_validate_power_state,
114 	.cpu_standby = imx_cpu_standby,
115 	.pwr_domain_suspend = imx_domain_suspend,
116 	.pwr_domain_suspend_finish = imx_domain_suspend_finish,
117 	.pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi,
118 	.get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
119 	.system_reset = imx_system_reset,
120 	.system_reset2 = imx_system_reset2,
121 	.system_off = imx_system_off,
122 };
123 
124 /* export the platform specific psci ops */
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)125 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
126 			const plat_psci_ops_t **psci_ops)
127 {
128 	imx_mailbox_init(sec_entrypoint);
129 	/* sec_entrypoint is used for warm reset */
130 	*psci_ops = &imx_plat_psci_ops;
131 
132 	return 0;
133 }
134