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