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