xref: /optee_os/core/drivers/atmel_shdwc.c (revision f527a3b76ed8f84fe48b928229cafc4211067758)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2015 Atmel Corporation,
4  *                    Nicolas Ferre <nicolas.ferre@atmel.com>
5  * Copyright (c) 2021, Microchip
6  */
7 
8 #include <drivers/atmel_shdwc.h>
9 #include <drivers/sam/at91_ddr.h>
10 #include <drivers/pm/sam/atmel_pm.h>
11 #include <io.h>
12 #include <kernel/dt.h>
13 #include <kernel/dt_driver.h>
14 #include <kernel/thread.h>
15 #include <libfdt.h>
16 #include <matrix.h>
17 #include <platform_config.h>
18 #include <stdbool.h>
19 #include <tee_api_defines.h>
20 #include <tee_api_types.h>
21 #include <trace.h>
22 #include <types_ext.h>
23 #include <util.h>
24 
25 #include "at91_clk.h"
26 
27 #define SHDW_WK_PIN(reg, cfg)	((reg) & \
28 					AT91_SHDW_WKUPIS((cfg)->wkup_pin_input))
29 #define SHDW_RTCWK(reg, cfg)	(((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1)
30 #define SHDW_RTTWK(reg, cfg)	(((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1)
31 #define SHDW_RTCWKEN(cfg)	BIT((cfg)->mr_rtcwk_shift)
32 #define SHDW_RTTWKEN(cfg)	BIT((cfg)->mr_rttwk_shift)
33 
34 #define SLOW_CLK_FREQ		32768ULL
35 #define DBC_PERIOD_US(x)	DIV_ROUND_UP((1000000ULL * (x)), SLOW_CLK_FREQ)
36 
37 /*
38  * @type_offset	offset of Memory Device Register
39  * @type_mask	mask of Memory Device Type in Memory Device Register
40  */
41 struct ddrc_reg_config {
42 	uint32_t type_offset;
43 	uint32_t type_mask;
44 };
45 
46 /*
47  * @shdwc_always_secure	Is peripheral SHDWC always secured?
48  * @ddrc		DDR controller configurations
49  */
50 struct shdwc_compat {
51 	bool shdwc_always_secure;
52 	struct ddrc_reg_config ddrc;
53 };
54 
55 static vaddr_t shdwc_base;
56 static vaddr_t mpddrc_base;
57 
58 bool atmel_shdwc_available(void)
59 {
60 	return shdwc_base != 0;
61 }
62 
63 void __noreturn atmel_shdwc_shutdown(void)
64 {
65 	vaddr_t pmc_base = at91_pmc_get_base();
66 
67 	/*
68 	 * Mask exception before entering assembly which does not expect to be
69 	 * interrupted.
70 	 */
71 	thread_mask_exceptions(THREAD_EXCP_ALL);
72 
73 	__atmel_shdwc_shutdown(mpddrc_base, shdwc_base, pmc_base);
74 
75 	/* We are going to shutdown the CPU so we will never hit this loop */
76 	while (true)
77 		;
78 }
79 
80 static const unsigned long long sdwc_dbc_period[] = {
81 	0, 3, 32, 512, 4096, 32768,
82 };
83 
84 static uint32_t at91_shdwc_debouncer_value(uint32_t in_period_us)
85 {
86 	int i = 0;
87 	int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
88 	uint64_t period_us = 0;
89 	uint64_t max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
90 
91 	if (in_period_us > max_period_us) {
92 		DMSG("debouncer period %"PRIu32" too big, using %"PRIu64" us",
93 		     in_period_us, max_period_us);
94 		return max_idx;
95 	}
96 
97 	for (i = max_idx - 1; i > 0; i--) {
98 		period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
99 		if (in_period_us > period_us)
100 			break;
101 	}
102 
103 	return i + 1;
104 }
105 
106 static uint32_t at91_shdwc_get_wakeup_input(const void *fdt, int np)
107 {
108 	const uint32_t *prop = NULL;
109 	uint32_t wk_input_mask = 0;
110 	uint32_t wuir = 0;
111 	uint32_t wk_input = 0;
112 	int child = 0;
113 	int len = 0;
114 
115 	fdt_for_each_subnode(child, fdt, np) {
116 		prop = fdt_getprop(fdt, child, "reg", &len);
117 		if (!prop || len != sizeof(uint32_t)) {
118 			DMSG("reg property is missing for node %s",
119 			     fdt_get_name(fdt, child, NULL));
120 			continue;
121 		}
122 		wk_input = fdt32_to_cpu(*prop);
123 		wk_input_mask = BIT32(wk_input);
124 		if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
125 			DMSG("wake-up input %"PRId32" out of bounds ignore",
126 			     wk_input);
127 			continue;
128 		}
129 		wuir |= wk_input_mask;
130 
131 		if (fdt_getprop(fdt, child, "atmel,wakeup-active-high", NULL))
132 			wuir |= AT91_SHDW_WKUPT(wk_input);
133 	}
134 
135 	return wuir;
136 }
137 
138 static void at91_shdwc_dt_configure(const void *fdt, int np)
139 {
140 	const uint32_t *prop = NULL;
141 	uint32_t mode = 0;
142 	uint32_t tmp = 0;
143 	uint32_t input = 0;
144 	int len = 0;
145 
146 	prop = fdt_getprop(fdt, np, "debounce-delay-us", &len);
147 	if (prop && len == sizeof(uint32_t)) {
148 		tmp = fdt32_to_cpu(*prop);
149 		mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(tmp));
150 	}
151 
152 	if (fdt_getprop(fdt, np, "atmel,wakeup-rtc-timer", &len))
153 		mode |= AT91_SHDW_RTCWKEN;
154 
155 	io_write32(shdwc_base + AT91_SHDW_MR, mode);
156 
157 	input = at91_shdwc_get_wakeup_input(fdt, np);
158 	io_write32(shdwc_base + AT91_SHDW_WUIR, input);
159 }
160 
161 static TEE_Result atmel_shdwc_probe(const void *fdt, int node,
162 				    const void *compat_data)
163 {
164 	int ddr_node = 0;
165 	size_t size = 0;
166 	uint32_t ddr = AT91_DDRSDRC_MD_LPDDR2;
167 	struct shdwc_compat *compat = (struct shdwc_compat *)compat_data;
168 
169 	/*
170 	 * Assembly code relies on the fact that there is only one CPU to avoid
171 	 * any other one to invalidate TLB/I-Cache.
172 	 */
173 	COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
174 
175 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
176 		return TEE_ERROR_BAD_PARAMETERS;
177 
178 	if (!compat->shdwc_always_secure)
179 		matrix_configure_periph_secure(AT91C_ID_SYS);
180 
181 	if (dt_map_dev(fdt, node, &shdwc_base, &size, DT_MAP_AUTO) < 0)
182 		return TEE_ERROR_GENERIC;
183 
184 	if (!compat->ddrc.type_mask)
185 		return TEE_SUCCESS;
186 
187 	ddr_node = fdt_node_offset_by_compatible(fdt, -1,
188 						 "atmel,sama5d3-ddramc");
189 	if (ddr_node < 0)
190 		return TEE_ERROR_GENERIC;
191 
192 	if (dt_map_dev(fdt, ddr_node, &mpddrc_base, &size, DT_MAP_AUTO) < 0)
193 		return TEE_ERROR_GENERIC;
194 
195 	ddr = io_read32(mpddrc_base + compat->ddrc.type_offset);
196 	ddr &= compat->ddrc.type_mask;
197 	if (ddr != AT91_DDRSDRC_MD_LPDDR2 && ddr != AT91_DDRSDRC_MD_LPDDR3)
198 		mpddrc_base = 0;
199 
200 	at91_shdwc_dt_configure(fdt, node);
201 
202 	return sama5d2_pm_init(fdt, shdwc_base);
203 }
204 
205 static const struct shdwc_compat sama5d2_compat = {
206 	.shdwc_always_secure = false,
207 	.ddrc = {
208 		.type_offset = AT91_DDRSDRC_MDR,
209 		.type_mask = AT91_DDRSDRC_MD,
210 	}
211 };
212 
213 static const struct shdwc_compat sama7g5_compat = {
214 	.shdwc_always_secure = true,
215 };
216 
217 static const struct dt_device_match atmel_shdwc_match_table[] = {
218 	{
219 		.compatible = "atmel,sama5d2-shdwc",
220 		.compat_data = &sama5d2_compat
221 	},
222 	{
223 		.compatible = "microchip,sama7g5-shdwc",
224 		.compat_data = &sama7g5_compat,
225 	},
226 	{ }
227 };
228 
229 DEFINE_DT_DRIVER(atmel_shdwc_dt_driver) = {
230 	.name = "atmel_shdwc",
231 	.type = DT_DRIVER_NOTYPE,
232 	.match_table = atmel_shdwc_match_table,
233 	.probe = atmel_shdwc_probe,
234 };
235