blob: 32ecd49e6255456d0438ba97e036c6de5f222b02 [file] [log] [blame]
Jacky Bai4d93d1d2020-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 Baic6fb6672023-09-18 22:08:52 +080017#include <upower_api.h>
Jacky Bai4d93d1d2020-07-02 14:39:58 +080018
Jacky Baiba997cc2021-06-25 09:47:46 +080019extern void cgc1_save(void);
20extern void cgc1_restore(void);
21extern void imx_apd_ctx_save(unsigned int cpu);
22extern void imx_apd_ctx_restore(unsigned int cpu);
23extern void usb_wakeup_enable(bool enable);
24
Jacky Bai4d93d1d2020-07-02 14:39:58 +080025static uintptr_t secure_entrypoint;
26
27#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
28#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
29#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
30
31#define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c))
32#define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c))
33#define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c))
34
Jacky Baic6fb6672023-09-18 22:08:52 +080035#define PMIC_CFG(v, m, msk) \
36 { \
37 .volt = (v), \
38 .mode = (m), \
39 .mode_msk = (msk), \
40 }
41
42#define PAD_CFG(c, r, t) \
43 { \
44 .pad_close = (c), \
45 .pad_reset = (r), \
46 .pad_tqsleep = (t) \
47 }
48
49#define BIAS_CFG(m, n, p, mbias) \
50 { \
51 .dombias_cfg = { \
52 .mode = (m), \
53 .rbbn = (n), \
54 .rbbp = (p), \
55 }, \
56 .membias_cfg = {mbias}, \
57 }
58
59#define SWT_BOARD(swt_on, msk) \
60 { \
61 .on = (swt_on), \
62 .mask = (msk), \
63 }
64
65#define SWT_MEM(a, p, m) \
66 { \
67 .array = (a), \
68 .perif = (p), \
69 .mask = (m), \
70 }
71
Jacky Baiba997cc2021-06-25 09:47:46 +080072extern void upower_wait_resp(void);
73
Jacky Bai4d93d1d2020-07-02 14:39:58 +080074static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
75{
76 mmio_write_32(RVBARADDRx(cpu), entry);
77
78 /* set update bit */
79 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
80 /* wait for ack */
81 while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
82 }
83
84 /* clear update bit */
85 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
86 /* clear ack bit */
87 mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
88
89 return 0;
90}
91
92int imx_pwr_domain_on(u_register_t mpidr)
93{
94 unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
95
96 imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
97
98 mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
99 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
100
101 /* enable wku wakeup for idle */
102 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
103
104 return PSCI_E_SUCCESS;
105}
106
107void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
108{
109 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
110 plat_gic_pcpu_init();
111 plat_gic_cpuif_enable();
112}
113
114int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
115{
116 return PSCI_E_SUCCESS;
117}
118
119void imx_pwr_domain_off(const psci_power_state_t *target_state)
120{
121 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
122
123 plat_gic_cpuif_disable();
124
125 /* disable wakeup */
126 mmio_write_32(WKPUx(cpu), 0);
127
Jacky Baic6fb6672023-09-18 22:08:52 +0800128 /* set core power mode to PD */
Jacky Bai4d93d1d2020-07-02 14:39:58 +0800129 mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
130}
Jacky Baiba997cc2021-06-25 09:47:46 +0800131
Jacky Baic6fb6672023-09-18 22:08:52 +0800132/* APD power mode config */
133ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = {
Jacky Baiba997cc2021-06-25 09:47:46 +0800134 /* PD */
135 [PD_PWR_MODE] = {
136 .swt_board_offs = 0x170,
137 .swt_mem_offs = 0x178,
138 .pmic_cfg = PMIC_CFG(0x23, 0x2, 0x2),
139 .pad_cfg = PAD_CFG(0x0, 0x0, 0x01e80a00),
140 .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
141 },
142
Jacky Baic6fb6672023-09-18 22:08:52 +0800143 [ADMA_PWR_MODE] = {
144 .swt_board_offs = 0x120,
145 .swt_mem_offs = 0x128,
146 .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
147 .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
148 .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
149 },
150
151 [ACT_PWR_MODE] = {
152 .swt_board_offs = 0x110,
153 .swt_mem_offs = 0x118,
154 .pmic_cfg = PMIC_CFG(0x23, 0x2, 0x2),
155 .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
156 .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
157 },
158};
159
160/* APD power switch config */
161ps_apd_swt_cfgs_t apd_swt_cfgs = {
Jacky Baiba997cc2021-06-25 09:47:46 +0800162 [PD_PWR_MODE] = {
163 .swt_board[0] = SWT_BOARD(0x00060003, 0x00001e74),
164 .swt_mem[0] = SWT_MEM(0x00010c00, 0x0, 0x1ffff),
165 .swt_mem[1] = SWT_MEM(0x003fffff, 0x003f0000, 0x0),
166 },
167
Jacky Baic6fb6672023-09-18 22:08:52 +0800168 [ADMA_PWR_MODE] = {
Jacky Baiba997cc2021-06-25 09:47:46 +0800169 .swt_board[0] = SWT_BOARD(0x0006ff77, 0x0006ff7c),
170 .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
171 .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
Jacky Baic6fb6672023-09-18 22:08:52 +0800172 },
173
174 [ACT_PWR_MODE] = {
Jacky Baiba997cc2021-06-25 09:47:46 +0800175 .swt_board[0] = SWT_BOARD(0x0006ff77, 0x0000ff7c),
176 .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
177 .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
Jacky Baic6fb6672023-09-18 22:08:52 +0800178 },
179};
180
181struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR;
182
183void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)
184{
185 if (mode >= NUM_PWR_MODES) {
186 return;
187 }
188
189 /* apd power mode config */
190 memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode],
191 sizeof(struct ps_apd_pwr_mode_cfg_t));
192
193 /* apd power switch config */
194 memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t));
195}
196
197void imx_domain_suspend(const psci_power_state_t *target_state)
198{
199 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
200
201 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
202 plat_gic_cpuif_disable();
203 imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
204 /* core put into power down */
205 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3);
206 /* FIXME config wakeup interrupt in WKPU */
207 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
208 } else {
209 /* for core standby/retention mode */
210 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1);
211 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
212 dsb();
213 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
214 isb();
215 }
216
Jacky Baiba997cc2021-06-25 09:47:46 +0800217 if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) {
Jacky Baic6fb6672023-09-18 22:08:52 +0800218 /*
219 * just for sleep mode for now, need to update to
Jacky Baiba997cc2021-06-25 09:47:46 +0800220 * support more modes, same for suspend finish call back.
Jacky Baic6fb6672023-09-18 22:08:52 +0800221 */
222 mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1);
223 mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1);
Jacky Baiba997cc2021-06-25 09:47:46 +0800224
225 } else if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) {
226 /*
227 * for cluster off state, put cluster into power down mode,
228 * config the cluster clock to be off.
229 */
230 mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7);
231 mmio_write_32(IMX_CMC1_BASE + 0x20, 0xf);
Jacky Baic6fb6672023-09-18 22:08:52 +0800232 }
233
Jacky Baic6fb6672023-09-18 22:08:52 +0800234 if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
235 /*
236 * low power mode config info used by upower
237 * to do low power mode transition.
238 */
239 imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
240 imx_set_pwr_mode_cfg(ACT_PWR_MODE);
Jacky Baiba997cc2021-06-25 09:47:46 +0800241 imx_set_pwr_mode_cfg(PD_PWR_MODE);
242
243 /* clear the upower wakeup */
244 upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
245 upower_wait_resp();
246
247 /* enable the USB wakeup */
248 usb_wakeup_enable(true);
249
250 /* config the WUU to enabled the wakeup source */
251 mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000);
252
253 /* !!! clear all the pad wakeup pending event */
254 mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
255
256 /* enable upower usb phy wakeup by default */
257 mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4) | BIT(1) | BIT(0));
258
259 /* enabled all pad wakeup by default */
260 mmio_write_32(IMX_WUU1_BASE + 0x8, 0xffffffff);
261
262 /* save the AD domain context before entering PD mode */
263 imx_apd_ctx_save(cpu);
Jacky Baic6fb6672023-09-18 22:08:52 +0800264 }
265}
Jacky Bai4d93d1d2020-07-02 14:39:58 +0800266
Jacky Baiba997cc2021-06-25 09:47:46 +0800267extern void imx8ulp_init_scmi_server(void);
Jacky Baic6fb6672023-09-18 22:08:52 +0800268void imx_domain_suspend_finish(const psci_power_state_t *target_state)
269{
270 unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
271
272 if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
Jacky Baiba997cc2021-06-25 09:47:46 +0800273 /* restore the ap domain context */
274 imx_apd_ctx_restore(cpu);
Jacky Baic6fb6672023-09-18 22:08:52 +0800275
Jacky Baiba997cc2021-06-25 09:47:46 +0800276 /* clear the upower wakeup */
277 upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
278 upower_wait_resp();
279
280 /* disable all pad wakeup */
281 mmio_write_32(IMX_WUU1_BASE + 0x8, 0x0);
282
283 /* clear all the pad wakeup pending event */
284 mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
285
286 /*
287 * disable the usb wakeup after resume to make sure the pending
288 * usb wakeup in WUU can be cleared successfully, otherwise,
289 * APD will resume failed in next PD mode.
290 */
291 usb_wakeup_enable(false);
292
293 /* re-init the SCMI channel */
294 imx8ulp_init_scmi_server();
Jacky Baic6fb6672023-09-18 22:08:52 +0800295 }
296
Jacky Baiba997cc2021-06-25 09:47:46 +0800297 /* clear cluster's LPM setting. */
298 mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0);
299 mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0);
300
Jacky Baic6fb6672023-09-18 22:08:52 +0800301 /* clear core's LPM setting */
302 mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0);
303 mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0);
304
305 if (is_local_state_off(CORE_PWR_STATE(target_state))) {
306 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
307 plat_gic_cpuif_enable();
308 } else {
309 dsb();
310 write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT));
311 isb();
312 }
313}
314
Jacky Bai4d93d1d2020-07-02 14:39:58 +0800315void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
316{
317 while (1) {
318 wfi();
319 }
320}
321
322void __dead2 imx8ulp_system_reset(void)
323{
324 imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
325
326 /* Write invalid command to WDOG CNT to trigger reset */
327 mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
328
329 while (true) {
330 wfi();
331 }
332}
333
Jacky Baic6fb6672023-09-18 22:08:52 +0800334int imx_validate_power_state(unsigned int power_state,
335 psci_power_state_t *req_state)
336{
337 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
338 int pwr_type = psci_get_pstate_type(power_state);
339
340 if (pwr_lvl > PLAT_MAX_PWR_LVL) {
341 return PSCI_E_INVALID_PARAMS;
342 }
343
344 if (pwr_type == PSTATE_TYPE_STANDBY) {
345 CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
346 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
347 }
348
349 /* No power down state support */
350 if (pwr_type == PSTATE_TYPE_POWERDOWN) {
351 return PSCI_E_INVALID_PARAMS;
352 }
353
354 return PSCI_E_SUCCESS;
355}
356
357void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
358{
359 unsigned int i;
360
361 for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
362 req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE;
363 }
364}
365
Jacky Bai4d93d1d2020-07-02 14:39:58 +0800366static const plat_psci_ops_t imx_plat_psci_ops = {
367 .pwr_domain_on = imx_pwr_domain_on,
368 .pwr_domain_on_finish = imx_pwr_domain_on_finish,
369 .validate_ns_entrypoint = imx_validate_ns_entrypoint,
370 .system_reset = imx8ulp_system_reset,
371 .pwr_domain_off = imx_pwr_domain_off,
Jacky Baic6fb6672023-09-18 22:08:52 +0800372 .pwr_domain_suspend = imx_domain_suspend,
373 .pwr_domain_suspend_finish = imx_domain_suspend_finish,
374 .get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
375 .validate_power_state = imx_validate_power_state,
Jacky Bai4d93d1d2020-07-02 14:39:58 +0800376 .pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi,
377};
378
379int plat_setup_psci_ops(uintptr_t sec_entrypoint,
380 const plat_psci_ops_t **psci_ops)
381{
382 secure_entrypoint = sec_entrypoint;
383 imx_pwr_set_cpu_entry(0, sec_entrypoint);
384 *psci_ops = &imx_plat_psci_ops;
385
386 mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
387 mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
388
389 return 0;
390}