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 * @compatible the compatible string in the device tree
41 */
42 struct ddrc_reg_config {
43 uint32_t type_offset;
44 uint32_t type_mask;
45 const char *compatible;
46 };
47
48 /*
49 * @shdwc_always_secure Is peripheral SHDWC always secured?
50 * @ddrc DDR controller configurations
51 */
52 struct shdwc_compat {
53 bool shdwc_always_secure;
54 struct ddrc_reg_config ddrc;
55 };
56
57 static vaddr_t shdwc_base;
58 static vaddr_t mpddrc_base;
59
atmel_shdwc_available(void)60 bool atmel_shdwc_available(void)
61 {
62 return shdwc_base != 0;
63 }
64
atmel_shdwc_shutdown(void)65 void __noreturn atmel_shdwc_shutdown(void)
66 {
67 vaddr_t pmc_base = at91_pmc_get_base();
68
69 /*
70 * Mask exception before entering assembly which does not expect to be
71 * interrupted.
72 */
73 thread_mask_exceptions(THREAD_EXCP_ALL);
74
75 __atmel_shdwc_shutdown(mpddrc_base, shdwc_base, pmc_base);
76
77 /* We are going to shutdown the CPU so we will never hit this loop */
78 while (true)
79 ;
80 }
81
82 static const unsigned long long sdwc_dbc_period[] = {
83 0, 3, 32, 512, 4096, 32768,
84 };
85
at91_shdwc_debouncer_value(uint32_t in_period_us)86 static uint32_t at91_shdwc_debouncer_value(uint32_t in_period_us)
87 {
88 int i = 0;
89 int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
90 uint64_t period_us = 0;
91 uint64_t max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
92
93 if (in_period_us > max_period_us) {
94 DMSG("debouncer period %"PRIu32" too big, using %"PRIu64" us",
95 in_period_us, max_period_us);
96 return max_idx;
97 }
98
99 for (i = max_idx - 1; i > 0; i--) {
100 period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
101 if (in_period_us > period_us)
102 break;
103 }
104
105 return i + 1;
106 }
107
at91_shdwc_get_wakeup_input(const void * fdt,int np)108 static uint32_t at91_shdwc_get_wakeup_input(const void *fdt, int np)
109 {
110 const uint32_t *prop = NULL;
111 uint32_t wk_input_mask = 0;
112 uint32_t wuir = 0;
113 uint32_t wk_input = 0;
114 int child = 0;
115 int len = 0;
116
117 fdt_for_each_subnode(child, fdt, np) {
118 prop = fdt_getprop(fdt, child, "reg", &len);
119 if (!prop || len != sizeof(uint32_t)) {
120 DMSG("reg property is missing for node %s",
121 fdt_get_name(fdt, child, NULL));
122 continue;
123 }
124 wk_input = fdt32_to_cpu(*prop);
125 wk_input_mask = BIT32(wk_input);
126 if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
127 DMSG("wake-up input %"PRId32" out of bounds ignore",
128 wk_input);
129 continue;
130 }
131 wuir |= wk_input_mask;
132
133 if (fdt_getprop(fdt, child, "atmel,wakeup-active-high", NULL))
134 wuir |= AT91_SHDW_WKUPT(wk_input);
135 }
136
137 return wuir;
138 }
139
at91_shdwc_dt_configure(const void * fdt,int np)140 static void at91_shdwc_dt_configure(const void *fdt, int np)
141 {
142 const uint32_t *prop = NULL;
143 uint32_t mode = 0;
144 uint32_t tmp = 0;
145 uint32_t input = 0;
146 int len = 0;
147
148 prop = fdt_getprop(fdt, np, "debounce-delay-us", &len);
149 if (prop && len == sizeof(uint32_t)) {
150 tmp = fdt32_to_cpu(*prop);
151 mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(tmp));
152 }
153
154 if (fdt_getprop(fdt, np, "atmel,wakeup-rtc-timer", &len))
155 mode |= AT91_SHDW_RTCWKEN;
156
157 if (fdt_getprop(fdt, np, "atmel,wakeup-rtt-timer", &len))
158 mode |= AT91_SHDW_RTTWKEN;
159
160 io_write32(shdwc_base + AT91_SHDW_MR, mode);
161
162 input = at91_shdwc_get_wakeup_input(fdt, np);
163 io_write32(shdwc_base + AT91_SHDW_WUIR, input);
164 }
165
atmel_shdwc_probe(const void * fdt,int node,const void * compat_data)166 static TEE_Result atmel_shdwc_probe(const void *fdt, int node,
167 const void *compat_data)
168 {
169 int ddr_node = 0;
170 size_t size = 0;
171 uint32_t ddr = AT91_DDRSDRC_MD_LPDDR2;
172 struct shdwc_compat *compat = (struct shdwc_compat *)compat_data;
173
174 /*
175 * Assembly code relies on the fact that there is only one CPU to avoid
176 * any other one to invalidate TLB/I-Cache.
177 */
178 COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
179
180 if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
181 return TEE_ERROR_BAD_PARAMETERS;
182
183 if (!compat->shdwc_always_secure)
184 matrix_configure_periph_secure(AT91C_ID_SYS);
185
186 if (dt_map_dev(fdt, node, &shdwc_base, &size, DT_MAP_AUTO) < 0)
187 return TEE_ERROR_GENERIC;
188
189 ddr_node = fdt_node_offset_by_compatible(fdt, -1,
190 compat->ddrc.compatible);
191 if (ddr_node < 0)
192 return TEE_ERROR_GENERIC;
193
194 if (dt_map_dev(fdt, ddr_node, &mpddrc_base, &size, DT_MAP_AUTO) < 0)
195 return TEE_ERROR_GENERIC;
196
197 if (!compat->ddrc.type_mask) {
198 ddr = io_read32(mpddrc_base + compat->ddrc.type_offset);
199 ddr &= compat->ddrc.type_mask;
200 if (ddr != AT91_DDRSDRC_MD_LPDDR2 &&
201 ddr != AT91_DDRSDRC_MD_LPDDR3)
202 mpddrc_base = 0;
203 } else {
204 /*
205 * Set the base to 0 as the code of DRAM controller for power
206 * down is not implemented yet.
207 */
208 mpddrc_base = 0;
209 }
210
211 at91_shdwc_dt_configure(fdt, node);
212
213 return sam_pm_init(fdt, shdwc_base);
214 }
215
216 static const struct shdwc_compat sama5d2_compat = {
217 .shdwc_always_secure = false,
218 .ddrc = {
219 .type_offset = AT91_DDRSDRC_MDR,
220 .type_mask = AT91_DDRSDRC_MD,
221 .compatible = "atmel,sama5d3-ddramc",
222 }
223 };
224
225 static const struct shdwc_compat sama7g5_compat = {
226 .shdwc_always_secure = true,
227 .ddrc = {
228 .compatible = "microchip,sama7g5-uddrc",
229 }
230 };
231
232 static const struct dt_device_match atmel_shdwc_match_table[] = {
233 {
234 .compatible = "atmel,sama5d2-shdwc",
235 .compat_data = &sama5d2_compat
236 },
237 {
238 .compatible = "microchip,sama7g5-shdwc",
239 .compat_data = &sama7g5_compat,
240 },
241 { }
242 };
243
244 DEFINE_DT_DRIVER(atmel_shdwc_dt_driver) = {
245 .name = "atmel_shdwc",
246 .type = DT_DRIVER_NOTYPE,
247 .match_table = atmel_shdwc_match_table,
248 .probe = atmel_shdwc_probe,
249 };
250