1 /*
2  * Copyright (c) 2022-2023, MediaTek Inc. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <stdint.h>
9 
10 #include <lib/spinlock.h>
11 
12 #include <lib/mtk_init/mtk_init.h>
13 #include <lib/pm/mtk_pm.h>
14 #include <lpm/mt_lp_rm.h>
15 #include "mt_cpu_pm.h"
16 #include "mt_cpu_pm_cpc.h"
17 #include "mt_cpu_pm_mbox.h"
18 #include "mt_smp.h"
19 #include <mtk_mmap_pool.h>
20 #include <platform_def.h>
21 
22 /*
23  * The locker must use the bakery locker when cache turns off.
24  * Using spin_lock will gain better performance.
25  */
26 #ifdef MT_CPU_PM_USING_BAKERY_LOCK
27 DEFINE_BAKERY_LOCK(mt_cpu_pm_lock);
28 #define plat_cpu_pm_lock_init()	bakery_lock_init(&mt_cpu_pm_lock)
29 #define plat_cpu_pm_lock()	bakery_lock_get(&mt_cpu_pm_lock)
30 #define plat_cpu_pm_unlock()	bakery_lock_release(&mt_cpu_pm_lock)
31 #else
32 spinlock_t mt_cpu_pm_lock;
33 #define plat_cpu_pm_lock_init()
34 #define plat_cpu_pm_lock()	spin_lock(&mt_cpu_pm_lock)
35 #define plat_cpu_pm_unlock()	spin_unlock(&mt_cpu_pm_lock)
36 #endif
37 
38 enum mt_pwr_node {
39 	MT_PWR_NONMCUSYS = 0,
40 	MT_PWR_MCUSYS_PDN,
41 	MT_PWR_SUSPEND,
42 	MT_PWR_SYSTEM_MEM,
43 	MT_PWR_SYSTEM_PLL,
44 	MT_PWR_SYSTEM_BUS,
45 	MT_PWR_MAX,
46 };
47 
48 #define CPU_PM_DEPD_INIT	BIT(0)
49 #define CPU_PM_DEPD_READY	BIT(1)
50 #define CPU_PM_PLAT_READY	BIT(2)
51 
52 #ifdef CPU_PM_TINYSYS_SUPPORT
53 #define CPU_PM_INIT_READY	(CPU_PM_DEPD_INIT | CPU_PM_DEPD_READY)
54 #define CPU_PM_LP_READY		(CPU_PM_INIT_READY | CPU_PM_PLAT_READY)
55 #else
56 #define CPU_PM_LP_READY		(CPU_PM_PLAT_READY)
57 #endif
58 
59 #if CONFIG_MTK_PM_SUPPORT
60 
61 #if CONFIG_MTK_CPU_SUSPEND_EN || CONFIG_MTK_SMP_EN
cpupm_cpu_resume_common(const struct mtk_cpupm_pwrstate * state)62 static void cpupm_cpu_resume_common(const struct mtk_cpupm_pwrstate *state)
63 {
64 	CPU_PM_ASSERT(state != NULL);
65 	mtk_cpc_core_on_hint_clr(state->info.cpuid);
66 }
67 #endif
68 
69 #if CONFIG_MTK_SMP_EN
cpupm_cpu_pwr_on_prepare(unsigned int cpu,uintptr_t entry)70 static int cpupm_cpu_pwr_on_prepare(unsigned int cpu, uintptr_t entry)
71 {
72 	struct cpu_pwr_ctrl pwr_ctrl;
73 
74 	PER_CPU_PWR_CTRL(pwr_ctrl, cpu);
75 	mt_smp_core_bootup_address_set(&pwr_ctrl, entry);
76 	mt_smp_core_init_arch(0, cpu, 1, &pwr_ctrl);
77 
78 	return mt_smp_power_core_on(cpu, &pwr_ctrl);
79 }
80 
cpupm_cpu_resume_smp(const struct mtk_cpupm_pwrstate * state)81 static void cpupm_cpu_resume_smp(const struct mtk_cpupm_pwrstate *state)
82 {
83 	CPU_PM_ASSERT(state != NULL);
84 
85 	plat_cpu_pm_lock();
86 	mmio_clrbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG,
87 			GIC_WAKEUP_IGNORE(state->info.cpuid));
88 	plat_cpu_pm_unlock();
89 	cpupm_cpu_resume_common(state);
90 }
91 
cpupm_cpu_suspend_smp(const struct mtk_cpupm_pwrstate * state)92 static void cpupm_cpu_suspend_smp(const struct mtk_cpupm_pwrstate *state)
93 {
94 	struct cpu_pwr_ctrl pwr_ctrl;
95 
96 	CPU_PM_ASSERT(state != NULL);
97 
98 	PER_CPU_PWR_CTRL(pwr_ctrl, state->info.cpuid);
99 	mt_smp_power_core_off(&pwr_ctrl);
100 	mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG,
101 			GIC_WAKEUP_IGNORE(state->info.cpuid));
102 }
103 
cpupm_smp_init(unsigned int cpu,uintptr_t sec_entrypoint)104 static void cpupm_smp_init(unsigned int cpu, uintptr_t sec_entrypoint)
105 {
106 	unsigned int reg;
107 	struct mtk_cpupm_pwrstate state = {
108 		.info = {
109 			.cpuid = cpu,
110 			.mode = MTK_CPU_PM_SMP,
111 		},
112 		.pwr = {
113 			.afflv = 0,
114 			.state_id = 0,
115 		},
116 	};
117 
118 	reg = mmio_read_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG);
119 	if ((reg & CPC_MCUSYS_CPC_RESET_PWR_ON_EN) != 0) {
120 		INFO("[%s:%d][CPU_PM] reset pwr on is enabled then clear it!\n",
121 		     __func__, __LINE__);
122 		mmio_clrbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, CPC_MCUSYS_CPC_RESET_PWR_ON_EN);
123 	}
124 
125 	cpupm_cpu_pwr_on_prepare(cpu, sec_entrypoint);
126 	cpupm_cpu_resume_smp(&state);
127 }
128 
129 static struct mtk_cpu_smp_ops cpcv3_2_cpu_smp = {
130 	.init = cpupm_smp_init,
131 	.cpu_pwr_on_prepare = cpupm_cpu_pwr_on_prepare,
132 	.cpu_on = cpupm_cpu_resume_smp,
133 	.cpu_off = cpupm_cpu_suspend_smp,
134 };
135 
136 #endif /* CONFIG_MTK_SMP_EN */
137 
138 #if CONFIG_MTK_CPU_SUSPEND_EN
139 #define CPUPM_READY_MS		(40000)
140 #define CPUPM_ARCH_TIME_MS(ms)	(ms * 1000 * SYS_COUNTER_FREQ_IN_MHZ)
141 #define CPUPM_BOOTUP_TIME_THR	CPUPM_ARCH_TIME_MS(CPUPM_READY_MS)
142 
143 static int mt_pwr_nodes[MT_PWR_MAX];
144 static int plat_mt_lp_cpu_rc;
145 static unsigned int cpu_pm_status;
146 static unsigned int plat_prev_stateid;
147 
mcusys_prepare_suspend(const struct mtk_cpupm_pwrstate * state)148 static int mcusys_prepare_suspend(const struct mtk_cpupm_pwrstate *state)
149 {
150 	unsigned int stateid = state->pwr.state_id;
151 
152 	if (mtk_cpc_mcusys_off_prepare() != CPC_SUCCESS) {
153 		goto mt_pwr_mcusysoff_break;
154 	}
155 
156 	if (!IS_PLAT_SUSPEND_ID(stateid)) {
157 		if (mt_pwr_nodes[MT_PWR_SYSTEM_MEM] != 0) {
158 			stateid = MT_PLAT_PWR_STATE_SYSTEM_MEM;
159 		} else if (mt_pwr_nodes[MT_PWR_SYSTEM_PLL] != 0) {
160 			stateid = MT_PLAT_PWR_STATE_SYSTEM_PLL;
161 		} else if (mt_pwr_nodes[MT_PWR_SYSTEM_BUS] != 0) {
162 			stateid = MT_PLAT_PWR_STATE_SYSTEM_BUS;
163 		} else if (mt_pwr_nodes[MT_PWR_SUSPEND] != 0) {
164 			stateid = MT_PLAT_PWR_STATE_SUSPEND;
165 		} else {
166 			stateid = MT_PLAT_PWR_STATE_MCUSYS;
167 		}
168 	}
169 
170 	plat_prev_stateid = stateid;
171 	plat_mt_lp_cpu_rc = mt_lp_rm_find_and_run_constraint(0, state->info.cpuid, stateid, NULL);
172 
173 	if (plat_mt_lp_cpu_rc < 0) {
174 		goto mt_pwr_mcusysoff_reflect;
175 	}
176 
177 #ifdef CPU_PM_TINYSYS_SUPPORT
178 	mtk_set_cpu_pm_preffered_cpu(state->info.cpuid);
179 #endif
180 	return MTK_CPUPM_E_OK;
181 
182 mt_pwr_mcusysoff_reflect:
183 	mtk_cpc_mcusys_off_reflect();
184 mt_pwr_mcusysoff_break:
185 	plat_mt_lp_cpu_rc = -1;
186 
187 	return MTK_CPUPM_E_FAIL;
188 }
189 
mcusys_prepare_resume(const struct mtk_cpupm_pwrstate * state)190 static int mcusys_prepare_resume(const struct mtk_cpupm_pwrstate *state)
191 {
192 	if (plat_mt_lp_cpu_rc < 0) {
193 		return MTK_CPUPM_E_FAIL;
194 	}
195 
196 	mt_lp_rm_reset_constraint(plat_mt_lp_cpu_rc, state->info.cpuid, plat_prev_stateid);
197 	mtk_cpc_mcusys_off_reflect();
198 	return MTK_CPUPM_E_OK;
199 }
200 
cpupm_do_pstate_off(const mtk_pstate_type psci_state,const struct mtk_cpupm_pwrstate * state)201 static unsigned int cpupm_do_pstate_off(const mtk_pstate_type psci_state,
202 					const struct mtk_cpupm_pwrstate *state)
203 {
204 	unsigned int pstate = MT_CPUPM_PWR_DOMAIN_CORE;
205 
206 	if (!state || (state->pwr.afflv > PLAT_MAX_PWR_LVL)) {
207 		CPU_PM_ASSERT(0);
208 	}
209 
210 	switch (state->pwr.state_id) {
211 	case MT_PLAT_PWR_STATE_SYSTEM_MEM:
212 		mt_pwr_nodes[MT_PWR_SYSTEM_MEM] += 1;
213 		break;
214 	case MT_PLAT_PWR_STATE_SYSTEM_PLL:
215 		mt_pwr_nodes[MT_PWR_SYSTEM_PLL] += 1;
216 		break;
217 	case MT_PLAT_PWR_STATE_SYSTEM_BUS:
218 		mt_pwr_nodes[MT_PWR_SYSTEM_BUS] += 1;
219 		break;
220 	case MT_PLAT_PWR_STATE_SUSPEND:
221 		mt_pwr_nodes[MT_PWR_SUSPEND] += 1;
222 		break;
223 	default:
224 		if (!IS_MT_PLAT_PWR_STATE_MCUSYS(state->pwr.state_id) &&
225 		    !IS_PLAT_SYSTEM_SUSPEND(state->pwr.afflv)) {
226 			plat_cpu_pm_lock();
227 			mt_pwr_nodes[MT_PWR_NONMCUSYS] += 1;
228 			flush_dcache_range((uintptr_t)&mt_pwr_nodes[MT_PWR_NONMCUSYS],
229 					   sizeof(mt_pwr_nodes[MT_PWR_NONMCUSYS]));
230 			plat_cpu_pm_unlock();
231 		}
232 		break;
233 	}
234 
235 	if ((mt_pwr_nodes[MT_PWR_NONMCUSYS] == 0) && IS_PLAT_MCUSYSOFF_AFFLV(state->pwr.afflv)) {
236 		/* Prepare to power down mcusys */
237 		if (mcusys_prepare_suspend(state) == MTK_CPUPM_E_OK) {
238 			mt_pwr_nodes[MT_PWR_MCUSYS_PDN] += 1;
239 			flush_dcache_range((uintptr_t)&mt_pwr_nodes[MT_PWR_MCUSYS_PDN],
240 					   sizeof(mt_pwr_nodes[MT_PWR_MCUSYS_PDN]));
241 			pstate |= (MT_CPUPM_PWR_DOMAIN_MCUSYS | MT_CPUPM_PWR_DOMAIN_CLUSTER);
242 		}
243 	}
244 
245 	if (state->pwr.afflv >= PLAT_MT_CPU_SUSPEND_CLUSTER) {
246 		pstate |= MT_CPUPM_PWR_DOMAIN_CLUSTER;
247 	}
248 
249 	if (psci_get_pstate_pwrlvl(psci_state) >= PLAT_MT_CPU_SUSPEND_CLUSTER) {
250 		pstate |= MT_CPUPM_PWR_DOMAIN_PERCORE_DSU;
251 	}
252 
253 	return pstate;
254 }
255 
cpupm_do_pstate_on(const mtk_pstate_type psci_state,const struct mtk_cpupm_pwrstate * state)256 static unsigned int cpupm_do_pstate_on(const mtk_pstate_type psci_state,
257 				       const struct mtk_cpupm_pwrstate *state)
258 {
259 	unsigned int pstate = MT_CPUPM_PWR_DOMAIN_CORE;
260 
261 	CPU_PM_ASSERT(state != NULL);
262 
263 	if (state->pwr.afflv > PLAT_MAX_PWR_LVL) {
264 		CPU_PM_ASSERT(0);
265 	}
266 
267 	if (mt_pwr_nodes[MT_PWR_MCUSYS_PDN] != 0) {
268 		mt_pwr_nodes[MT_PWR_MCUSYS_PDN] = 0;
269 		flush_dcache_range((uintptr_t)&mt_pwr_nodes[MT_PWR_MCUSYS_PDN],
270 				   sizeof(mt_pwr_nodes[MT_PWR_MCUSYS_PDN]));
271 		pstate |= (MT_CPUPM_PWR_DOMAIN_MCUSYS | MT_CPUPM_PWR_DOMAIN_CLUSTER);
272 		mcusys_prepare_resume(state);
273 	}
274 
275 	if (state->pwr.afflv >= PLAT_MT_CPU_SUSPEND_CLUSTER) {
276 		pstate |= MT_CPUPM_PWR_DOMAIN_CLUSTER;
277 	}
278 
279 	switch (state->pwr.state_id) {
280 	case MT_PLAT_PWR_STATE_SYSTEM_MEM:
281 		mt_pwr_nodes[MT_PWR_SYSTEM_MEM] -= 1;
282 		CPU_PM_ASSERT(mt_pwr_nodes[MT_PWR_SYSTEM_MEM] >= 0);
283 		break;
284 	case MT_PLAT_PWR_STATE_SYSTEM_PLL:
285 		mt_pwr_nodes[MT_PWR_SYSTEM_PLL] -= 1;
286 		CPU_PM_ASSERT(mt_pwr_nodes[MT_PWR_SYSTEM_PLL] >= 0);
287 		break;
288 	case MT_PLAT_PWR_STATE_SYSTEM_BUS:
289 		mt_pwr_nodes[MT_PWR_SYSTEM_BUS] -= 1;
290 		CPU_PM_ASSERT(mt_pwr_nodes[MT_PWR_SYSTEM_BUS] >= 0);
291 		break;
292 	case MT_PLAT_PWR_STATE_SUSPEND:
293 		mt_pwr_nodes[MT_PWR_SUSPEND] -= 1;
294 		CPU_PM_ASSERT(mt_pwr_nodes[MT_PWR_SUSPEND] >= 0);
295 		break;
296 	default:
297 		if (!IS_MT_PLAT_PWR_STATE_MCUSYS(state->pwr.state_id) &&
298 		    !IS_PLAT_SYSTEM_SUSPEND(state->pwr.afflv)) {
299 			plat_cpu_pm_lock();
300 			mt_pwr_nodes[MT_PWR_NONMCUSYS] -= 1;
301 			flush_dcache_range((uintptr_t)&mt_pwr_nodes[MT_PWR_NONMCUSYS],
302 					   sizeof(mt_pwr_nodes[MT_PWR_NONMCUSYS]));
303 			plat_cpu_pm_unlock();
304 		}
305 		break;
306 	}
307 
308 	if (IS_PLAT_SYSTEM_SUSPEND(state->pwr.afflv) ||
309 	    (IS_PLAT_SYSTEM_RETENTION(state->pwr.afflv) && (mt_pwr_nodes[MT_PWR_SUSPEND] > 0))) {
310 		mtk_cpc_time_sync();
311 	}
312 
313 	if (mt_pwr_nodes[MT_PWR_NONMCUSYS] < 0) {
314 		CPU_PM_ASSERT(0);
315 	}
316 
317 	pstate |= MT_CPUPM_PWR_DOMAIN_PERCORE_DSU;
318 
319 	return pstate;
320 }
321 
cpupm_cpu_resume(const struct mtk_cpupm_pwrstate * state)322 static void cpupm_cpu_resume(const struct mtk_cpupm_pwrstate *state)
323 {
324 	cpupm_cpu_resume_common(state);
325 }
326 
cpupm_mcusys_resume(const struct mtk_cpupm_pwrstate * state)327 static void cpupm_mcusys_resume(const struct mtk_cpupm_pwrstate *state)
328 {
329 	assert(state != NULL);
330 }
331 
cpupm_mcusys_suspend(const struct mtk_cpupm_pwrstate * state)332 static void cpupm_mcusys_suspend(const struct mtk_cpupm_pwrstate *state)
333 {
334 	assert(state != NULL);
335 }
336 
cpupm_get_pstate(enum mt_cpupm_pwr_domain domain,const mtk_pstate_type psci_state,const struct mtk_cpupm_pwrstate * state)337 static unsigned int cpupm_get_pstate(enum mt_cpupm_pwr_domain domain,
338 				     const mtk_pstate_type psci_state,
339 				     const struct mtk_cpupm_pwrstate *state)
340 {
341 	unsigned int pstate = 0;
342 
343 	if (state == NULL) {
344 		return 0;
345 	}
346 
347 	if (state->info.mode == MTK_CPU_PM_SMP) {
348 		pstate = MT_CPUPM_PWR_DOMAIN_CORE;
349 	} else {
350 		if (domain == CPUPM_PWR_OFF) {
351 			pstate = cpupm_do_pstate_off(psci_state, state);
352 		} else if (domain == CPUPM_PWR_ON) {
353 			pstate = cpupm_do_pstate_on(psci_state, state);
354 		} else {
355 			INFO("[%s:%d][CPU_PM] unknown pwr domain :%d\n",
356 			     __func__, __LINE__, domain);
357 			assert(0);
358 		}
359 	}
360 	return pstate;
361 }
362 
cpupm_init(void)363 static int cpupm_init(void)
364 {
365 	int ret = MTK_CPUPM_E_OK;
366 
367 #ifdef CPU_PM_TINYSYS_SUPPORT
368 	int status;
369 
370 	if ((cpu_pm_status & CPU_PM_INIT_READY) == CPU_PM_INIT_READY) {
371 		return MTK_CPUPM_E_OK;
372 	}
373 
374 	if (!(cpu_pm_status & CPU_PM_DEPD_INIT)) {
375 		status = mtk_lp_depd_condition(CPUPM_MBOX_WAIT_DEV_INIT);
376 		if (status == 0) {
377 			plat_cpu_pm_lock();
378 			cpu_pm_status |= CPU_PM_DEPD_INIT;
379 			plat_cpu_pm_unlock();
380 		}
381 	}
382 
383 	if ((cpu_pm_status & CPU_PM_DEPD_INIT) && !(cpu_pm_status & CPU_PM_DEPD_READY)) {
384 		status = mtk_lp_depd_condition(CPUPM_MBOX_WAIT_TASK_READY);
385 		if (status == 0) {
386 			plat_cpu_pm_lock();
387 			cpu_pm_status |= CPU_PM_DEPD_READY;
388 			plat_cpu_pm_unlock();
389 		}
390 	}
391 
392 	ret = ((cpu_pm_status & CPU_PM_INIT_READY) == CPU_PM_INIT_READY) ?
393 	      MTK_CPUPM_E_OK : MTK_CPUPM_E_FAIL;
394 #endif
395 	return ret;
396 }
397 
cpupm_pwr_state_valid(unsigned int afflv,unsigned int state)398 static int cpupm_pwr_state_valid(unsigned int afflv, unsigned int state)
399 {
400 	if (cpu_pm_status == CPU_PM_LP_READY) {
401 		return MTK_CPUPM_E_OK;
402 	}
403 
404 	if (cpupm_init() != MTK_CPUPM_E_OK) {
405 		return MTK_CPUPM_E_FAIL;
406 	}
407 
408 	if (read_cntpct_el0() >= (uint64_t)CPUPM_BOOTUP_TIME_THR) {
409 		plat_cpu_pm_lock();
410 		cpu_pm_status |= CPU_PM_PLAT_READY;
411 		plat_cpu_pm_unlock();
412 	}
413 
414 	if (!IS_PLAT_SYSTEM_SUSPEND(afflv) && (cpu_pm_status & CPU_PM_PLAT_READY) == 0) {
415 		return MTK_CPUPM_E_FAIL;
416 	}
417 
418 	return MTK_CPUPM_E_OK;
419 }
420 
421 static struct mtk_cpu_pm_ops cpcv3_2_mcdi = {
422 	.get_pstate = cpupm_get_pstate,
423 	.pwr_state_valid = cpupm_pwr_state_valid,
424 	.cpu_resume = cpupm_cpu_resume,
425 	.mcusys_suspend = cpupm_mcusys_suspend,
426 	.mcusys_resume = cpupm_mcusys_resume,
427 };
428 #endif /* CONFIG_MTK_CPU_SUSPEND_EN */
429 
430 #endif /* CONFIG_MTK_PM_SUPPORT */
431 
432 /*
433  * Depend on mtk pm methodology, the psci op init must
434  * be invoked after cpu pm to avoid initialization fail.
435  */
mt_plat_cpu_pm_init(void)436 int mt_plat_cpu_pm_init(void)
437 {
438 	plat_cpu_pm_lock_init();
439 
440 	mtk_cpc_init();
441 #if CONFIG_MTK_PM_SUPPORT
442 
443 #if CONFIG_MTK_CPU_SUSPEND_EN
444 	register_cpu_pm_ops(CPU_PM_FN, &cpcv3_2_mcdi);
445 #endif /* CONFIG_MTK_CPU_SUSPEND_EN */
446 
447 #if CONFIG_MTK_SMP_EN
448 	register_cpu_smp_ops(CPU_PM_FN, &cpcv3_2_cpu_smp);
449 #endif /* CONFIG_MTK_SMP_EN */
450 
451 #endif /* CONFIG_MTK_PM_SUPPORT */
452 
453 	INFO("[%s:%d] - CPU PM INIT finished\n", __func__, __LINE__);
454 	return 0;
455 }
456 MTK_ARCH_INIT(mt_plat_cpu_pm_init);
457 
458 static const mmap_region_t cpu_pm_mmap[] MTK_MMAP_SECTION = {
459 #ifdef CPU_PM_TINYSYS_SUPPORT
460 #if CONFIG_MTK_PM_SUPPORT && CONFIG_MTK_CPU_SUSPEND_EN
461 	MAP_REGION_FLAT(CPU_EB_TCM_BASE, CPU_EB_TCM_SIZE, MT_DEVICE | MT_RW | MT_SECURE),
462 #endif
463 #endif
464 	{0}
465 };
466 DECLARE_MTK_MMAP_REGIONS(cpu_pm_mmap);
467