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