xref: /optee_os/core/drivers/stm32_hsem.c (revision bc9c7c89d05577efdbfb358dc241977ddc82e07c)
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_RIF_RESOURCES		U(16)
62ec9aa1a4SGatien Chevallier 
63ec9aa1a4SGatien Chevallier struct hsem_pdata {
64ec9aa1a4SGatien Chevallier 	struct clk *hsem_clock;
65ec9aa1a4SGatien Chevallier 	struct rif_conf_data conf_data;
66ec9aa1a4SGatien Chevallier 	unsigned int nb_channels;
67ec9aa1a4SGatien Chevallier 	vaddr_t base;
68ec9aa1a4SGatien Chevallier 	uint32_t *rif_proc_conf;
69ec9aa1a4SGatien Chevallier };
70ec9aa1a4SGatien Chevallier 
71ec9aa1a4SGatien Chevallier static struct hsem_pdata *hsem_d;
72ec9aa1a4SGatien Chevallier 
apply_rif_config(bool is_tdcid)73ec9aa1a4SGatien Chevallier static void apply_rif_config(bool is_tdcid)
74ec9aa1a4SGatien Chevallier {
75ec9aa1a4SGatien Chevallier 	unsigned int i = 0;
76ec9aa1a4SGatien Chevallier 	unsigned int j = 0;
77ec9aa1a4SGatien Chevallier 
78ec9aa1a4SGatien Chevallier 	/*
79ec9aa1a4SGatien Chevallier 	 * When TDCID, OP-TEE should be the one to set the CID filtering
80ec9aa1a4SGatien Chevallier 	 * configuration. Clearing previous configuration prevents
81ec9aa1a4SGatien Chevallier 	 * undesired events during the only legitimate configuration.
82ec9aa1a4SGatien Chevallier 	 */
83ec9aa1a4SGatien Chevallier 	if (is_tdcid) {
84ec9aa1a4SGatien Chevallier 		for (i = 0; i < HSEM_NB_PROC; i++)
85ec9aa1a4SGatien Chevallier 			io_clrbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1),
86ec9aa1a4SGatien Chevallier 				     HSEM_CnCIDCFGR_CONF_MASK);
87ec9aa1a4SGatien Chevallier 
88ec9aa1a4SGatien Chevallier 		/* Clean HSEM groups configuration registers */
89ec9aa1a4SGatien Chevallier 		for (i = 0; i < HSEM_NB_SEM_GROUPS; i++)
90ec9aa1a4SGatien Chevallier 			io_clrbits32(hsem_d->base + HSEM_GpCIDCFGR(i),
91ec9aa1a4SGatien Chevallier 				     HSEM_GpCIDCFGR_CONF_MASK);
92ec9aa1a4SGatien Chevallier 	}
93ec9aa1a4SGatien Chevallier 
94ec9aa1a4SGatien Chevallier 	/* Security and privilege RIF configuration */
95ec9aa1a4SGatien Chevallier 	io_clrsetbits32(hsem_d->base + HSEM_SECCFGR,
96ec9aa1a4SGatien Chevallier 			HSEM_SECCFGR_MASK & hsem_d->conf_data.access_mask[0],
97ec9aa1a4SGatien Chevallier 			hsem_d->conf_data.sec_conf[0]);
98ec9aa1a4SGatien Chevallier 	io_clrsetbits32(hsem_d->base + HSEM_PRIVCFGR,
99ec9aa1a4SGatien Chevallier 			HSEM_PRIVCFGR_MASK & hsem_d->conf_data.access_mask[0],
100ec9aa1a4SGatien Chevallier 			hsem_d->conf_data.priv_conf[0]);
101ec9aa1a4SGatien Chevallier 
102ec9aa1a4SGatien Chevallier 	if (!is_tdcid)
103ec9aa1a4SGatien Chevallier 		return;
104ec9aa1a4SGatien Chevallier 
105ec9aa1a4SGatien Chevallier 	/* Configure HSEM processors configuration registers */
106ec9aa1a4SGatien Chevallier 	for (i = 0; i < HSEM_NB_PROC; i++) {
107ec9aa1a4SGatien Chevallier 		/*
108ec9aa1a4SGatien Chevallier 		 * If a processor CID configuration is present, enable it.
109ec9aa1a4SGatien Chevallier 		 * Else, nothing to do.
110ec9aa1a4SGatien Chevallier 		 */
111ec9aa1a4SGatien Chevallier 		if (!hsem_d->rif_proc_conf[i])
112ec9aa1a4SGatien Chevallier 			continue;
113ec9aa1a4SGatien Chevallier 
114ec9aa1a4SGatien Chevallier 		io_clrsetbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1),
115ec9aa1a4SGatien Chevallier 				HSEM_CnCIDCFGR_CONF_MASK,
116ec9aa1a4SGatien Chevallier 				_CIDCFGR_CFEN | hsem_d->rif_proc_conf[i]);
117ec9aa1a4SGatien Chevallier 	}
118ec9aa1a4SGatien Chevallier 
119ec9aa1a4SGatien Chevallier 	/*
120ec9aa1a4SGatien Chevallier 	 * Configure HSEM groups configuration registers
121ec9aa1a4SGatien Chevallier 	 * If one semaphore is configured, all semaphores of its group
122ec9aa1a4SGatien Chevallier 	 * must be configured too and MUST have the same RIF
123ec9aa1a4SGatien Chevallier 	 * configuration.
124ec9aa1a4SGatien Chevallier 	 */
125ec9aa1a4SGatien Chevallier 	for (i = 0; i < HSEM_NB_SEM_GROUPS; i++) {
126*bc9c7c89SGatien Chevallier 		unsigned int grp_idx = i * HSEM_NB_SEM_PER_GROUP;
127*bc9c7c89SGatien Chevallier 		uint32_t group_cid_value = hsem_d->conf_data.cid_confs[grp_idx];
128*bc9c7c89SGatien Chevallier 		unsigned int sem_wlist_c = 0;
129*bc9c7c89SGatien Chevallier 		bool cid_found = false;
130ec9aa1a4SGatien Chevallier 
131ec9aa1a4SGatien Chevallier 		/*
132ec9aa1a4SGatien Chevallier 		 * HSEM resources in the same group must have the same CID
133ec9aa1a4SGatien Chevallier 		 * filtering configuration. Else it is inconsistent.
134ec9aa1a4SGatien Chevallier 		 */
135ec9aa1a4SGatien Chevallier 		for (j = 0; j < HSEM_NB_SEM_PER_GROUP; j++)
136*bc9c7c89SGatien Chevallier 			if (hsem_d->conf_data.cid_confs[j + grp_idx] !=
137*bc9c7c89SGatien Chevallier 			    group_cid_value)
138ec9aa1a4SGatien Chevallier 				panic("Inconsistent HSEM RIF group config");
139ec9aa1a4SGatien Chevallier 
140*bc9c7c89SGatien Chevallier 		/* If CID filtering is disabled, do nothing */
141*bc9c7c89SGatien Chevallier 		if (!(group_cid_value & _CIDCFGR_CFEN))
142*bc9c7c89SGatien Chevallier 			continue;
143*bc9c7c89SGatien Chevallier 
144*bc9c7c89SGatien Chevallier 		/*
145*bc9c7c89SGatien Chevallier 		 * Check if configured CIDs correspond to a processor's
146*bc9c7c89SGatien Chevallier 		 * CID in HSEM_CnCIDCFGR registers.
147*bc9c7c89SGatien Chevallier 		 */
148*bc9c7c89SGatien Chevallier 		for (j = 0; j < HSEM_NB_PROC; j++) {
149*bc9c7c89SGatien Chevallier 			uint32_t proc_cid = hsem_d->rif_proc_conf[j] >>
150*bc9c7c89SGatien Chevallier 					    _CIDCFGR_SCID_SHIFT;
151*bc9c7c89SGatien Chevallier 
152*bc9c7c89SGatien Chevallier 			assert(proc_cid <= MAX_CID_SUPPORTED);
153*bc9c7c89SGatien Chevallier 			if (BIT(proc_cid + _CIDCFGR_SEMWL_SHIFT) &
154*bc9c7c89SGatien Chevallier 			    group_cid_value) {
155*bc9c7c89SGatien Chevallier 				sem_wlist_c |=
156*bc9c7c89SGatien Chevallier 					BIT(j + HSEM_GpCIDCFGR_SEM_WLIST_SHIFT);
157*bc9c7c89SGatien Chevallier 				cid_found = true;
158*bc9c7c89SGatien Chevallier 			}
159*bc9c7c89SGatien Chevallier 		}
160*bc9c7c89SGatien Chevallier 		if (!cid_found)
161*bc9c7c89SGatien Chevallier 			panic("Unknown HSEM processor CID");
162*bc9c7c89SGatien Chevallier 
163ec9aa1a4SGatien Chevallier 		io_clrsetbits32(hsem_d->base + HSEM_GpCIDCFGR(i),
164ec9aa1a4SGatien Chevallier 				HSEM_GpCIDCFGR_CONF_MASK,
165*bc9c7c89SGatien Chevallier 				_CIDCFGR_CFEN | sem_wlist_c);
166ec9aa1a4SGatien Chevallier 	}
167ec9aa1a4SGatien Chevallier }
168ec9aa1a4SGatien Chevallier 
parse_dt(const void * fdt,int node)169ec9aa1a4SGatien Chevallier static TEE_Result parse_dt(const void *fdt, int node)
170ec9aa1a4SGatien Chevallier {
171ec9aa1a4SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
172ec9aa1a4SGatien Chevallier 	unsigned int i = 0;
173ec9aa1a4SGatien Chevallier 	int lenp = 0;
174ec9aa1a4SGatien Chevallier 	const fdt32_t *cuint = NULL;
175ec9aa1a4SGatien Chevallier 	struct dt_node_info info = { };
176ec9aa1a4SGatien Chevallier 	struct io_pa_va addr = { };
177ec9aa1a4SGatien Chevallier 
178ec9aa1a4SGatien Chevallier 	fdt_fill_device_info(fdt, &info, node);
179ec9aa1a4SGatien Chevallier 	assert(info.reg != DT_INFO_INVALID_REG &&
180ec9aa1a4SGatien Chevallier 	       info.reg_size != DT_INFO_INVALID_REG_SIZE);
181ec9aa1a4SGatien Chevallier 
182ec9aa1a4SGatien Chevallier 	addr.pa = info.reg;
183ec9aa1a4SGatien Chevallier 	hsem_d->base = io_pa_or_va_secure(&addr, info.reg_size);
184ec9aa1a4SGatien Chevallier 
185ec9aa1a4SGatien Chevallier 	/* Gate the IP */
186ec9aa1a4SGatien Chevallier 	res = clk_dt_get_by_index(fdt, node, 0, &hsem_d->hsem_clock);
187ec9aa1a4SGatien Chevallier 	if (res)
188ec9aa1a4SGatien Chevallier 		return res;
189ec9aa1a4SGatien Chevallier 
190ec9aa1a4SGatien Chevallier 	cuint = fdt_getprop(fdt, node, "st,protreg", &lenp);
191ec9aa1a4SGatien Chevallier 	if (!cuint)
192ec9aa1a4SGatien Chevallier 		panic("No RIF configuration available");
193ec9aa1a4SGatien Chevallier 
194ec9aa1a4SGatien Chevallier 	hsem_d->nb_channels = (unsigned int)(lenp / sizeof(uint32_t));
195ec9aa1a4SGatien Chevallier 	assert(hsem_d->nb_channels <= HSEM_RIF_RESOURCES);
196ec9aa1a4SGatien Chevallier 
197ec9aa1a4SGatien Chevallier 	hsem_d->rif_proc_conf = calloc(HSEM_NB_PROC, sizeof(uint32_t));
198ec9aa1a4SGatien Chevallier 	assert(hsem_d->rif_proc_conf);
199ec9aa1a4SGatien Chevallier 	hsem_d->conf_data.cid_confs = calloc(HSEM_RIF_RESOURCES,
200ec9aa1a4SGatien Chevallier 					     sizeof(uint32_t));
201ec9aa1a4SGatien Chevallier 	hsem_d->conf_data.sec_conf = calloc(1, sizeof(uint32_t));
202ec9aa1a4SGatien Chevallier 	hsem_d->conf_data.priv_conf = calloc(1, sizeof(uint32_t));
203ec9aa1a4SGatien Chevallier 	hsem_d->conf_data.access_mask = calloc(1, sizeof(uint32_t));
204ec9aa1a4SGatien Chevallier 	assert(hsem_d->conf_data.cid_confs && hsem_d->conf_data.sec_conf &&
205ec9aa1a4SGatien Chevallier 	       hsem_d->conf_data.priv_conf && hsem_d->conf_data.access_mask);
206ec9aa1a4SGatien Chevallier 
207ec9aa1a4SGatien Chevallier 	for (i = 0; i < hsem_d->nb_channels; i++)
208ec9aa1a4SGatien Chevallier 		stm32_rif_parse_cfg(fdt32_to_cpu(cuint[i]), &hsem_d->conf_data,
209ec9aa1a4SGatien Chevallier 				    HSEM_RIF_RESOURCES);
210ec9aa1a4SGatien Chevallier 
211ec9aa1a4SGatien Chevallier 	cuint = fdt_getprop(fdt, node, "st,proccid", &lenp);
212ec9aa1a4SGatien Chevallier 	if (!cuint)
213ec9aa1a4SGatien Chevallier 		panic("No RIF proc configuration available");
214ec9aa1a4SGatien Chevallier 
215ec9aa1a4SGatien Chevallier 	lenp = (unsigned int)(lenp / sizeof(uint32_t));
216ec9aa1a4SGatien Chevallier 	/*
217ec9aa1a4SGatien Chevallier 	 * There should be maximum (HSEM_NB_PROC * 2) property argument.
218ec9aa1a4SGatien Chevallier 	 * First argument for a processor is its number, the second is its CID.
219ec9aa1a4SGatien Chevallier 	 */
220ec9aa1a4SGatien Chevallier 	assert((unsigned int)lenp <= (HSEM_NB_PROC * 2));
221ec9aa1a4SGatien Chevallier 
222ec9aa1a4SGatien Chevallier 	for (i = 0; i < (unsigned int)lenp / 2; i++) {
223ec9aa1a4SGatien Chevallier 		unsigned int pos = fdt32_to_cpu(cuint[i * 2]) - 1;
224ec9aa1a4SGatien Chevallier 		unsigned int cid_value = fdt32_to_cpu(cuint[(i * 2) + 1]);
225ec9aa1a4SGatien Chevallier 
226646ad62bSGatien Chevallier 		hsem_d->rif_proc_conf[pos] = SHIFT_U32(cid_value,
227646ad62bSGatien Chevallier 						       _CIDCFGR_SCID_SHIFT);
228ec9aa1a4SGatien Chevallier 	}
229ec9aa1a4SGatien Chevallier 
230ec9aa1a4SGatien Chevallier 	return TEE_SUCCESS;
231ec9aa1a4SGatien Chevallier }
232ec9aa1a4SGatien Chevallier 
stm32_hsem_probe(const void * fdt,int node,const void * compat_data __unused)233ec9aa1a4SGatien Chevallier static TEE_Result stm32_hsem_probe(const void *fdt, int node,
234ec9aa1a4SGatien Chevallier 				   const void *compat_data __unused)
235ec9aa1a4SGatien Chevallier {
236ec9aa1a4SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
237ec9aa1a4SGatien Chevallier 	bool is_tdcid = false;
238ec9aa1a4SGatien Chevallier 
239ec9aa1a4SGatien Chevallier 	res = stm32_rifsc_check_tdcid(&is_tdcid);
240ec9aa1a4SGatien Chevallier 	if (res)
241ec9aa1a4SGatien Chevallier 		return res;
242ec9aa1a4SGatien Chevallier 
243ec9aa1a4SGatien Chevallier 	hsem_d = calloc(1, sizeof(*hsem_d));
244ec9aa1a4SGatien Chevallier 	if (!hsem_d)
245ec9aa1a4SGatien Chevallier 		return TEE_ERROR_OUT_OF_MEMORY;
246ec9aa1a4SGatien Chevallier 
247ec9aa1a4SGatien Chevallier 	res = parse_dt(fdt, node);
24819c6bd04SGatien Chevallier 	if (res) {
24919c6bd04SGatien Chevallier 		free(hsem_d);
250ec9aa1a4SGatien Chevallier 		return res;
25119c6bd04SGatien Chevallier 	}
252ec9aa1a4SGatien Chevallier 
253ec9aa1a4SGatien Chevallier 	res = clk_enable(hsem_d->hsem_clock);
254ec9aa1a4SGatien Chevallier 	if (res)
255ec9aa1a4SGatien Chevallier 		panic("Cannot access HSEM clock");
256ec9aa1a4SGatien Chevallier 
257ec9aa1a4SGatien Chevallier 	apply_rif_config(is_tdcid);
258ec9aa1a4SGatien Chevallier 
259ec9aa1a4SGatien Chevallier 	clk_disable(hsem_d->hsem_clock);
260ec9aa1a4SGatien Chevallier 
261ec9aa1a4SGatien Chevallier 	return TEE_SUCCESS;
262ec9aa1a4SGatien Chevallier }
263ec9aa1a4SGatien Chevallier 
264ec9aa1a4SGatien Chevallier static const struct dt_device_match stm32_hsem_match_table[] = {
265ec9aa1a4SGatien Chevallier 	{ .compatible = "st,stm32mp25-hsem" },
266ec9aa1a4SGatien Chevallier 	{ }
267ec9aa1a4SGatien Chevallier };
268ec9aa1a4SGatien Chevallier 
269ec9aa1a4SGatien Chevallier DEFINE_DT_DRIVER(stm32_hsem_dt_driver) = {
270ec9aa1a4SGatien Chevallier 	.name = "st,stm32-hsem",
271ec9aa1a4SGatien Chevallier 	.match_table = stm32_hsem_match_table,
272ec9aa1a4SGatien Chevallier 	.probe = stm32_hsem_probe,
273ec9aa1a4SGatien Chevallier };
274