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/thread.h>
14 #include <libfdt.h>
15 #include <matrix.h>
16 #include <sama5d2.h>
17 #include <stdbool.h>
18 #include <tee_api_defines.h>
19 #include <tee_api_types.h>
20 #include <trace.h>
21 #include <types_ext.h>
22 #include <util.h>
23
24 #include "at91_clk.h"
25
26 #define SHDW_WK_PIN(reg, cfg) ((reg) & \
27 AT91_SHDW_WKUPIS((cfg)->wkup_pin_input))
28 #define SHDW_RTCWK(reg, cfg) (((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1)
29 #define SHDW_RTTWK(reg, cfg) (((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1)
30 #define SHDW_RTCWKEN(cfg) BIT((cfg)->mr_rtcwk_shift)
31 #define SHDW_RTTWKEN(cfg) BIT((cfg)->mr_rttwk_shift)
32
33 #define SLOW_CLK_FREQ 32768ULL
34 #define DBC_PERIOD_US(x) DIV_ROUND_UP((1000000ULL * (x)), SLOW_CLK_FREQ)
35
36 static vaddr_t shdwc_base;
37 static vaddr_t mpddrc_base;
38
atmel_shdwc_available(void)39 bool atmel_shdwc_available(void)
40 {
41 return shdwc_base != 0;
42 }
43
atmel_shdwc_shutdown(void)44 void __noreturn atmel_shdwc_shutdown(void)
45 {
46 vaddr_t pmc_base = at91_pmc_get_base();
47
48 /*
49 * Mask exception before entering assembly which does not expect to be
50 * interrupted.
51 */
52 thread_mask_exceptions(THREAD_EXCP_ALL);
53
54 __atmel_shdwc_shutdown(mpddrc_base, shdwc_base, pmc_base);
55
56 /* We are going to shutdown the CPU so we will never hit this loop */
57 while (true)
58 ;
59 }
60
61 static const unsigned long long sdwc_dbc_period[] = {
62 0, 3, 32, 512, 4096, 32768,
63 };
64
at91_shdwc_debouncer_value(uint32_t in_period_us)65 static uint32_t at91_shdwc_debouncer_value(uint32_t in_period_us)
66 {
67 int i = 0;
68 int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
69 uint64_t period_us = 0;
70 uint64_t max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
71
72 if (in_period_us > max_period_us) {
73 DMSG("debouncer period %"PRIu32" too big, using %"PRIu64" us",
74 in_period_us, max_period_us);
75 return max_idx;
76 }
77
78 for (i = max_idx - 1; i > 0; i--) {
79 period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
80 if (in_period_us > period_us)
81 break;
82 }
83
84 return i + 1;
85 }
86
at91_shdwc_get_wakeup_input(const void * fdt,int np)87 static uint32_t at91_shdwc_get_wakeup_input(const void *fdt, int np)
88 {
89 const uint32_t *prop = NULL;
90 uint32_t wk_input_mask = 0;
91 uint32_t wuir = 0;
92 uint32_t wk_input = 0;
93 int child = 0;
94 int len = 0;
95
96 fdt_for_each_subnode(child, fdt, np) {
97 prop = fdt_getprop(fdt, child, "reg", &len);
98 if (!prop || len != sizeof(uint32_t)) {
99 DMSG("reg property is missing for node %s",
100 fdt_get_name(fdt, child, NULL));
101 continue;
102 }
103 wk_input = fdt32_to_cpu(*prop);
104 wk_input_mask = BIT32(wk_input);
105 if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
106 DMSG("wake-up input %"PRId32" out of bounds ignore",
107 wk_input);
108 continue;
109 }
110 wuir |= wk_input_mask;
111
112 if (fdt_getprop(fdt, child, "atmel,wakeup-active-high", NULL))
113 wuir |= AT91_SHDW_WKUPT(wk_input);
114 }
115
116 return wuir;
117 }
118
at91_shdwc_dt_configure(const void * fdt,int np)119 static void at91_shdwc_dt_configure(const void *fdt, int np)
120 {
121 const uint32_t *prop = NULL;
122 uint32_t mode = 0;
123 uint32_t tmp = 0;
124 uint32_t input = 0;
125 int len = 0;
126
127 prop = fdt_getprop(fdt, np, "debounce-delay-us", &len);
128 if (prop && len == sizeof(uint32_t)) {
129 tmp = fdt32_to_cpu(*prop);
130 mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(tmp));
131 }
132
133 if (fdt_getprop(fdt, np, "atmel,wakeup-rtc-timer", &len))
134 mode |= AT91_SHDW_RTCWKEN;
135
136 io_write32(shdwc_base + AT91_SHDW_MR, mode);
137
138 input = at91_shdwc_get_wakeup_input(fdt, np);
139 io_write32(shdwc_base + AT91_SHDW_WUIR, input);
140 }
141
atmel_shdwc_probe(const void * fdt,int node,const void * compat_data __unused)142 static TEE_Result atmel_shdwc_probe(const void *fdt, int node,
143 const void *compat_data __unused)
144 {
145 int ddr_node = 0;
146 size_t size = 0;
147 uint32_t ddr = AT91_DDRSDRC_MD_LPDDR2;
148
149 /*
150 * Assembly code relies on the fact that there is only one CPU to avoid
151 * any other one to invalidate TLB/I-Cache.
152 */
153 COMPILE_TIME_ASSERT(CFG_TEE_CORE_NB_CORE == 1);
154
155 if (_fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
156 return TEE_ERROR_BAD_PARAMETERS;
157
158 matrix_configure_periph_secure(AT91C_ID_SYS);
159
160 if (dt_map_dev(fdt, node, &shdwc_base, &size, DT_MAP_AUTO) < 0)
161 return TEE_ERROR_GENERIC;
162
163 ddr_node = fdt_node_offset_by_compatible(fdt, -1,
164 "atmel,sama5d3-ddramc");
165 if (ddr_node < 0)
166 return TEE_ERROR_GENERIC;
167
168 if (dt_map_dev(fdt, ddr_node, &mpddrc_base, &size, DT_MAP_AUTO) < 0)
169 return TEE_ERROR_GENERIC;
170
171 ddr = io_read32(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
172 if (ddr != AT91_DDRSDRC_MD_LPDDR2 && ddr != AT91_DDRSDRC_MD_LPDDR3)
173 mpddrc_base = 0;
174
175 at91_shdwc_dt_configure(fdt, node);
176
177 return sama5d2_pm_init(fdt, shdwc_base);
178 }
179
180 static const struct dt_device_match atmel_shdwc_match_table[] = {
181 { .compatible = "atmel,sama5d2-shdwc" },
182 { }
183 };
184
185 DEFINE_DT_DRIVER(atmel_shdwc_dt_driver) = {
186 .name = "atmel_shdwc",
187 .type = DT_DRIVER_NOTYPE,
188 .match_table = atmel_shdwc_match_table,
189 .probe = atmel_shdwc_probe,
190 };
191