xref: /optee_os/core/drivers/pm/sam/at91_pm.c (revision 22b10ee0cdb0e66404166421929c465a44d8e7ee)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2021, Microchip
4  */
5 
6 #include <assert.h>
7 #include <at91_clk.h>
8 #include <drivers/atmel_shdwc.h>
9 #include <drivers/pm/sam/atmel_pm.h>
10 #include <drivers/sam/at91_ddr.h>
11 #include <io.h>
12 #include <kernel/boot.h>
13 #include <kernel/dt.h>
14 #include <kernel/pm.h>
15 #include <kernel/tlb_helpers.h>
16 #include <libfdt.h>
17 #include <matrix.h>
18 #include <mm/core_memprot.h>
19 #include <smc_ids.h>
20 #include <sm/pm.h>
21 #include <stdbool.h>
22 #include <tee_api_types.h>
23 
24 #include "at91_pm.h"
25 
26 #if CFG_ATMEL_PM_SUSPEND_MODE < AT91_PM_STANDBY || \
27 	CFG_ATMEL_PM_SUSPEND_MODE > AT91_PM_BACKUP
28 #error Invalid suspend mode, please check CFG_ATMEL_PM_SUSPEND_MODE
29 #endif
30 
31 #define AT91_SECUMOD_SYSR		0x04
32 #define AT91_SECUMOD_RAMRDY		0x14
33 #define AT91_SECUMOD_RAMRDY_READY	BIT(0)
34 
35 static struct at91_pm_data soc_pm;
36 
37 /* Backup canary */
38 static uint32_t canary = 0xA5A5A5A5;
39 
40 /* Backup mode information used by at91bootstrap */
41 static struct at91bootstrap_bu {
42 	uint32_t suspended;
43 	uint32_t reserved;
44 	uint32_t *canary;
45 	uint32_t resume;
46 } *at91bootstrap_bu;
47 
48 static vaddr_t at91_suspend_sram_base;
49 static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
50 
51 enum sm_handler_ret at91_pm_set_suspend_mode(struct thread_smc_args *args)
52 {
53 	unsigned int mode = args->a1;
54 
55 	/*
56 	 * We don't expect this function to be called simultaneously while we
57 	 * are entering suspend/resume function. On sama5d2, this is not a
58 	 * problem since this SoC is a single core one but in order to prevent
59 	 * any other SoC support to be added without handling this concurrency,
60 	 * check that we are compiled for a single core.
61 	 */
62 	COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
63 
64 	if (mode > AT91_PM_BACKUP) {
65 		args->a0 = SAMA5_SMC_SIP_RETURN_EINVAL;
66 		return SM_HANDLER_SMC_HANDLED;
67 	}
68 	DMSG("Setting suspend mode to %u", mode);
69 
70 	args->a0 = SAMA5_SMC_SIP_RETURN_SUCCESS;
71 	soc_pm.mode = mode;
72 
73 	return SM_HANDLER_SMC_HANDLED;
74 }
75 
76 enum sm_handler_ret at91_pm_get_suspend_mode(struct thread_smc_args *args)
77 {
78 	args->a1 = soc_pm.mode;
79 	args->a0 = SAMA5_SMC_SIP_RETURN_SUCCESS;
80 
81 	return SM_HANDLER_SMC_HANDLED;
82 }
83 
84 static void at91_pm_copy_suspend_to_sram(void)
85 {
86 	memcpy((void *)at91_suspend_sram_base, &at91_pm_suspend_in_sram,
87 	       at91_pm_suspend_in_sram_sz);
88 
89 	cache_op_inner(DCACHE_AREA_CLEAN, (void *)at91_suspend_sram_base,
90 		       at91_pm_suspend_in_sram_sz);
91 	cache_op_inner(ICACHE_AREA_INVALIDATE, at91_suspend_sram_fn,
92 		       at91_pm_suspend_in_sram_sz);
93 }
94 
95 void atmel_pm_cpu_idle(void)
96 {
97 	uint32_t lpr0 = 0;
98 	uint32_t saved_lpr0 = 0;
99 
100 	saved_lpr0 = io_read32(soc_pm.ramc + AT91_DDRSDRC_LPR);
101 	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
102 	lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;
103 
104 	io_write32(soc_pm.ramc + AT91_DDRSDRC_LPR, lpr0);
105 
106 	cpu_idle();
107 
108 	io_write32(soc_pm.ramc + AT91_DDRSDRC_LPR, saved_lpr0);
109 }
110 
111 static void at91_sama5d2_config_shdwc_ws(vaddr_t shdwc, uint32_t *mode,
112 					 uint32_t *polarity)
113 {
114 	uint32_t val = 0;
115 
116 	/* SHDWC.WUIR */
117 	val = io_read32(shdwc + AT91_SHDW_WUIR);
118 	*mode |= val & AT91_SHDW_WKUPEN_MASK;
119 	*polarity |= (val >> AT91_SHDW_WKUPT_SHIFT) & AT91_SHDW_WKUPT_MASK;
120 }
121 
122 static int at91_sama5d2_config_pmc_ws(vaddr_t pmc, uint32_t mode,
123 				      uint32_t polarity)
124 {
125 	io_write32(pmc + AT91_PMC_FSMR, mode);
126 	io_write32(pmc + AT91_PMC_FSPR, polarity);
127 
128 	return 0;
129 }
130 
131 struct wakeup_source_info {
132 	unsigned int pmc_fsmr_bit;
133 	unsigned int shdwc_mr_bit;
134 	bool set_polarity;
135 };
136 
137 static const struct wakeup_source_info ws_info[] = {
138 	{ .pmc_fsmr_bit = AT91_PMC_FSTT(10),	.set_polarity = true },
139 	{ .pmc_fsmr_bit = AT91_PMC_RTCAL,	.shdwc_mr_bit = BIT(17) },
140 	{ .pmc_fsmr_bit = AT91_PMC_USBAL },
141 	{ .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
142 };
143 
144 struct wakeup_src {
145 	const char *compatible;
146 	const struct wakeup_source_info *info;
147 };
148 
149 static const struct wakeup_src sama5d2_ws_ids[] = {
150 	{ .compatible = "atmel,sama5d2-gem",		.info = &ws_info[0] },
151 	{ .compatible = "atmel,at91rm9200-rtc",		.info = &ws_info[1] },
152 	{ .compatible = "atmel,sama5d3-udc",		.info = &ws_info[2] },
153 	{ .compatible = "atmel,at91rm9200-ohci",	.info = &ws_info[2] },
154 	{ .compatible = "usb-ohci",			.info = &ws_info[2] },
155 	{ .compatible = "atmel,at91sam9g45-ehci",	.info = &ws_info[2] },
156 	{ .compatible = "usb-ehci",			.info = &ws_info[2] },
157 	{ .compatible = "atmel,sama5d2-sdhci",		.info = &ws_info[3] }
158 };
159 
160 static bool dev_is_wakeup_source(const void *fdt, int node)
161 {
162 	return fdt_get_property(fdt, node, "wakeup-source", NULL);
163 }
164 
165 static int at91_pm_config_ws_ulp1(bool set)
166 {
167 	const struct wakeup_source_info *wsi = NULL;
168 	const struct wakeup_src *wsrc = NULL;
169 	unsigned int polarity = 0;
170 	unsigned int mode = 0;
171 	unsigned int val = 0;
172 	unsigned int src = 0;
173 	int node = 0;
174 
175 	if (!set) {
176 		io_write32(soc_pm.pmc + AT91_PMC_FSMR, mode);
177 		return TEE_SUCCESS;
178 	}
179 
180 	at91_sama5d2_config_shdwc_ws(soc_pm.shdwc, &mode, &polarity);
181 
182 	val = io_read32(soc_pm.shdwc + AT91_SHDW_MR);
183 
184 	/* Loop through defined wakeup sources. */
185 	for (src = 0; src < ARRAY_SIZE(sama5d2_ws_ids); src++) {
186 		wsrc = &sama5d2_ws_ids[src];
187 		wsi = wsrc->info;
188 
189 		node = fdt_node_offset_by_compatible(soc_pm.fdt, -1,
190 						     wsrc->compatible);
191 		while (node >= 0) {
192 			if (dev_is_wakeup_source(soc_pm.fdt, node)) {
193 				/* Check if enabled on SHDWC. */
194 				if (wsi->shdwc_mr_bit &&
195 				    !(val & wsi->shdwc_mr_bit))
196 					goto next_node;
197 
198 				mode |= wsi->pmc_fsmr_bit;
199 				if (wsi->set_polarity)
200 					polarity |= wsi->pmc_fsmr_bit;
201 			}
202 next_node:
203 			node = fdt_node_offset_by_compatible(soc_pm.fdt, node,
204 							     wsrc->compatible);
205 		}
206 	}
207 
208 	if (!mode) {
209 		EMSG("AT91: PM: no ULP1 wakeup sources found!");
210 		return TEE_ERROR_BAD_STATE;
211 	}
212 
213 	at91_sama5d2_config_pmc_ws(soc_pm.pmc, mode, polarity);
214 
215 	return TEE_SUCCESS;
216 }
217 
218 /*
219  * Verify that all the clocks are correct before entering
220  * slow-clock mode.
221  */
222 static bool at91_pm_verify_clocks(void)
223 {
224 	int i = 0;
225 	uint32_t scsr = 0;
226 
227 	scsr = io_read32(soc_pm.pmc + AT91_PMC_SCSR);
228 
229 	/* USB must not be using PLLB */
230 	if ((scsr & (AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP)) != 0) {
231 		EMSG("AT91: PM - Suspend-to-RAM with USB still active");
232 		return false;
233 	}
234 
235 	/* PCK0..PCK3 must be disabled, or configured to use clk32k */
236 	for (i = 0; i < 4; i++) {
237 		uint32_t css = 0;
238 
239 		if ((scsr & (AT91_PMC_PCK0 << i)) == 0)
240 			continue;
241 		css = io_read32(soc_pm.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS;
242 		if (css != AT91_PMC_CSS_SLOW) {
243 			EMSG("AT91: PM - Suspend-to-RAM with PCK%d src %"PRId32,
244 			     i, css);
245 			return false;
246 		}
247 	}
248 
249 	return true;
250 }
251 
252 static TEE_Result at91_write_backup_data(void)
253 {
254 	uint32_t val = 0;
255 
256 	while (true) {
257 		val = io_read32(soc_pm.secumod + AT91_SECUMOD_RAMRDY);
258 		if (val & AT91_SECUMOD_RAMRDY_READY)
259 			break;
260 	}
261 
262 	io_write32((vaddr_t)&at91bootstrap_bu->suspended, 1);
263 	io_write32((vaddr_t)&at91bootstrap_bu->canary, virt_to_phys(&canary));
264 	io_write32((vaddr_t)&at91bootstrap_bu->resume,
265 		   virt_to_phys((void *)(vaddr_t)at91_pm_cpu_resume));
266 
267 	return TEE_SUCCESS;
268 }
269 
270 static TEE_Result at91_enter_backup(void)
271 {
272 	int ret = -1;
273 	TEE_Result res = TEE_ERROR_GENERIC;
274 
275 	res = at91_write_backup_data();
276 	if (res)
277 		return res;
278 
279 	pm_change_state(PM_OP_SUSPEND, 0);
280 	ret = sm_pm_cpu_suspend((uint32_t)&soc_pm,
281 				(void *)at91_suspend_sram_fn);
282 	if (ret < 0) {
283 		DMSG("Suspend failed");
284 		res = TEE_ERROR_BAD_STATE;
285 	} else {
286 		res = TEE_SUCCESS;
287 	}
288 
289 	pm_change_state(PM_OP_RESUME, 0);
290 	if (res)
291 		return res;
292 
293 	/* SRAM content is lost after resume */
294 	at91_pm_copy_suspend_to_sram();
295 
296 	return TEE_SUCCESS;
297 }
298 
299 TEE_Result atmel_pm_suspend(uintptr_t entry, struct sm_nsec_ctx *nsec)
300 {
301 	TEE_Result res = TEE_ERROR_GENERIC;
302 	uint32_t sctlr = 0;
303 
304 	DMSG("Entering suspend mode %d", soc_pm.mode);
305 
306 	if (soc_pm.mode >= AT91_PM_ULP0) {
307 		if (!at91_pm_verify_clocks())
308 			return TEE_ERROR_BAD_STATE;
309 	}
310 
311 	if (soc_pm.mode == AT91_PM_ULP1)
312 		at91_pm_config_ws_ulp1(true);
313 
314 	sm_save_unbanked_regs(&nsec->ub_regs);
315 
316 	/*
317 	 * In order to run code for low-power out of SRAM without abort,
318 	 * configure regions with write permission with not forced to
319 	 * XN (Execute-never) attribute.
320 	 */
321 	if (IS_ENABLED(CFG_HWSUPP_MEM_PERM_WXN)) {
322 		sctlr = read_sctlr();
323 		if (sctlr & SCTLR_WXN) {
324 			write_sctlr(sctlr & ~SCTLR_WXN);
325 			tlbi_all();
326 		}
327 	}
328 
329 	if (soc_pm.mode == AT91_PM_BACKUP) {
330 		res = at91_enter_backup();
331 	} else {
332 		at91_suspend_sram_fn(&soc_pm);
333 		res = TEE_SUCCESS;
334 	}
335 
336 	/* Restore the XN attribute */
337 	if (IS_ENABLED(CFG_HWSUPP_MEM_PERM_WXN)) {
338 		if (sctlr & SCTLR_WXN) {
339 			write_sctlr(sctlr);
340 			tlbi_all();
341 		}
342 	}
343 
344 	if (soc_pm.mode == AT91_PM_ULP1)
345 		at91_pm_config_ws_ulp1(false);
346 
347 	sm_restore_unbanked_regs(&nsec->ub_regs);
348 
349 	/*
350 	 * If the system went to backup mode, register state was lost and must
351 	 * be restored by jumping to the user provided entry point
352 	 */
353 	if (res == TEE_SUCCESS && soc_pm.mode == AT91_PM_BACKUP)
354 		nsec->mon_lr = entry;
355 
356 	DMSG("Exiting suspend mode %d, res %"PRIx32, soc_pm.mode, res);
357 
358 	return res;
359 }
360 
361 static TEE_Result at91_pm_dt_dram_init(const void *fdt)
362 {
363 	int node = -1;
364 	size_t size = 0;
365 
366 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d3-ddramc");
367 	if (node < 0)
368 		return TEE_ERROR_ITEM_NOT_FOUND;
369 
370 	if (dt_map_dev(fdt, node, &soc_pm.ramc, &size, DT_MAP_AUTO) < 0)
371 		return TEE_ERROR_GENERIC;
372 
373 	return TEE_SUCCESS;
374 }
375 
376 static TEE_Result at91_pm_backup_init(const void *fdt)
377 {
378 	enum dt_map_dev_directive mapping = DT_MAP_AUTO;
379 	int node = -1;
380 	size_t size = 0;
381 
382 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-sfrbu");
383 	if (node < 0)
384 		return TEE_ERROR_ITEM_NOT_FOUND;
385 
386 	if (IS_ENABLED(CFG_SAMA7G5))
387 		mapping = DT_MAP_SECURE;
388 
389 	if (dt_map_dev(fdt, node, &soc_pm.sfrbu, &size, mapping) < 0)
390 		return TEE_ERROR_GENERIC;
391 
392 	if (fdt_get_status(fdt, node) == DT_STATUS_OK_SEC)
393 		/* for SAMA7G5 SFRBU is always secured, no need to configre */
394 		if (!IS_ENABLED(CFG_SAMA7G5))
395 			matrix_configure_periph_secure(AT91C_ID_SFRBU);
396 
397 	return TEE_SUCCESS;
398 }
399 
400 static TEE_Result at91_pm_sram_init(const void *fdt)
401 {
402 	int node = -1;
403 	size_t size = 0;
404 	paddr_t at91_suspend_sram_pbase;
405 	size_t suspend_sz = at91_pm_suspend_in_sram_sz;
406 
407 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-sram");
408 	if (node < 0)
409 		return TEE_ERROR_ITEM_NOT_FOUND;
410 
411 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
412 		return TEE_ERROR_GENERIC;
413 
414 	if (dt_map_dev(fdt, node, &at91_suspend_sram_base, &size,
415 		       DT_MAP_AUTO) < 0)
416 		return TEE_ERROR_GENERIC;
417 
418 	at91_suspend_sram_pbase = virt_to_phys((void *)at91_suspend_sram_base);
419 
420 	/*
421 	 * Map the secure ram suspend code with the memory area type
422 	 * "MEM_AREA_TEE_COHERENT" to make it non-cacheable.
423 	 * Mapping with memory area type "MEM_AREA_TEE_RAM" would enable
424 	 * cacheable attribute and might cause abort in some cases.
425 	 */
426 	at91_suspend_sram_fn = core_mmu_add_mapping(MEM_AREA_TEE_COHERENT,
427 						    at91_suspend_sram_pbase,
428 						    suspend_sz);
429 	if (!at91_suspend_sram_fn) {
430 		EMSG("Failed to remap sram as executable");
431 		return TEE_ERROR_GENERIC;
432 	}
433 
434 	at91_pm_copy_suspend_to_sram();
435 
436 	return TEE_SUCCESS;
437 }
438 
439 static TEE_Result at91_securam_init(const void *fdt)
440 {
441 	int node = -1;
442 	size_t size = 0;
443 	struct clk *clk = NULL;
444 	TEE_Result res = TEE_ERROR_GENERIC;
445 
446 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-securam");
447 	if (node < 0)
448 		return TEE_ERROR_ITEM_NOT_FOUND;
449 
450 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
451 		return TEE_ERROR_GENERIC;
452 
453 	if (dt_map_dev(fdt, node, &soc_pm.securam, &size, DT_MAP_AUTO) < 0)
454 		return TEE_ERROR_GENERIC;
455 
456 	res = clk_dt_get_by_index(fdt, node, 0, &clk);
457 	if (res)
458 		return res;
459 
460 	if (clk_enable(clk))
461 		return TEE_ERROR_GENERIC;
462 
463 	if (size < sizeof(struct at91bootstrap_bu))
464 		return TEE_ERROR_SHORT_BUFFER;
465 
466 	at91bootstrap_bu = (void *)soc_pm.securam;
467 
468 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-secumod");
469 	if (node < 0)
470 		return TEE_ERROR_ITEM_NOT_FOUND;
471 
472 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
473 		return TEE_ERROR_GENERIC;
474 
475 	if (dt_map_dev(fdt, node, &soc_pm.secumod, &size, DT_MAP_AUTO) < 0)
476 		return TEE_ERROR_GENERIC;
477 
478 	return TEE_SUCCESS;
479 }
480 
481 static TEE_Result sam_pm_init_all(const void *fdt, vaddr_t shdwc)
482 {
483 	TEE_Result res = TEE_ERROR_GENERIC;
484 
485 	soc_pm.fdt = fdt;
486 	soc_pm.shdwc = shdwc;
487 	soc_pm.pmc = at91_pmc_get_base();
488 	if (!soc_pm.pmc)
489 		return TEE_ERROR_GENERIC;
490 
491 	soc_pm.mode = CFG_ATMEL_PM_SUSPEND_MODE;
492 
493 	res = at91_securam_init(fdt);
494 	if (res)
495 		return res;
496 
497 	res = at91_pm_dt_dram_init(fdt);
498 	if (res)
499 		return res;
500 
501 	res = at91_pm_backup_init(fdt);
502 	if (res)
503 		return res;
504 
505 	res = at91_pm_sram_init(fdt);
506 	if (res)
507 		return res;
508 
509 	return TEE_SUCCESS;
510 }
511 
512 TEE_Result sam_pm_init(const void *fdt, vaddr_t shdwc)
513 {
514 	if (sam_pm_init_all(fdt, shdwc))
515 		panic("Failed to setup PM for this MPU");
516 
517 	return TEE_SUCCESS;
518 }
519