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