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