xref: /optee_os/core/drivers/stm32_hsem.c (revision b0563631928755fe864b97785160fb3088e9efdc)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2022-2024, STMicroelectronics
4  */
5 
6 #include <arm.h>
7 #include <config.h>
8 #include <drivers/clk.h>
9 #include <drivers/clk_dt.h>
10 #include <drivers/stm32_rif.h>
11 #include <io.h>
12 #include <kernel/boot.h>
13 #include <kernel/delay.h>
14 #include <kernel/dt.h>
15 #include <kernel/dt_driver.h>
16 #include <kernel/panic.h>
17 #include <libfdt.h>
18 #include <mm/core_memprot.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <stm32_util.h>
22 #include <trace.h>
23 
24 #define HSEM_SECCFGR			U(0x200)
25 #define HSEM_PRIVCFGR			U(0x210)
26 #define HSEM_CnCIDCFGR(x)		(U(0x220) + U(0x004) * ((x) - 1))
27 #define HSEM_GpCIDCFGR(x)		(U(0x240) + U(0x004) * (x))
28 
29 /*
30  * CnCIDCFGR register bitfields
31  */
32 #define HSEM_CnCIDCFGR_CONF_MASK	(_CIDCFGR_CFEN |	 \
33 					 HSEM_CnCIDCFGR_SCID_MASK)
34 #define HSEM_CnCIDCFGR_SCID_MASK	GENMASK_32(6, 4)
35 
36 /*
37  * GpCIDCFGR register bitfields
38  */
39 #define HSEM_GpCIDCFGR_SEM_WLIST_C_MASK	GENMASK_32(18, 16)
40 #define HSEM_GpCIDCFGR_SEM_WLIST_SHIFT	U(16)
41 #define HSEM_GpCIDCFGR_CONF_MASK	(_CIDCFGR_CFEN |	 \
42 					 HSEM_GpCIDCFGR_SEM_WLIST_C_MASK)
43 
44 /*
45  * PRIVCFGR register bitfields
46  */
47 #define HSEM_PRIVCFGR_MASK		GENMASK_32(15, 0)
48 
49 /*
50  * SECCFGR register bitfields
51  */
52 #define HSEM_SECCFGR_MASK		GENMASK_32(15, 0)
53 
54 /*
55  * Miscellaneous
56  */
57 #define HSEM_NB_PROC			U(3)
58 #define HSEM_NB_SEM_GROUPS		U(4)
59 #define HSEM_NB_SEM_PER_GROUP		U(4)
60 
61 #define HSEM_NB_MAX_CID_SUPPORTED	U(7)
62 #define HSEM_RIF_RESOURCES		U(16)
63 
64 struct hsem_pdata {
65 	struct clk *hsem_clock;
66 	struct rif_conf_data conf_data;
67 	unsigned int nb_channels;
68 	vaddr_t base;
69 	uint32_t *rif_proc_conf;
70 };
71 
72 static struct hsem_pdata *hsem_d;
73 
74 static void apply_rif_config(bool is_tdcid)
75 {
76 	unsigned int i = 0;
77 	unsigned int j = 0;
78 	uint32_t prev_cid_value = 0;
79 
80 	/*
81 	 * When TDCID, OP-TEE should be the one to set the CID filtering
82 	 * configuration. Clearing previous configuration prevents
83 	 * undesired events during the only legitimate configuration.
84 	 */
85 	if (is_tdcid) {
86 		for (i = 0; i < HSEM_NB_PROC; i++)
87 			io_clrbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1),
88 				     HSEM_CnCIDCFGR_CONF_MASK);
89 
90 		/* Clean HSEM groups configuration registers */
91 		for (i = 0; i < HSEM_NB_SEM_GROUPS; i++)
92 			io_clrbits32(hsem_d->base + HSEM_GpCIDCFGR(i),
93 				     HSEM_GpCIDCFGR_CONF_MASK);
94 	}
95 
96 	/* Security and privilege RIF configuration */
97 	io_clrsetbits32(hsem_d->base + HSEM_SECCFGR,
98 			HSEM_SECCFGR_MASK & hsem_d->conf_data.access_mask[0],
99 			hsem_d->conf_data.sec_conf[0]);
100 	io_clrsetbits32(hsem_d->base + HSEM_PRIVCFGR,
101 			HSEM_PRIVCFGR_MASK & hsem_d->conf_data.access_mask[0],
102 			hsem_d->conf_data.priv_conf[0]);
103 
104 	if (!is_tdcid)
105 		return;
106 
107 	/* Configure HSEM processors configuration registers */
108 	for (i = 0; i < HSEM_NB_PROC; i++) {
109 		/*
110 		 * If a processor CID configuration is present, enable it.
111 		 * Else, nothing to do.
112 		 */
113 		if (!hsem_d->rif_proc_conf[i])
114 			continue;
115 
116 		io_clrsetbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1),
117 				HSEM_CnCIDCFGR_CONF_MASK,
118 				_CIDCFGR_CFEN | hsem_d->rif_proc_conf[i]);
119 	}
120 
121 	/*
122 	 * Configure HSEM groups configuration registers
123 	 * If one semaphore is configured, all semaphores of its group
124 	 * must be configured too and MUST have the same RIF
125 	 * configuration.
126 	 */
127 	for (i = 0; i < HSEM_NB_SEM_GROUPS; i++) {
128 		unsigned int hsem_idx = i * HSEM_NB_SEM_PER_GROUP;
129 		unsigned int hsem_cid = 0;
130 		unsigned int known_cid_idx = 0;
131 
132 		prev_cid_value = hsem_d->conf_data.cid_confs[hsem_idx];
133 
134 		/* If CID filtering is disabled, do nothing */
135 		if (!(prev_cid_value & _CIDCFGR_CFEN))
136 			continue;
137 
138 		/*
139 		 * Check if configured CID corresponds to a processor's
140 		 * CID in HSEM_CnCIDCFGR.
141 		 */
142 		for (j = 0; j < HSEM_NB_PROC; j++) {
143 			uint32_t proc_cid = hsem_d->rif_proc_conf[j];
144 
145 			hsem_cid = (prev_cid_value & HSEM_CnCIDCFGR_SCID_MASK);
146 			DMSG("hsem_cid %u", hsem_cid);
147 			DMSG("proc_cid %u", proc_cid);
148 			if (proc_cid == hsem_cid) {
149 				known_cid_idx = BIT(j +
150 					HSEM_GpCIDCFGR_SEM_WLIST_SHIFT);
151 				break;
152 			}
153 		}
154 		if (!known_cid_idx)
155 			panic("Unknown HSEM processor CID");
156 
157 		/*
158 		 * HSEM resources in the same group must have the same CID
159 		 * filtering configuration. Else it is inconsistent.
160 		 */
161 		for (j = 0; j < HSEM_NB_SEM_PER_GROUP; j++)
162 			if (hsem_d->conf_data.cid_confs[j + hsem_idx] !=
163 			    prev_cid_value)
164 				panic("Inconsistent HSEM RIF group config");
165 
166 		io_clrsetbits32(hsem_d->base + HSEM_GpCIDCFGR(i),
167 				HSEM_GpCIDCFGR_CONF_MASK,
168 				_CIDCFGR_CFEN | known_cid_idx);
169 	}
170 }
171 
172 static TEE_Result parse_dt(const void *fdt, int node)
173 {
174 	TEE_Result res = TEE_ERROR_GENERIC;
175 	unsigned int i = 0;
176 	int lenp = 0;
177 	const fdt32_t *cuint = NULL;
178 	struct dt_node_info info = { };
179 	struct io_pa_va addr = { };
180 
181 	fdt_fill_device_info(fdt, &info, node);
182 	assert(info.reg != DT_INFO_INVALID_REG &&
183 	       info.reg_size != DT_INFO_INVALID_REG_SIZE);
184 
185 	addr.pa = info.reg;
186 	hsem_d->base = io_pa_or_va_secure(&addr, info.reg_size);
187 
188 	/* Gate the IP */
189 	res = clk_dt_get_by_index(fdt, node, 0, &hsem_d->hsem_clock);
190 	if (res)
191 		return res;
192 
193 	cuint = fdt_getprop(fdt, node, "st,protreg", &lenp);
194 	if (!cuint)
195 		panic("No RIF configuration available");
196 
197 	hsem_d->nb_channels = (unsigned int)(lenp / sizeof(uint32_t));
198 	assert(hsem_d->nb_channels <= HSEM_RIF_RESOURCES);
199 
200 	hsem_d->rif_proc_conf = calloc(HSEM_NB_PROC, sizeof(uint32_t));
201 	assert(hsem_d->rif_proc_conf);
202 	hsem_d->conf_data.cid_confs = calloc(HSEM_RIF_RESOURCES,
203 					     sizeof(uint32_t));
204 	hsem_d->conf_data.sec_conf = calloc(1, sizeof(uint32_t));
205 	hsem_d->conf_data.priv_conf = calloc(1, sizeof(uint32_t));
206 	hsem_d->conf_data.access_mask = calloc(1, sizeof(uint32_t));
207 	assert(hsem_d->conf_data.cid_confs && hsem_d->conf_data.sec_conf &&
208 	       hsem_d->conf_data.priv_conf && hsem_d->conf_data.access_mask);
209 
210 	for (i = 0; i < hsem_d->nb_channels; i++)
211 		stm32_rif_parse_cfg(fdt32_to_cpu(cuint[i]), &hsem_d->conf_data,
212 				    HSEM_NB_MAX_CID_SUPPORTED,
213 				    HSEM_RIF_RESOURCES);
214 
215 	cuint = fdt_getprop(fdt, node, "st,proccid", &lenp);
216 	if (!cuint)
217 		panic("No RIF proc configuration available");
218 
219 	lenp = (unsigned int)(lenp / sizeof(uint32_t));
220 	/*
221 	 * There should be maximum (HSEM_NB_PROC * 2) property argument.
222 	 * First argument for a processor is its number, the second is its CID.
223 	 */
224 	assert((unsigned int)lenp <= (HSEM_NB_PROC * 2));
225 
226 	for (i = 0; i < (unsigned int)lenp / 2; i++) {
227 		unsigned int pos = fdt32_to_cpu(cuint[i * 2]) - 1;
228 		unsigned int cid_value = fdt32_to_cpu(cuint[(i * 2) + 1]);
229 
230 		hsem_d->rif_proc_conf[pos] = SHIFT_U32(cid_value, SCID_SHIFT);
231 	}
232 
233 	return TEE_SUCCESS;
234 }
235 
236 static TEE_Result stm32_hsem_probe(const void *fdt, int node,
237 				   const void *compat_data __unused)
238 {
239 	TEE_Result res = TEE_ERROR_GENERIC;
240 	bool is_tdcid = false;
241 
242 	res = stm32_rifsc_check_tdcid(&is_tdcid);
243 	if (res)
244 		return res;
245 
246 	hsem_d = calloc(1, sizeof(*hsem_d));
247 	if (!hsem_d)
248 		return TEE_ERROR_OUT_OF_MEMORY;
249 
250 	res = parse_dt(fdt, node);
251 	if (res)
252 		return res;
253 
254 	res = clk_enable(hsem_d->hsem_clock);
255 	if (res)
256 		panic("Cannot access HSEM clock");
257 
258 	apply_rif_config(is_tdcid);
259 
260 	clk_disable(hsem_d->hsem_clock);
261 
262 	return TEE_SUCCESS;
263 }
264 
265 static const struct dt_device_match stm32_hsem_match_table[] = {
266 	{ .compatible = "st,stm32mp25-hsem" },
267 	{ }
268 };
269 
270 DEFINE_DT_DRIVER(stm32_hsem_dt_driver) = {
271 	.name = "st,stm32-hsem",
272 	.match_table = stm32_hsem_match_table,
273 	.probe = stm32_hsem_probe,
274 };
275