blob: 3522ee0d476095be9e23d1e2c4173ab647852da8 [file] [log] [blame]
Jacky Baifcd41e82020-07-02 14:39:58 +08001/*
2 * Copyright 2021-2024 NXP
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/arm/gicv3.h>
13#include <lib/mmio.h>
14#include <lib/psci/psci.h>
15
16#include <plat_imx8.h>
Jacky Baidaa44782023-09-18 22:08:52 +080017#include <upower_api.h>
Jacky Baifcd41e82020-07-02 14:39:58 +080018
19static uintptr_t secure_entrypoint;
20
21#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
22#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
23#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
24
25#define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c))
26#define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c))
27#define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c))
28
Jacky Baidaa44782023-09-18 22:08:52 +080029#define PMIC_CFG(v, m, msk) \
30 { \
31 .volt = (v), \
32 .mode = (m), \
33 .mode_msk = (msk), \
34 }
35
36#define PAD_CFG(c, r, t) \
37 { \
38 .pad_close = (c), \
39 .pad_reset = (r), \
40 .pad_tqsleep = (t) \
41 }
42
43#define BIAS_CFG(m, n, p, mbias) \
44 { \
45 .dombias_cfg = { \
46 .mode = (m), \
47 .rbbn = (n), \
48 .rbbp = (p), \
49 }, \
50 .membias_cfg = {mbias}, \
51 }
52
53#define SWT_BOARD(swt_on, msk) \
54 { \
55 .on = (swt_on), \
56 .mask = (msk), \
57 }
58
59#define SWT_MEM(a, p, m) \
60 { \
61 .array = (a), \
62 .perif = (p), \
63 .mask = (m), \
64 }
65
Jacky Baifcd41e82020-07-02 14:39:58 +080066static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
67{
68 mmio_write_32(RVBARADDRx(cpu), entry);
69
70 /* set update bit */
71 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
72 /* wait for ack */
73 while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
74 }
75
76 /* clear update bit */
77 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
78 /* clear ack bit */
79 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
80
81 return 0;
82}
83
84int imx_pwr_domain_on(u_register_t mpidr)
85{
86 unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
87
88 imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
89
90 mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
91 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
92
93 /* enable wku wakeup for idle */
94 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
95
96 return PSCI_E_SUCCESS;
97}
98
99void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
100{
101 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
102 plat_gic_pcpu_init();
103 plat_gic_cpuif_enable();
104}
105
106int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
107{
108 return PSCI_E_SUCCESS;
109}
110
111void imx_pwr_domain_off(const psci_power_state_t *target_state)
112{
113 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
114
115 plat_gic_cpuif_disable();
116
117 /* disable wakeup */
118 mmio_write_32(WKPUx(cpu), 0);
119
Jacky Baidaa44782023-09-18 22:08:52 +0800120 /* set core power mode to PD */
Jacky Baifcd41e82020-07-02 14:39:58 +0800121 mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
122}
Jacky Baidaa44782023-09-18 22:08:52 +0800123/* APD power mode config */
124ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = {
125 [ADMA_PWR_MODE] = {
126 .swt_board_offs = 0x120,
127 .swt_mem_offs = 0x128,
128 .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
129 .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
130 .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
131 },
132
133 [ACT_PWR_MODE] = {
134 .swt_board_offs = 0x110,
135 .swt_mem_offs = 0x118,
136 .pmic_cfg = PMIC_CFG(0x23, 0x2, 0x2),
137 .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
138 .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
139 },
140};
141
142/* APD power switch config */
143ps_apd_swt_cfgs_t apd_swt_cfgs = {
144 [ADMA_PWR_MODE] = {
145 .swt_board[0] = SWT_BOARD(0x74, 0x7c),
146 .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd),
147 .swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0),
148 },
149
150 [ACT_PWR_MODE] = {
151 .swt_board[0] = SWT_BOARD(0x74, 0x7c),
152 .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd),
153 .swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0),
154 },
155};
156
157struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR;
158
159void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)
160{
161 if (mode >= NUM_PWR_MODES) {
162 return;
163 }
164
165 /* apd power mode config */
166 memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode],
167 sizeof(struct ps_apd_pwr_mode_cfg_t));
168
169 /* apd power switch config */
170 memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t));
171}
172
173void imx_domain_suspend(const psci_power_state_t *target_state)
174{
175 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
176
177 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
178 plat_gic_cpuif_disable();
179 imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
180 /* core put into power down */
181 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3);
182 /* FIXME config wakeup interrupt in WKPU */
183 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
184 } else {
185 /* for core standby/retention mode */
186 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1);
187 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
188 dsb();
189 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
190 isb();
191 }
192
193 if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) {
194 /* TODO imx_set_wakeup() based on GIC config*/
195
196 /*
197 * just for sleep mode for now, need to update to
198 * support more mode, same for suspend finish call back.
199 */
200 mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1);
201 mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1);
202 }
203
204 /* TODO, may need to add more system level config here */
205 if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
206 /*
207 * low power mode config info used by upower
208 * to do low power mode transition.
209 */
210 imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
211 imx_set_pwr_mode_cfg(ACT_PWR_MODE);
212 }
213}
214
215void imx_domain_suspend_finish(const psci_power_state_t *target_state)
216{
217 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
218
219 if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
220 /* TODO reverse setting for system level */
221 }
222
223 if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) {
224 mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0);
225 mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0);
226 }
227
228 /* clear core's LPM setting */
229 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0);
230 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0);
231
232 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
233 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
234 plat_gic_cpuif_enable();
235 } else {
236 dsb();
237 write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT));
238 isb();
239 }
240}
Jacky Baifcd41e82020-07-02 14:39:58 +0800241
242void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
243{
244 while (1) {
245 wfi();
246 }
247}
248
249void __dead2 imx8ulp_system_reset(void)
250{
251 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
252
253 /* Write invalid command to WDOG CNT to trigger reset */
254 mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
255
256 while (true) {
257 wfi();
258 }
259}
260
Jacky Baidaa44782023-09-18 22:08:52 +0800261int imx_validate_power_state(unsigned int power_state,
262 psci_power_state_t *req_state)
263{
264 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
265 int pwr_type = psci_get_pstate_type(power_state);
266
267 if (pwr_lvl > PLAT_MAX_PWR_LVL) {
268 return PSCI_E_INVALID_PARAMS;
269 }
270
271 if (pwr_type == PSTATE_TYPE_STANDBY) {
272 CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
273 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
274 }
275
276 /* No power down state support */
277 if (pwr_type == PSTATE_TYPE_POWERDOWN) {
278 return PSCI_E_INVALID_PARAMS;
279 }
280
281 return PSCI_E_SUCCESS;
282}
283
284void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
285{
286 unsigned int i;
287
288 for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
289 req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE;
290 }
291}
292
Jacky Baifcd41e82020-07-02 14:39:58 +0800293static const plat_psci_ops_t imx_plat_psci_ops = {
294 .pwr_domain_on = imx_pwr_domain_on,
295 .pwr_domain_on_finish = imx_pwr_domain_on_finish,
296 .validate_ns_entrypoint = imx_validate_ns_entrypoint,
297 .system_reset = imx8ulp_system_reset,
298 .pwr_domain_off = imx_pwr_domain_off,
Jacky Baidaa44782023-09-18 22:08:52 +0800299 .pwr_domain_suspend = imx_domain_suspend,
300 .pwr_domain_suspend_finish = imx_domain_suspend_finish,
301 .get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
302 .validate_power_state = imx_validate_power_state,
Jacky Baifcd41e82020-07-02 14:39:58 +0800303 .pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi,
304};
305
306int plat_setup_psci_ops(uintptr_t sec_entrypoint,
307 const plat_psci_ops_t **psci_ops)
308{
309 secure_entrypoint = sec_entrypoint;
310 imx_pwr_set_cpu_entry(0, sec_entrypoint);
311 *psci_ops = &imx_plat_psci_ops;
312
313 mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
314 mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
315
316 return 0;
317}