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