xref: /optee_os/core/drivers/atmel_shdwc.c (revision 95acfb12f3e7928ad08a864db1a45f7e3fd19e0e)
158200af7SClément Léger // SPDX-License-Identifier: BSD-2-Clause
258200af7SClément Léger /*
358200af7SClément Léger  * Copyright (c) 2015 Atmel Corporation,
458200af7SClément Léger  *                    Nicolas Ferre <nicolas.ferre@atmel.com>
558200af7SClément Léger  * Copyright (c) 2021, Microchip
658200af7SClément Léger  */
758200af7SClément Léger 
858200af7SClément Léger #include <drivers/atmel_shdwc.h>
958200af7SClément Léger #include <drivers/sam/at91_ddr.h>
10d031d1ecSClément Léger #include <drivers/pm/sam/atmel_pm.h>
1158200af7SClément Léger #include <io.h>
1258200af7SClément Léger #include <kernel/dt.h>
139e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
1458200af7SClément Léger #include <kernel/thread.h>
1558200af7SClément Léger #include <libfdt.h>
16c2daaa37SClément Léger #include <matrix.h>
176370f75dSTony Han #include <platform_config.h>
1858200af7SClément Léger #include <stdbool.h>
1958200af7SClément Léger #include <tee_api_defines.h>
2058200af7SClément Léger #include <tee_api_types.h>
2158200af7SClément Léger #include <trace.h>
2258200af7SClément Léger #include <types_ext.h>
2358200af7SClément Léger #include <util.h>
2458200af7SClément Léger 
2558200af7SClément Léger #include "at91_clk.h"
2658200af7SClément Léger 
2758200af7SClément Léger #define SHDW_WK_PIN(reg, cfg)	((reg) & \
2858200af7SClément Léger 					AT91_SHDW_WKUPIS((cfg)->wkup_pin_input))
2958200af7SClément Léger #define SHDW_RTCWK(reg, cfg)	(((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1)
3058200af7SClément Léger #define SHDW_RTTWK(reg, cfg)	(((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1)
3158200af7SClément Léger #define SHDW_RTCWKEN(cfg)	BIT((cfg)->mr_rtcwk_shift)
3258200af7SClément Léger #define SHDW_RTTWKEN(cfg)	BIT((cfg)->mr_rttwk_shift)
3358200af7SClément Léger 
3458200af7SClément Léger #define SLOW_CLK_FREQ		32768ULL
3558200af7SClément Léger #define DBC_PERIOD_US(x)	DIV_ROUND_UP((1000000ULL * (x)), SLOW_CLK_FREQ)
3658200af7SClément Léger 
37f527a3b7STony Han /*
38f527a3b7STony Han  * @type_offset	offset of Memory Device Register
39f527a3b7STony Han  * @type_mask	mask of Memory Device Type in Memory Device Register
40a991d533STony Han  * @compatible	the compatible string in the device tree
41f527a3b7STony Han  */
42f527a3b7STony Han struct ddrc_reg_config {
43f527a3b7STony Han 	uint32_t type_offset;
44f527a3b7STony Han 	uint32_t type_mask;
45a991d533STony Han 	const char *compatible;
46f527a3b7STony Han };
47f527a3b7STony Han 
48f527a3b7STony Han /*
49f527a3b7STony Han  * @shdwc_always_secure	Is peripheral SHDWC always secured?
50f527a3b7STony Han  * @ddrc		DDR controller configurations
51f527a3b7STony Han  */
52f527a3b7STony Han struct shdwc_compat {
53f527a3b7STony Han 	bool shdwc_always_secure;
54f527a3b7STony Han 	struct ddrc_reg_config ddrc;
55f527a3b7STony Han };
56f527a3b7STony Han 
5758200af7SClément Léger static vaddr_t shdwc_base;
5858200af7SClément Léger static vaddr_t mpddrc_base;
5958200af7SClément Léger 
atmel_shdwc_available(void)6058200af7SClément Léger bool atmel_shdwc_available(void)
6158200af7SClément Léger {
6258200af7SClément Léger 	return shdwc_base != 0;
6358200af7SClément Léger }
6458200af7SClément Léger 
atmel_shdwc_shutdown(void)6558200af7SClément Léger void __noreturn atmel_shdwc_shutdown(void)
6658200af7SClément Léger {
6758200af7SClément Léger 	vaddr_t pmc_base = at91_pmc_get_base();
6858200af7SClément Léger 
6958200af7SClément Léger 	/*
7058200af7SClément Léger 	 * Mask exception before entering assembly which does not expect to be
7158200af7SClément Léger 	 * interrupted.
7258200af7SClément Léger 	 */
7358200af7SClément Léger 	thread_mask_exceptions(THREAD_EXCP_ALL);
7458200af7SClément Léger 
7558200af7SClément Léger 	__atmel_shdwc_shutdown(mpddrc_base, shdwc_base, pmc_base);
7658200af7SClément Léger 
7758200af7SClément Léger 	/* We are going to shutdown the CPU so we will never hit this loop */
7858200af7SClément Léger 	while (true)
7958200af7SClément Léger 		;
8058200af7SClément Léger }
8158200af7SClément Léger 
8258200af7SClément Léger static const unsigned long long sdwc_dbc_period[] = {
8358200af7SClément Léger 	0, 3, 32, 512, 4096, 32768,
8458200af7SClément Léger };
8558200af7SClément Léger 
at91_shdwc_debouncer_value(uint32_t in_period_us)8658200af7SClément Léger static uint32_t at91_shdwc_debouncer_value(uint32_t in_period_us)
8758200af7SClément Léger {
8858200af7SClément Léger 	int i = 0;
8958200af7SClément Léger 	int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
9058200af7SClément Léger 	uint64_t period_us = 0;
9158200af7SClément Léger 	uint64_t max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
9258200af7SClément Léger 
9358200af7SClément Léger 	if (in_period_us > max_period_us) {
9458200af7SClément Léger 		DMSG("debouncer period %"PRIu32" too big, using %"PRIu64" us",
9558200af7SClément Léger 		     in_period_us, max_period_us);
9658200af7SClément Léger 		return max_idx;
9758200af7SClément Léger 	}
9858200af7SClément Léger 
9958200af7SClément Léger 	for (i = max_idx - 1; i > 0; i--) {
10058200af7SClément Léger 		period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
10158200af7SClément Léger 		if (in_period_us > period_us)
10258200af7SClément Léger 			break;
10358200af7SClément Léger 	}
10458200af7SClément Léger 
10558200af7SClément Léger 	return i + 1;
10658200af7SClément Léger }
10758200af7SClément Léger 
at91_shdwc_get_wakeup_input(const void * fdt,int np)10858200af7SClément Léger static uint32_t at91_shdwc_get_wakeup_input(const void *fdt, int np)
10958200af7SClément Léger {
11058200af7SClément Léger 	const uint32_t *prop = NULL;
11158200af7SClément Léger 	uint32_t wk_input_mask = 0;
11258200af7SClément Léger 	uint32_t wuir = 0;
11358200af7SClément Léger 	uint32_t wk_input = 0;
11458200af7SClément Léger 	int child = 0;
11558200af7SClément Léger 	int len = 0;
11658200af7SClément Léger 
11758200af7SClément Léger 	fdt_for_each_subnode(child, fdt, np) {
11858200af7SClément Léger 		prop = fdt_getprop(fdt, child, "reg", &len);
11958200af7SClément Léger 		if (!prop || len != sizeof(uint32_t)) {
12058200af7SClément Léger 			DMSG("reg property is missing for node %s",
12158200af7SClément Léger 			     fdt_get_name(fdt, child, NULL));
12258200af7SClément Léger 			continue;
12358200af7SClément Léger 		}
12458200af7SClément Léger 		wk_input = fdt32_to_cpu(*prop);
12558200af7SClément Léger 		wk_input_mask = BIT32(wk_input);
12658200af7SClément Léger 		if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
12758200af7SClément Léger 			DMSG("wake-up input %"PRId32" out of bounds ignore",
12858200af7SClément Léger 			     wk_input);
12958200af7SClément Léger 			continue;
13058200af7SClément Léger 		}
13158200af7SClément Léger 		wuir |= wk_input_mask;
13258200af7SClément Léger 
13358200af7SClément Léger 		if (fdt_getprop(fdt, child, "atmel,wakeup-active-high", NULL))
13458200af7SClément Léger 			wuir |= AT91_SHDW_WKUPT(wk_input);
13558200af7SClément Léger 	}
13658200af7SClément Léger 
13758200af7SClément Léger 	return wuir;
13858200af7SClément Léger }
13958200af7SClément Léger 
at91_shdwc_dt_configure(const void * fdt,int np)14058200af7SClément Léger static void at91_shdwc_dt_configure(const void *fdt, int np)
14158200af7SClément Léger {
14258200af7SClément Léger 	const uint32_t *prop = NULL;
14358200af7SClément Léger 	uint32_t mode = 0;
14458200af7SClément Léger 	uint32_t tmp = 0;
14558200af7SClément Léger 	uint32_t input = 0;
14658200af7SClément Léger 	int len = 0;
14758200af7SClément Léger 
14858200af7SClément Léger 	prop = fdt_getprop(fdt, np, "debounce-delay-us", &len);
14958200af7SClément Léger 	if (prop && len == sizeof(uint32_t)) {
15058200af7SClément Léger 		tmp = fdt32_to_cpu(*prop);
15158200af7SClément Léger 		mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(tmp));
15258200af7SClément Léger 	}
15358200af7SClément Léger 
15458200af7SClément Léger 	if (fdt_getprop(fdt, np, "atmel,wakeup-rtc-timer", &len))
15558200af7SClément Léger 		mode |= AT91_SHDW_RTCWKEN;
15658200af7SClément Léger 
15713d015f7STony Han 	if (fdt_getprop(fdt, np, "atmel,wakeup-rtt-timer", &len))
15813d015f7STony Han 		mode |= AT91_SHDW_RTTWKEN;
15913d015f7STony Han 
16058200af7SClément Léger 	io_write32(shdwc_base + AT91_SHDW_MR, mode);
16158200af7SClément Léger 
16258200af7SClément Léger 	input = at91_shdwc_get_wakeup_input(fdt, np);
16358200af7SClément Léger 	io_write32(shdwc_base + AT91_SHDW_WUIR, input);
16458200af7SClément Léger }
16558200af7SClément Léger 
atmel_shdwc_probe(const void * fdt,int node,const void * compat_data)16658200af7SClément Léger static TEE_Result atmel_shdwc_probe(const void *fdt, int node,
167f527a3b7STony Han 				    const void *compat_data)
16858200af7SClément Léger {
16958200af7SClément Léger 	int ddr_node = 0;
17058200af7SClément Léger 	size_t size = 0;
17158200af7SClément Léger 	uint32_t ddr = AT91_DDRSDRC_MD_LPDDR2;
172f527a3b7STony Han 	struct shdwc_compat *compat = (struct shdwc_compat *)compat_data;
17358200af7SClément Léger 
17458200af7SClément Léger 	/*
17558200af7SClément Léger 	 * Assembly code relies on the fact that there is only one CPU to avoid
17658200af7SClément Léger 	 * any other one to invalidate TLB/I-Cache.
17758200af7SClément Léger 	 */
17858200af7SClément Léger 	COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
17958200af7SClément Léger 
180f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
181c2daaa37SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
182c2daaa37SClément Léger 
183f527a3b7STony Han 	if (!compat->shdwc_always_secure)
184c2daaa37SClément Léger 		matrix_configure_periph_secure(AT91C_ID_SYS);
185c2daaa37SClément Léger 
186a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &shdwc_base, &size, DT_MAP_AUTO) < 0)
18758200af7SClément Léger 		return TEE_ERROR_GENERIC;
18858200af7SClément Léger 
18958200af7SClément Léger 	ddr_node = fdt_node_offset_by_compatible(fdt, -1,
190a991d533STony Han 						 compat->ddrc.compatible);
19158200af7SClément Léger 	if (ddr_node < 0)
19258200af7SClément Léger 		return TEE_ERROR_GENERIC;
19358200af7SClément Léger 
194a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, ddr_node, &mpddrc_base, &size, DT_MAP_AUTO) < 0)
19558200af7SClément Léger 		return TEE_ERROR_GENERIC;
19658200af7SClément Léger 
197a991d533STony Han 	if (!compat->ddrc.type_mask) {
198f527a3b7STony Han 		ddr = io_read32(mpddrc_base + compat->ddrc.type_offset);
199f527a3b7STony Han 		ddr &= compat->ddrc.type_mask;
200a991d533STony Han 		if (ddr != AT91_DDRSDRC_MD_LPDDR2 &&
201a991d533STony Han 		    ddr != AT91_DDRSDRC_MD_LPDDR3)
20258200af7SClément Léger 			mpddrc_base = 0;
203a991d533STony Han 	} else {
204a991d533STony Han 		/*
205a991d533STony Han 		 * Set the base to 0 as the code of DRAM controller for power
206a991d533STony Han 		 * down is not implemented yet.
207a991d533STony Han 		 */
208a991d533STony Han 		mpddrc_base = 0;
209a991d533STony Han 	}
21058200af7SClément Léger 
21158200af7SClément Léger 	at91_shdwc_dt_configure(fdt, node);
21258200af7SClément Léger 
213*95acfb12STony Han 	return sam_pm_init(fdt, shdwc_base);
21458200af7SClément Léger }
21558200af7SClément Léger 
216f527a3b7STony Han static const struct shdwc_compat sama5d2_compat = {
217f527a3b7STony Han 	.shdwc_always_secure = false,
218f527a3b7STony Han 	.ddrc = {
219f527a3b7STony Han 		.type_offset = AT91_DDRSDRC_MDR,
220f527a3b7STony Han 		.type_mask = AT91_DDRSDRC_MD,
221a991d533STony Han 		.compatible = "atmel,sama5d3-ddramc",
222f527a3b7STony Han 	}
223f527a3b7STony Han };
224f527a3b7STony Han 
225f527a3b7STony Han static const struct shdwc_compat sama7g5_compat = {
226f527a3b7STony Han 	.shdwc_always_secure = true,
227a991d533STony Han 	.ddrc = {
228a991d533STony Han 		.compatible = "microchip,sama7g5-uddrc",
229a991d533STony Han 	}
230f527a3b7STony Han };
231f527a3b7STony Han 
23258200af7SClément Léger static const struct dt_device_match atmel_shdwc_match_table[] = {
233f527a3b7STony Han 	{
234f527a3b7STony Han 		.compatible = "atmel,sama5d2-shdwc",
235f527a3b7STony Han 		.compat_data = &sama5d2_compat
236f527a3b7STony Han 	},
237f527a3b7STony Han 	{
238f527a3b7STony Han 		.compatible = "microchip,sama7g5-shdwc",
239f527a3b7STony Han 		.compat_data = &sama7g5_compat,
240f527a3b7STony Han 	},
24158200af7SClément Léger 	{ }
24258200af7SClément Léger };
24358200af7SClément Léger 
24461bdedeaSJerome Forissier DEFINE_DT_DRIVER(atmel_shdwc_dt_driver) = {
24558200af7SClément Léger 	.name = "atmel_shdwc",
24658200af7SClément Léger 	.type = DT_DRIVER_NOTYPE,
24758200af7SClément Léger 	.match_table = atmel_shdwc_match_table,
24858200af7SClément Léger 	.probe = atmel_shdwc_probe,
24958200af7SClément Léger };
250