xref: /optee_os/core/drivers/pm/sam/at91_pm.c (revision 7a639aedfe9c80e29e9bee7f7118f27d0511ff30)
115300b40SClément Léger // SPDX-License-Identifier: BSD-2-Clause
215300b40SClément Léger /*
315300b40SClément Léger  * Copyright (c) 2021, Microchip
415300b40SClément Léger  */
515300b40SClément Léger 
615300b40SClément Léger #include <assert.h>
715300b40SClément Léger #include <at91_clk.h>
815300b40SClément Léger #include <drivers/atmel_shdwc.h>
915300b40SClément Léger #include <drivers/pm/sam/atmel_pm.h>
1015300b40SClément Léger #include <drivers/sam/at91_ddr.h>
1115300b40SClément Léger #include <io.h>
1215300b40SClément Léger #include <kernel/boot.h>
1315300b40SClément Léger #include <kernel/dt.h>
1415300b40SClément Léger #include <kernel/pm.h>
1567aac8e6STony Han #include <kernel/tlb_helpers.h>
1615300b40SClément Léger #include <libfdt.h>
1715300b40SClément Léger #include <matrix.h>
1815300b40SClément Léger #include <mm/core_memprot.h>
19f8c3938bSClément Léger #include <smc_ids.h>
2015300b40SClément Léger #include <sm/pm.h>
2115300b40SClément Léger #include <stdbool.h>
2215300b40SClément Léger #include <tee_api_types.h>
2315300b40SClément Léger 
2415300b40SClément Léger #include "at91_pm.h"
2515300b40SClément Léger 
2615300b40SClément Léger #if CFG_ATMEL_PM_SUSPEND_MODE < AT91_PM_STANDBY || \
2715300b40SClément Léger 	CFG_ATMEL_PM_SUSPEND_MODE > AT91_PM_BACKUP
2815300b40SClément Léger #error Invalid suspend mode, please check CFG_ATMEL_PM_SUSPEND_MODE
2915300b40SClément Léger #endif
3015300b40SClément Léger 
3115300b40SClément Léger #define AT91_SECUMOD_SYSR		0x04
3215300b40SClément Léger #define AT91_SECUMOD_RAMRDY		0x14
3315300b40SClément Léger #define AT91_SECUMOD_RAMRDY_READY	BIT(0)
3415300b40SClément Léger 
3515300b40SClément Léger static struct at91_pm_data soc_pm;
3615300b40SClément Léger 
3715300b40SClément Léger /* Backup canary */
3815300b40SClément Léger static uint32_t canary = 0xA5A5A5A5;
3915300b40SClément Léger 
4015300b40SClément Léger /* Backup mode information used by at91bootstrap */
4115300b40SClément Léger static struct at91bootstrap_bu {
4215300b40SClément Léger 	uint32_t suspended;
4315300b40SClément Léger 	uint32_t reserved;
4415300b40SClément Léger 	uint32_t *canary;
4515300b40SClément Léger 	uint32_t resume;
4615300b40SClément Léger } *at91bootstrap_bu;
4715300b40SClément Léger 
4815300b40SClément Léger static vaddr_t at91_suspend_sram_base;
4915300b40SClément Léger static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
5015300b40SClément Léger 
at91_pm_set_suspend_mode(struct thread_smc_args * args)51f8c3938bSClément Léger enum sm_handler_ret at91_pm_set_suspend_mode(struct thread_smc_args *args)
52f8c3938bSClément Léger {
53f8c3938bSClément Léger 	unsigned int mode = args->a1;
54f8c3938bSClément Léger 
55f8c3938bSClément Léger 	/*
56f8c3938bSClément Léger 	 * We don't expect this function to be called simultaneously while we
57f8c3938bSClément Léger 	 * are entering suspend/resume function. On sama5d2, this is not a
58f8c3938bSClément Léger 	 * problem since this SoC is a single core one but in order to prevent
59f8c3938bSClément Léger 	 * any other SoC support to be added without handling this concurrency,
60f8c3938bSClément Léger 	 * check that we are compiled for a single core.
61f8c3938bSClément Léger 	 */
62f8c3938bSClément Léger 	COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
63f8c3938bSClément Léger 
64f8c3938bSClément Léger 	if (mode > AT91_PM_BACKUP) {
65f8c3938bSClément Léger 		args->a0 = SAMA5_SMC_SIP_RETURN_EINVAL;
66f8c3938bSClément Léger 		return SM_HANDLER_SMC_HANDLED;
67f8c3938bSClément Léger 	}
68f8c3938bSClément Léger 	DMSG("Setting suspend mode to %u", mode);
69f8c3938bSClément Léger 
70f8c3938bSClément Léger 	args->a0 = SAMA5_SMC_SIP_RETURN_SUCCESS;
71f8c3938bSClément Léger 	soc_pm.mode = mode;
72f8c3938bSClément Léger 
73f8c3938bSClément Léger 	return SM_HANDLER_SMC_HANDLED;
74f8c3938bSClément Léger }
75f8c3938bSClément Léger 
at91_pm_get_suspend_mode(struct thread_smc_args * args)76f8c3938bSClément Léger enum sm_handler_ret at91_pm_get_suspend_mode(struct thread_smc_args *args)
77f8c3938bSClément Léger {
78f8c3938bSClément Léger 	args->a1 = soc_pm.mode;
79f8c3938bSClément Léger 	args->a0 = SAMA5_SMC_SIP_RETURN_SUCCESS;
80f8c3938bSClément Léger 
81f8c3938bSClément Léger 	return SM_HANDLER_SMC_HANDLED;
82f8c3938bSClément Léger }
83f8c3938bSClément Léger 
at91_pm_copy_suspend_to_sram(void)8415300b40SClément Léger static void at91_pm_copy_suspend_to_sram(void)
8515300b40SClément Léger {
8615300b40SClément Léger 	memcpy((void *)at91_suspend_sram_base, &at91_pm_suspend_in_sram,
8715300b40SClément Léger 	       at91_pm_suspend_in_sram_sz);
8815300b40SClément Léger 
8915300b40SClément Léger 	cache_op_inner(DCACHE_AREA_CLEAN, (void *)at91_suspend_sram_base,
9015300b40SClément Léger 		       at91_pm_suspend_in_sram_sz);
9115300b40SClément Léger 	cache_op_inner(ICACHE_AREA_INVALIDATE, at91_suspend_sram_fn,
9215300b40SClément Léger 		       at91_pm_suspend_in_sram_sz);
9315300b40SClément Léger }
9415300b40SClément Léger 
atmel_pm_cpu_idle(void)9515300b40SClément Léger void atmel_pm_cpu_idle(void)
9615300b40SClément Léger {
9715300b40SClément Léger 	uint32_t lpr0 = 0;
9815300b40SClément Léger 	uint32_t saved_lpr0 = 0;
9915300b40SClément Léger 
10015300b40SClément Léger 	saved_lpr0 = io_read32(soc_pm.ramc + AT91_DDRSDRC_LPR);
10115300b40SClément Léger 	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
10215300b40SClément Léger 	lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;
10315300b40SClément Léger 
10415300b40SClément Léger 	io_write32(soc_pm.ramc + AT91_DDRSDRC_LPR, lpr0);
10515300b40SClément Léger 
10615300b40SClément Léger 	cpu_idle();
10715300b40SClément Léger 
10815300b40SClément Léger 	io_write32(soc_pm.ramc + AT91_DDRSDRC_LPR, saved_lpr0);
10915300b40SClément Léger }
11015300b40SClément Léger 
at91_sam_config_shdwc_ws(vaddr_t shdwc,uint32_t * mode,uint32_t * polarity)1119a974572STony Han static void at91_sam_config_shdwc_ws(vaddr_t shdwc, uint32_t *mode,
11215300b40SClément Léger 				     uint32_t *polarity)
11315300b40SClément Léger {
11415300b40SClément Léger 	uint32_t val = 0;
11515300b40SClément Léger 
11615300b40SClément Léger 	/* SHDWC.WUIR */
11715300b40SClément Léger 	val = io_read32(shdwc + AT91_SHDW_WUIR);
11815300b40SClément Léger 	*mode |= val & AT91_SHDW_WKUPEN_MASK;
11915300b40SClément Léger 	*polarity |= (val >> AT91_SHDW_WKUPT_SHIFT) & AT91_SHDW_WKUPT_MASK;
12015300b40SClément Léger }
12115300b40SClément Léger 
at91_sam_config_pmc_ws(vaddr_t pmc,uint32_t mode,uint32_t polarity)1229a974572STony Han static int at91_sam_config_pmc_ws(vaddr_t pmc, uint32_t mode, uint32_t polarity)
12315300b40SClément Léger {
12415300b40SClément Léger 	io_write32(pmc + AT91_PMC_FSMR, mode);
1259a974572STony Han 	if (IS_ENABLED(CFG_SAMA5D2))
12615300b40SClément Léger 		io_write32(pmc + AT91_PMC_FSPR, polarity);
12715300b40SClément Léger 
12815300b40SClément Léger 	return 0;
12915300b40SClément Léger }
13015300b40SClément Léger 
13115300b40SClément Léger struct wakeup_source_info {
13215300b40SClément Léger 	unsigned int pmc_fsmr_bit;
13315300b40SClément Léger 	unsigned int shdwc_mr_bit;
13415300b40SClément Léger 	bool set_polarity;
13515300b40SClément Léger };
13615300b40SClément Léger 
13715300b40SClément Léger static const struct wakeup_source_info ws_info[] = {
13815300b40SClément Léger 	{ .pmc_fsmr_bit = AT91_PMC_FSTT(10),	.set_polarity = true },
13915300b40SClément Léger 	{ .pmc_fsmr_bit = AT91_PMC_RTCAL,	.shdwc_mr_bit = BIT(17) },
14015300b40SClément Léger 	{ .pmc_fsmr_bit = AT91_PMC_USBAL },
14115300b40SClément Léger 	{ .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
1429a974572STony Han 	{ .pmc_fsmr_bit = AT91_PMC_RTTAL },
1439a974572STony Han 	{ .pmc_fsmr_bit = AT91_PMC_RXLP_MCE },
14415300b40SClément Léger };
14515300b40SClément Léger 
14615300b40SClément Léger struct wakeup_src {
14715300b40SClément Léger 	const char *compatible;
14815300b40SClément Léger 	const struct wakeup_source_info *info;
14915300b40SClément Léger };
15015300b40SClément Léger 
1519a974572STony Han static const struct wakeup_src sam_ws_ids[] = {
1529a974572STony Han #ifdef CFG_SAMA5D2
15315300b40SClément Léger 	{ .compatible = "atmel,sama5d2-gem",		.info = &ws_info[0] },
15415300b40SClément Léger 	{ .compatible = "atmel,at91rm9200-rtc",		.info = &ws_info[1] },
15515300b40SClément Léger 	{ .compatible = "atmel,sama5d3-udc",		.info = &ws_info[2] },
15615300b40SClément Léger 	{ .compatible = "atmel,at91rm9200-ohci",	.info = &ws_info[2] },
15715300b40SClément Léger 	{ .compatible = "usb-ohci",			.info = &ws_info[2] },
15815300b40SClément Léger 	{ .compatible = "atmel,at91sam9g45-ehci",	.info = &ws_info[2] },
15915300b40SClément Léger 	{ .compatible = "usb-ehci",			.info = &ws_info[2] },
16015300b40SClément Léger 	{ .compatible = "atmel,sama5d2-sdhci",		.info = &ws_info[3] }
1619a974572STony Han #endif
1629a974572STony Han #ifdef CFG_SAMA7G5
1639a974572STony Han 	{ .compatible = "microchip,sama7g5-rtc",	.info = &ws_info[1] },
1649a974572STony Han 	{ .compatible = "microchip,sama7g5-ohci",	.info = &ws_info[2] },
1659a974572STony Han 	{ .compatible = "usb-ohci",			.info = &ws_info[2] },
1669a974572STony Han 	{ .compatible = "atmel,at91sam9g45-ehci",	.info = &ws_info[2] },
1679a974572STony Han 	{ .compatible = "usb-ehci",			.info = &ws_info[2] },
1689a974572STony Han 	{ .compatible = "microchip,sama7g5-sdhci",	.info = &ws_info[3] },
1699a974572STony Han 	{ .compatible = "microchip,sama7g5-rtt",	.info = &ws_info[4] },
1709a974572STony Han #endif
17115300b40SClément Léger };
17215300b40SClément Léger 
dev_is_wakeup_source(const void * fdt,int node)17315300b40SClément Léger static bool dev_is_wakeup_source(const void *fdt, int node)
17415300b40SClément Léger {
17515300b40SClément Léger 	return fdt_get_property(fdt, node, "wakeup-source", NULL);
17615300b40SClément Léger }
17715300b40SClément Léger 
at91_pm_config_ws_ulp1(bool set)17815300b40SClément Léger static int at91_pm_config_ws_ulp1(bool set)
17915300b40SClément Léger {
18015300b40SClément Léger 	const struct wakeup_source_info *wsi = NULL;
18115300b40SClément Léger 	const struct wakeup_src *wsrc = NULL;
18215300b40SClément Léger 	unsigned int polarity = 0;
18315300b40SClément Léger 	unsigned int mode = 0;
18415300b40SClément Léger 	unsigned int val = 0;
18515300b40SClément Léger 	unsigned int src = 0;
18615300b40SClément Léger 	int node = 0;
18715300b40SClément Léger 
18815300b40SClément Léger 	if (!set) {
18915300b40SClément Léger 		io_write32(soc_pm.pmc + AT91_PMC_FSMR, mode);
19015300b40SClément Léger 		return TEE_SUCCESS;
19115300b40SClément Léger 	}
19215300b40SClément Léger 
1939a974572STony Han 	at91_sam_config_shdwc_ws(soc_pm.shdwc, &mode, &polarity);
19415300b40SClément Léger 
19515300b40SClément Léger 	val = io_read32(soc_pm.shdwc + AT91_SHDW_MR);
19615300b40SClément Léger 
19715300b40SClément Léger 	/* Loop through defined wakeup sources. */
1989a974572STony Han 	for (src = 0; src < ARRAY_SIZE(sam_ws_ids); src++) {
1999a974572STony Han 		wsrc = &sam_ws_ids[src];
20015300b40SClément Léger 		wsi = wsrc->info;
20115300b40SClément Léger 
20215300b40SClément Léger 		node = fdt_node_offset_by_compatible(soc_pm.fdt, -1,
20315300b40SClément Léger 						     wsrc->compatible);
20415300b40SClément Léger 		while (node >= 0) {
20515300b40SClément Léger 			if (dev_is_wakeup_source(soc_pm.fdt, node)) {
20615300b40SClément Léger 				/* Check if enabled on SHDWC. */
20715300b40SClément Léger 				if (wsi->shdwc_mr_bit &&
20815300b40SClément Léger 				    !(val & wsi->shdwc_mr_bit))
20915300b40SClément Léger 					goto next_node;
21015300b40SClément Léger 
21115300b40SClément Léger 				mode |= wsi->pmc_fsmr_bit;
21215300b40SClément Léger 				if (wsi->set_polarity)
21315300b40SClément Léger 					polarity |= wsi->pmc_fsmr_bit;
21415300b40SClément Léger 			}
21515300b40SClément Léger next_node:
21615300b40SClément Léger 			node = fdt_node_offset_by_compatible(soc_pm.fdt, node,
21715300b40SClément Léger 							     wsrc->compatible);
21815300b40SClément Léger 		}
21915300b40SClément Léger 	}
22015300b40SClément Léger 
22115300b40SClément Léger 	if (!mode) {
22215300b40SClément Léger 		EMSG("AT91: PM: no ULP1 wakeup sources found!");
22315300b40SClément Léger 		return TEE_ERROR_BAD_STATE;
22415300b40SClément Léger 	}
22515300b40SClément Léger 
2269a974572STony Han 	at91_sam_config_pmc_ws(soc_pm.pmc, mode, polarity);
22715300b40SClément Léger 
22815300b40SClément Léger 	return TEE_SUCCESS;
22915300b40SClément Léger }
23015300b40SClément Léger 
23115300b40SClément Léger /*
23215300b40SClément Léger  * Verify that all the clocks are correct before entering
23315300b40SClément Léger  * slow-clock mode.
23415300b40SClément Léger  */
at91_pm_verify_clocks(void)23515300b40SClément Léger static bool at91_pm_verify_clocks(void)
23615300b40SClément Léger {
23715300b40SClément Léger 	int i = 0;
23815300b40SClément Léger 	uint32_t scsr = 0;
23915300b40SClément Léger 
24015300b40SClément Léger 	scsr = io_read32(soc_pm.pmc + AT91_PMC_SCSR);
24115300b40SClément Léger 
24215300b40SClément Léger 	/* USB must not be using PLLB */
24315300b40SClément Léger 	if ((scsr & (AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP)) != 0) {
24415300b40SClément Léger 		EMSG("AT91: PM - Suspend-to-RAM with USB still active");
24515300b40SClément Léger 		return false;
24615300b40SClément Léger 	}
24715300b40SClément Léger 
2486d792c58STony Han 	/* PCK0..PCKx must be disabled, or configured to use clk32k */
2496d792c58STony Han 	for (i = 0; i < AT91_PMC_PCK_COUNT; i++) {
25015300b40SClément Léger 		uint32_t css = 0;
25115300b40SClément Léger 
25215300b40SClément Léger 		if ((scsr & (AT91_PMC_PCK0 << i)) == 0)
25315300b40SClément Léger 			continue;
25415300b40SClément Léger 		css = io_read32(soc_pm.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS;
25515300b40SClément Léger 		if (css != AT91_PMC_CSS_SLOW) {
25615300b40SClément Léger 			EMSG("AT91: PM - Suspend-to-RAM with PCK%d src %"PRId32,
25715300b40SClément Léger 			     i, css);
25815300b40SClément Léger 			return false;
25915300b40SClément Léger 		}
26015300b40SClément Léger 	}
26115300b40SClément Léger 
26215300b40SClément Léger 	return true;
26315300b40SClément Léger }
26415300b40SClément Léger 
at91_write_backup_data(void)26515300b40SClément Léger static TEE_Result at91_write_backup_data(void)
26615300b40SClément Léger {
26715300b40SClément Léger 	uint32_t val = 0;
26815300b40SClément Léger 
26915300b40SClément Léger 	while (true) {
27015300b40SClément Léger 		val = io_read32(soc_pm.secumod + AT91_SECUMOD_RAMRDY);
27115300b40SClément Léger 		if (val & AT91_SECUMOD_RAMRDY_READY)
27215300b40SClément Léger 			break;
27315300b40SClément Léger 	}
27415300b40SClément Léger 
27515300b40SClément Léger 	io_write32((vaddr_t)&at91bootstrap_bu->suspended, 1);
27615300b40SClément Léger 	io_write32((vaddr_t)&at91bootstrap_bu->canary, virt_to_phys(&canary));
27715300b40SClément Léger 	io_write32((vaddr_t)&at91bootstrap_bu->resume,
27815300b40SClément Léger 		   virt_to_phys((void *)(vaddr_t)at91_pm_cpu_resume));
27915300b40SClément Léger 
28015300b40SClément Léger 	return TEE_SUCCESS;
28115300b40SClément Léger }
28215300b40SClément Léger 
at91_pm_change_state(enum pm_op op)283*7a639aedSThomas Perrot static void at91_pm_change_state(enum pm_op op)
284*7a639aedSThomas Perrot {
285*7a639aedSThomas Perrot 	int type = 0;
286*7a639aedSThomas Perrot 	uint32_t hint = 0;
287*7a639aedSThomas Perrot 
288*7a639aedSThomas Perrot 	if (soc_pm.mode == AT91_PM_STANDBY)
289*7a639aedSThomas Perrot 		type = PM_SUSPEND_STANDBY;
290*7a639aedSThomas Perrot 	else
291*7a639aedSThomas Perrot 		type = PM_SUSPEND_TO_MEM;
292*7a639aedSThomas Perrot 
293*7a639aedSThomas Perrot 	hint = SHIFT_U32(type, PM_HINT_SUSPEND_TYPE_SHIFT);
294*7a639aedSThomas Perrot 
295*7a639aedSThomas Perrot 	pm_change_state(op, hint);
296*7a639aedSThomas Perrot }
297*7a639aedSThomas Perrot 
at91_enter_backup(void)29815300b40SClément Léger static TEE_Result at91_enter_backup(void)
29915300b40SClément Léger {
30015300b40SClément Léger 	int ret = -1;
30115300b40SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
30215300b40SClément Léger 
30315300b40SClément Léger 	res = at91_write_backup_data();
30415300b40SClément Léger 	if (res)
30515300b40SClément Léger 		return res;
30615300b40SClément Léger 
307*7a639aedSThomas Perrot 	at91_pm_change_state(PM_OP_SUSPEND);
30815300b40SClément Léger 	ret = sm_pm_cpu_suspend((uint32_t)&soc_pm,
30915300b40SClément Léger 				(void *)at91_suspend_sram_fn);
31015300b40SClément Léger 	if (ret < 0) {
31115300b40SClément Léger 		DMSG("Suspend failed");
31215300b40SClément Léger 		res = TEE_ERROR_BAD_STATE;
31315300b40SClément Léger 	} else {
31415300b40SClément Léger 		res = TEE_SUCCESS;
31515300b40SClément Léger 	}
31615300b40SClément Léger 
317*7a639aedSThomas Perrot 	at91_pm_change_state(PM_OP_RESUME);
31815300b40SClément Léger 	if (res)
31915300b40SClément Léger 		return res;
32015300b40SClément Léger 
32115300b40SClément Léger 	/* SRAM content is lost after resume */
32215300b40SClément Léger 	at91_pm_copy_suspend_to_sram();
32315300b40SClément Léger 
32415300b40SClément Léger 	return TEE_SUCCESS;
32515300b40SClément Léger }
32615300b40SClément Léger 
atmel_pm_suspend(uintptr_t entry,struct sm_nsec_ctx * nsec)32715300b40SClément Léger TEE_Result atmel_pm_suspend(uintptr_t entry, struct sm_nsec_ctx *nsec)
32815300b40SClément Léger {
32915300b40SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
33067aac8e6STony Han 	uint32_t sctlr = 0;
33115300b40SClément Léger 
33215300b40SClément Léger 	DMSG("Entering suspend mode %d", soc_pm.mode);
33315300b40SClément Léger 
33415300b40SClément Léger 	if (soc_pm.mode >= AT91_PM_ULP0) {
33515300b40SClément Léger 		if (!at91_pm_verify_clocks())
33615300b40SClément Léger 			return TEE_ERROR_BAD_STATE;
33715300b40SClément Léger 	}
33815300b40SClément Léger 
33915300b40SClément Léger 	if (soc_pm.mode == AT91_PM_ULP1)
34015300b40SClément Léger 		at91_pm_config_ws_ulp1(true);
34115300b40SClément Léger 
34215300b40SClément Léger 	sm_save_unbanked_regs(&nsec->ub_regs);
34315300b40SClément Léger 
34467aac8e6STony Han 	/*
34567aac8e6STony Han 	 * In order to run code for low-power out of SRAM without abort,
34667aac8e6STony Han 	 * configure regions with write permission with not forced to
34767aac8e6STony Han 	 * XN (Execute-never) attribute.
34867aac8e6STony Han 	 */
34967aac8e6STony Han 	if (IS_ENABLED(CFG_HWSUPP_MEM_PERM_WXN)) {
35067aac8e6STony Han 		sctlr = read_sctlr();
35167aac8e6STony Han 		if (sctlr & SCTLR_WXN) {
35267aac8e6STony Han 			write_sctlr(sctlr & ~SCTLR_WXN);
35367aac8e6STony Han 			tlbi_all();
35467aac8e6STony Han 		}
35567aac8e6STony Han 	}
35667aac8e6STony Han 
35715300b40SClément Léger 	if (soc_pm.mode == AT91_PM_BACKUP) {
35815300b40SClément Léger 		res = at91_enter_backup();
35915300b40SClément Léger 	} else {
36015300b40SClément Léger 		at91_suspend_sram_fn(&soc_pm);
36115300b40SClément Léger 		res = TEE_SUCCESS;
36215300b40SClément Léger 	}
36315300b40SClément Léger 
36467aac8e6STony Han 	/* Restore the XN attribute */
36567aac8e6STony Han 	if (IS_ENABLED(CFG_HWSUPP_MEM_PERM_WXN)) {
36667aac8e6STony Han 		if (sctlr & SCTLR_WXN) {
36767aac8e6STony Han 			write_sctlr(sctlr);
36867aac8e6STony Han 			tlbi_all();
36967aac8e6STony Han 		}
37067aac8e6STony Han 	}
37167aac8e6STony Han 
37215300b40SClément Léger 	if (soc_pm.mode == AT91_PM_ULP1)
37315300b40SClément Léger 		at91_pm_config_ws_ulp1(false);
37415300b40SClément Léger 
37515300b40SClément Léger 	sm_restore_unbanked_regs(&nsec->ub_regs);
37615300b40SClément Léger 
37715300b40SClément Léger 	/*
37815300b40SClément Léger 	 * If the system went to backup mode, register state was lost and must
37915300b40SClément Léger 	 * be restored by jumping to the user provided entry point
38015300b40SClément Léger 	 */
38115300b40SClément Léger 	if (res == TEE_SUCCESS && soc_pm.mode == AT91_PM_BACKUP)
38215300b40SClément Léger 		nsec->mon_lr = entry;
38315300b40SClément Léger 
38415300b40SClément Léger 	DMSG("Exiting suspend mode %d, res %"PRIx32, soc_pm.mode, res);
38515300b40SClément Léger 
38615300b40SClément Léger 	return res;
38715300b40SClément Léger }
38815300b40SClément Léger 
at91_pm_dt_dram_init(const void * fdt)38915300b40SClément Léger static TEE_Result at91_pm_dt_dram_init(const void *fdt)
39015300b40SClément Léger {
39146f0e733STony Han 	const struct {
39246f0e733STony Han 		const char *compatible;
39346f0e733STony Han 		vaddr_t *address;
39446f0e733STony Han 	} dram_map[] = {
39546f0e733STony Han #ifdef CFG_SAMA5D2
39646f0e733STony Han 		{
39746f0e733STony Han 			.compatible = "atmel,sama5d3-ddramc",
39846f0e733STony Han 			.address = &soc_pm.ramc,
39946f0e733STony Han 		},
40046f0e733STony Han #endif
40146f0e733STony Han #ifdef CFG_SAMA7G5
40246f0e733STony Han 		{
40346f0e733STony Han 			.compatible = "microchip,sama7g5-uddrc",
40446f0e733STony Han 			.address = &soc_pm.ramc,
40546f0e733STony Han 		},
40646f0e733STony Han 		{
40746f0e733STony Han 			.compatible = "microchip,sama7g5-ddr3phy",
40846f0e733STony Han 			.address = &soc_pm.ramc_phy,
40946f0e733STony Han 		},
41046f0e733STony Han #endif
41146f0e733STony Han 	};
41246f0e733STony Han 	uint32_t i = 0;
41315300b40SClément Léger 	int node = -1;
41415300b40SClément Léger 	size_t size = 0;
41515300b40SClément Léger 
41646f0e733STony Han 	for (i = 0; i < ARRAY_SIZE(dram_map); i++) {
41746f0e733STony Han 		node = fdt_node_offset_by_compatible(fdt, -1,
41846f0e733STony Han 						     dram_map[i].compatible);
41946f0e733STony Han 
42015300b40SClément Léger 		if (node < 0)
42115300b40SClément Léger 			return TEE_ERROR_ITEM_NOT_FOUND;
42215300b40SClément Léger 
42346f0e733STony Han 		if (dt_map_dev(fdt, node,
42446f0e733STony Han 			       dram_map[i].address, &size, DT_MAP_AUTO) < 0)
42515300b40SClément Léger 			return TEE_ERROR_GENERIC;
42646f0e733STony Han 	}
42715300b40SClément Léger 
42815300b40SClément Léger 	return TEE_SUCCESS;
42915300b40SClément Léger }
43015300b40SClément Léger 
at91_pm_backup_init(const void * fdt)43115300b40SClément Léger static TEE_Result at91_pm_backup_init(const void *fdt)
43215300b40SClément Léger {
4336e4bc5d9STony Han 	enum dt_map_dev_directive mapping = DT_MAP_AUTO;
43415300b40SClément Léger 	int node = -1;
43515300b40SClément Léger 	size_t size = 0;
43615300b40SClément Léger 
43715300b40SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-sfrbu");
43815300b40SClément Léger 	if (node < 0)
43915300b40SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
44015300b40SClément Léger 
4416e4bc5d9STony Han 	if (IS_ENABLED(CFG_SAMA7G5))
4426e4bc5d9STony Han 		mapping = DT_MAP_SECURE;
4436e4bc5d9STony Han 
4446e4bc5d9STony Han 	if (dt_map_dev(fdt, node, &soc_pm.sfrbu, &size, mapping) < 0)
44515300b40SClément Léger 		return TEE_ERROR_GENERIC;
44615300b40SClément Léger 
447f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) == DT_STATUS_OK_SEC)
4486e4bc5d9STony Han 		/* for SAMA7G5 SFRBU is always secured, no need to configre */
4496e4bc5d9STony Han 		if (!IS_ENABLED(CFG_SAMA7G5))
45015300b40SClément Léger 			matrix_configure_periph_secure(AT91C_ID_SFRBU);
45115300b40SClément Léger 
45215300b40SClément Léger 	return TEE_SUCCESS;
45315300b40SClément Léger }
45415300b40SClément Léger 
at91_pm_sram_init(const void * fdt)45515300b40SClément Léger static TEE_Result at91_pm_sram_init(const void *fdt)
45615300b40SClément Léger {
45715300b40SClément Léger 	int node = -1;
45815300b40SClément Léger 	size_t size = 0;
45915300b40SClément Léger 	paddr_t at91_suspend_sram_pbase;
46015300b40SClément Léger 	size_t suspend_sz = at91_pm_suspend_in_sram_sz;
46115300b40SClément Léger 
46215300b40SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-sram");
46315300b40SClément Léger 	if (node < 0)
46415300b40SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
46515300b40SClément Léger 
466f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
46715300b40SClément Léger 		return TEE_ERROR_GENERIC;
46815300b40SClément Léger 
469a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &at91_suspend_sram_base, &size,
470a5d5bbc8SVesa Jääskeläinen 		       DT_MAP_AUTO) < 0)
47115300b40SClément Léger 		return TEE_ERROR_GENERIC;
47215300b40SClément Léger 
47315300b40SClément Léger 	at91_suspend_sram_pbase = virt_to_phys((void *)at91_suspend_sram_base);
47415300b40SClément Léger 
47522b10ee0STony Han 	/*
47622b10ee0STony Han 	 * Map the secure ram suspend code with the memory area type
47722b10ee0STony Han 	 * "MEM_AREA_TEE_COHERENT" to make it non-cacheable.
47822b10ee0STony Han 	 * Mapping with memory area type "MEM_AREA_TEE_RAM" would enable
47922b10ee0STony Han 	 * cacheable attribute and might cause abort in some cases.
48022b10ee0STony Han 	 */
48122b10ee0STony Han 	at91_suspend_sram_fn = core_mmu_add_mapping(MEM_AREA_TEE_COHERENT,
48215300b40SClément Léger 						    at91_suspend_sram_pbase,
48315300b40SClément Léger 						    suspend_sz);
48415300b40SClément Léger 	if (!at91_suspend_sram_fn) {
48515300b40SClément Léger 		EMSG("Failed to remap sram as executable");
48615300b40SClément Léger 		return TEE_ERROR_GENERIC;
48715300b40SClément Léger 	}
48815300b40SClément Léger 
48915300b40SClément Léger 	at91_pm_copy_suspend_to_sram();
49015300b40SClément Léger 
49115300b40SClément Léger 	return TEE_SUCCESS;
49215300b40SClément Léger }
49315300b40SClément Léger 
at91_securam_init(const void * fdt)49415300b40SClément Léger static TEE_Result at91_securam_init(const void *fdt)
49515300b40SClément Léger {
49615300b40SClément Léger 	int node = -1;
49715300b40SClément Léger 	size_t size = 0;
49815300b40SClément Léger 	struct clk *clk = NULL;
49915300b40SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
50015300b40SClément Léger 
50115300b40SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-securam");
50215300b40SClément Léger 	if (node < 0)
50315300b40SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
50415300b40SClément Léger 
505f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
50615300b40SClément Léger 		return TEE_ERROR_GENERIC;
50715300b40SClément Léger 
508a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &soc_pm.securam, &size, DT_MAP_AUTO) < 0)
50915300b40SClément Léger 		return TEE_ERROR_GENERIC;
51015300b40SClément Léger 
51115300b40SClément Léger 	res = clk_dt_get_by_index(fdt, node, 0, &clk);
51215300b40SClément Léger 	if (res)
51315300b40SClément Léger 		return res;
51415300b40SClément Léger 
51515300b40SClément Léger 	if (clk_enable(clk))
51615300b40SClément Léger 		return TEE_ERROR_GENERIC;
51715300b40SClément Léger 
51815300b40SClément Léger 	if (size < sizeof(struct at91bootstrap_bu))
51915300b40SClément Léger 		return TEE_ERROR_SHORT_BUFFER;
52015300b40SClément Léger 
52115300b40SClément Léger 	at91bootstrap_bu = (void *)soc_pm.securam;
52215300b40SClément Léger 
52315300b40SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-secumod");
52415300b40SClément Léger 	if (node < 0)
52515300b40SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
52615300b40SClément Léger 
527f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
52815300b40SClément Léger 		return TEE_ERROR_GENERIC;
52915300b40SClément Léger 
530a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &soc_pm.secumod, &size, DT_MAP_AUTO) < 0)
53115300b40SClément Léger 		return TEE_ERROR_GENERIC;
53215300b40SClément Léger 
53315300b40SClément Léger 	return TEE_SUCCESS;
53415300b40SClément Léger }
53515300b40SClément Léger 
sam_pm_init_all(const void * fdt,vaddr_t shdwc)53695acfb12STony Han static TEE_Result sam_pm_init_all(const void *fdt, vaddr_t shdwc)
53715300b40SClément Léger {
53815300b40SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
53915300b40SClément Léger 
54015300b40SClément Léger 	soc_pm.fdt = fdt;
54115300b40SClément Léger 	soc_pm.shdwc = shdwc;
54215300b40SClément Léger 	soc_pm.pmc = at91_pmc_get_base();
54315300b40SClément Léger 	if (!soc_pm.pmc)
54415300b40SClément Léger 		return TEE_ERROR_GENERIC;
54515300b40SClément Léger 
54615300b40SClément Léger 	soc_pm.mode = CFG_ATMEL_PM_SUSPEND_MODE;
54715300b40SClément Léger 
54815300b40SClément Léger 	res = at91_securam_init(fdt);
54915300b40SClément Léger 	if (res)
55015300b40SClément Léger 		return res;
55115300b40SClément Léger 
55215300b40SClément Léger 	res = at91_pm_dt_dram_init(fdt);
55315300b40SClément Léger 	if (res)
55415300b40SClément Léger 		return res;
55515300b40SClément Léger 
55615300b40SClément Léger 	res = at91_pm_backup_init(fdt);
55715300b40SClément Léger 	if (res)
55815300b40SClément Léger 		return res;
55915300b40SClément Léger 
56015300b40SClément Léger 	res = at91_pm_sram_init(fdt);
56115300b40SClément Léger 	if (res)
56215300b40SClément Léger 		return res;
56315300b40SClément Léger 
56415300b40SClément Léger 	return TEE_SUCCESS;
56515300b40SClément Léger }
56615300b40SClément Léger 
sam_pm_init(const void * fdt,vaddr_t shdwc)56795acfb12STony Han TEE_Result sam_pm_init(const void *fdt, vaddr_t shdwc)
56815300b40SClément Léger {
56995acfb12STony Han 	if (sam_pm_init_all(fdt, shdwc))
57095acfb12STony Han 		panic("Failed to setup PM for this MPU");
57115300b40SClément Léger 
57215300b40SClément Léger 	return TEE_SUCCESS;
57315300b40SClément Léger }
574