1 /*
2 * Copyright (c) 2017-2024, Arm Limited. All rights reserved.
3 * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon
4 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5 * reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 #include "mpu_armv8m_drv.h"
12 #include "tfm_hal_device_header.h"
13
14 /*
15 * FixMe:
16 * This is a beta quality driver for MPU in v8M. To be finalized.
17 */
18
mpu_armv8m_enable(struct mpu_armv8m_dev_t * dev,uint32_t privdef_en,uint32_t hfnmi_en)19 enum mpu_armv8m_error_t mpu_armv8m_enable(struct mpu_armv8m_dev_t *dev,
20 uint32_t privdef_en,
21 uint32_t hfnmi_en)
22 {
23 /*No error checking*/
24
25 MPU_Type *mpu = (MPU_Type *)dev->base;
26
27 /*
28 * FixMe: Set 4 pre-defined MAIR_ATTR for memory. The attributes come
29 * from default memory map, need to check if fine-tune is necessary.
30 *
31 * MAIR0_0: Peripheral, Device-nGnRE.
32 * MAIR0_1: Code, WT RA. Same attr for Outer and Inner.
33 * MAIR0_2: SRAM, WBWA RA. Same attr for Outer and Inner.
34 * MAIR0_3: Non cacheable RA. Same attr for Outer and Inner.
35 */
36 mpu->MAIR0 = (MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL << MPU_MAIR0_Attr0_Pos) |
37 (MPU_ARMV8M_MAIR_ATTR_CODE_VAL << MPU_MAIR0_Attr1_Pos) |
38 (MPU_ARMV8M_MAIR_ATTR_DATA_VAL << MPU_MAIR0_Attr2_Pos) |
39 (MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL << MPU_MAIR0_Attr3_Pos);
40
41 mpu->CTRL =
42 (privdef_en ? MPU_CTRL_PRIVDEFENA_Msk : 0) |
43 (hfnmi_en ? MPU_CTRL_HFNMIENA_Msk : 0);
44
45 /*Ensure all configuration is written before enable*/
46
47 mpu->CTRL |= MPU_CTRL_ENABLE_Msk;
48
49 /* Enable MPU before next instruction */
50 __DSB();
51 __ISB();
52 return MPU_ARMV8M_OK;
53 }
54
mpu_armv8m_check(struct mpu_armv8m_dev_t * dev,uint32_t privdef_en,uint32_t hfnmi_en)55 enum mpu_armv8m_error_t mpu_armv8m_check(struct mpu_armv8m_dev_t *dev,
56 uint32_t privdef_en,
57 uint32_t hfnmi_en)
58 {
59 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_ERROR;
60 uint32_t mair0;
61 uint32_t ctrl;
62
63 /*No error checking*/
64
65 MPU_Type *mpu = (MPU_Type *)dev->base;
66
67 /*
68 * FixMe: Set 4 pre-defined MAIR_ATTR for memory. The attributes come
69 * from default memory map, need to check if fine-tune is necessary.
70 *
71 * MAIR0_0: Peripheral, Device-nGnRE.
72 * MAIR0_1: Code, WT RA. Same attr for Outer and Inner.
73 * MAIR0_2: SRAM, WBWA RA. Same attr for Outer and Inner.
74 * MAIR0_3: Non cacheable RA. Same attr for Outer and Inner.
75 */
76 mair0 = (MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL << MPU_MAIR0_Attr0_Pos) |
77 (MPU_ARMV8M_MAIR_ATTR_CODE_VAL << MPU_MAIR0_Attr1_Pos) |
78 (MPU_ARMV8M_MAIR_ATTR_DATA_VAL << MPU_MAIR0_Attr2_Pos) |
79 (MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL << MPU_MAIR0_Attr3_Pos);
80
81 ctrl =
82 (privdef_en ? MPU_CTRL_PRIVDEFENA_Msk : 0) |
83 (hfnmi_en ? MPU_CTRL_HFNMIENA_Msk : 0);
84
85 /*Ensure all configuration is written before enable*/
86
87 ctrl |= MPU_CTRL_ENABLE_Msk;
88
89
90 if ((mpu->MAIR0 == mair0) && (mpu->CTRL == ctrl))
91 {
92 ret_val = MPU_ARMV8M_OK;
93 }
94
95 return ret_val;
96 }
97
mpu_armv8m_disable(struct mpu_armv8m_dev_t * dev)98 enum mpu_armv8m_error_t mpu_armv8m_disable(struct mpu_armv8m_dev_t *dev)
99 {
100 MPU_Type *mpu = (MPU_Type *)dev->base;
101
102 /* Reset all fields as enable does full setup */
103 mpu->CTRL = 0;
104
105 return MPU_ARMV8M_OK;
106 }
107
108
mpu_armv8m_region_enable(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)109 enum mpu_armv8m_error_t mpu_armv8m_region_enable(
110 struct mpu_armv8m_dev_t *dev,
111 struct mpu_armv8m_region_cfg_t *region_cfg)
112 {
113 MPU_Type *mpu = (MPU_Type *)dev->base;
114
115 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_OK;
116 uint32_t base_cfg;
117 uint32_t limit_cfg;
118
119 /*FIXME : Add complete error checking*/
120 if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0)
121 {
122 return MPU_ARMV8M_ERROR;
123 }
124 /* region_limit doesn't need to be aligned but the scatter
125 * file needs to be setup to ensure that partitions do not overlap.
126 */
127 /* don't disable MPU */
128
129 mpu->RNR = region_cfg->region_nr & MPU_RNR_REGION_Msk;
130
131 /* This zeroes the lower bits of the base address */
132 base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
133 base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
134 base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
135 base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
136
137 mpu->RBAR = base_cfg;
138
139 /* This zeroes the lower bits of limit address but they are treated as 1 */
140 limit_cfg = (region_cfg->region_limit - 1) & MPU_RLAR_LIMIT_Msk;
141
142 limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
143 MPU_RLAR_AttrIndx_Msk;
144
145 limit_cfg |= MPU_RLAR_EN_Msk;
146
147 mpu->RLAR = limit_cfg;
148
149 /* Enable MPU before the next instruction */
150 __DSB();
151 __ISB();
152
153 return ret_val;
154 }
155
mpu_armv8m_region_enable_check(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)156 enum mpu_armv8m_error_t mpu_armv8m_region_enable_check(
157 struct mpu_armv8m_dev_t *dev,
158 struct mpu_armv8m_region_cfg_t *region_cfg)
159 {
160 MPU_Type *mpu = (MPU_Type *)dev->base;
161
162 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_ERROR;
163 uint32_t base_cfg;
164 uint32_t limit_cfg;
165
166 /*FIXME : Add complete error checking*/
167 if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0)
168 {
169 return MPU_ARMV8M_ERROR;
170 }
171 /* region_limit doesn't need to be aligned but the scatter
172 * file needs to be setup to ensure that partitions do not overlap.
173 */
174 /* don't disable MPU */
175
176 mpu->RNR = region_cfg->region_nr & MPU_RNR_REGION_Msk;
177
178 /* This zeroes the lower bits of the base address */
179 base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
180 base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
181 base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
182 base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
183
184 /* This zeroes the lower bits of limit address but they are treated as 1 */
185 limit_cfg = (region_cfg->region_limit - 1) & MPU_RLAR_LIMIT_Msk;
186
187 limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
188 MPU_RLAR_AttrIndx_Msk;
189
190 limit_cfg |= MPU_RLAR_EN_Msk;
191
192 if ((mpu->RBAR == base_cfg) && (mpu->RLAR == limit_cfg))
193 {
194 ret_val = MPU_ARMV8M_OK;
195 }
196
197 return ret_val;
198 }
199
mpu_armv8m_region_disable(struct mpu_armv8m_dev_t * dev,uint32_t region_nr)200 enum mpu_armv8m_error_t mpu_armv8m_region_disable(
201 struct mpu_armv8m_dev_t *dev,
202 uint32_t region_nr)
203 {
204
205 MPU_Type *mpu = (MPU_Type *)dev->base;
206
207 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_OK;
208 uint32_t ctrl_before;
209
210 /*FIXME : Add complete error checking*/
211
212 ctrl_before = mpu->CTRL;
213 mpu->CTRL = 0;
214
215 mpu->RNR = region_nr & MPU_RNR_REGION_Msk;
216
217 mpu->RBAR = 0;
218 mpu->RLAR = 0;
219
220 /*Restore main MPU control*/
221 mpu->CTRL = ctrl_before;
222
223 return ret_val;
224 }
225
mpu_armv8m_region_disable_check(struct mpu_armv8m_dev_t * dev,uint32_t region_nr)226 enum mpu_armv8m_error_t mpu_armv8m_region_disable_check(
227 struct mpu_armv8m_dev_t *dev,
228 uint32_t region_nr)
229 {
230
231 MPU_Type *mpu = (MPU_Type *)dev->base;
232
233 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_ERROR;
234
235 mpu->RNR = region_nr & MPU_RNR_REGION_Msk;
236
237 if ((mpu->RBAR == 0) && (mpu->RLAR == 0))
238 {
239 ret_val = MPU_ARMV8M_OK;
240 }
241
242 return ret_val;
243 }
244
mpu_armv8m_region_config_only(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)245 enum mpu_armv8m_error_t mpu_armv8m_region_config_only(
246 struct mpu_armv8m_dev_t *dev,
247 struct mpu_armv8m_region_cfg_t *region_cfg)
248 {
249 MPU_Type *mpu = (MPU_Type *)dev->base;
250
251 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_OK;
252 uint32_t base_cfg;
253 uint32_t limit_cfg;
254
255 /*FIXME : Add complete error checking*/
256 if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0)
257 {
258 return MPU_ARMV8M_ERROR;
259 }
260 if (((region_cfg->region_limit+1) & ~MPU_RLAR_LIMIT_Msk) !=0)
261 {
262 return MPU_ARMV8M_ERROR;
263 }
264 /* region_limit needs to be setup to ensure that partitions do not overlap.
265 */
266 /* don't disable MPU */
267
268 mpu->RNR = region_cfg->region_nr & MPU_RNR_REGION_Msk;
269
270 /* This 0s the lower bits of the base address */
271 base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
272 base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
273 base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
274 base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
275
276 mpu->RBAR = base_cfg;
277
278 /*This 0s the lower bits of base address but they are treated as 1 */
279 limit_cfg = (region_cfg->region_limit) & MPU_RLAR_LIMIT_Msk;
280
281 limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
282 MPU_RLAR_AttrIndx_Msk;
283
284 /* Do not enable region */
285 /* limit_cfg |= MPU_RLAR_EN_Msk; */
286
287 mpu->RLAR = limit_cfg;
288
289 /* Enable MPU before the next instruction */
290 __DSB();
291 __ISB();
292
293 return ret_val;
294 }
295
mpu_armv8m_region_config_only_check(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)296 enum mpu_armv8m_error_t mpu_armv8m_region_config_only_check(
297 struct mpu_armv8m_dev_t *dev,
298 struct mpu_armv8m_region_cfg_t *region_cfg)
299 {
300 MPU_Type *mpu = (MPU_Type *)dev->base;
301
302 enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_ERROR;
303 uint32_t base_cfg;
304 uint32_t limit_cfg;
305
306 /*FIXME : Add complete error checking*/
307 if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0)
308 {
309 return MPU_ARMV8M_ERROR;
310 }
311 /* region_limit doesn't need to be aligned but the scatter
312 * file needs to be setup to ensure that partitions do not overlap.
313 */
314 /* don't disable MPU */
315
316 mpu->RNR = region_cfg->region_nr & MPU_RNR_REGION_Msk;
317
318 /* This 0s the lower bits of the base address */
319 base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
320 base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
321 base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
322 base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
323
324 /*This 0s the lower bits of base address but they are treated as 1 */
325 limit_cfg = (region_cfg->region_limit) & MPU_RLAR_LIMIT_Msk;
326
327 limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
328 MPU_RLAR_AttrIndx_Msk;
329
330 /* Region is not enabled */
331 /* limit_cfg |= MPU_RLAR_EN_Msk; */
332
333 if ((mpu->RBAR == base_cfg) && (mpu->RLAR == limit_cfg))
334 {
335 ret_val = MPU_ARMV8M_OK;
336 }
337
338 return ret_val;
339 }
340
mpu_armv8m_clean(struct mpu_armv8m_dev_t * dev)341 enum mpu_armv8m_error_t mpu_armv8m_clean(struct mpu_armv8m_dev_t *dev)
342 {
343 MPU_Type *mpu = (MPU_Type *)dev->base;
344 uint32_t i = (mpu->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
345
346 while (i > 0)
347 {
348 mpu_armv8m_region_disable(dev, i - 1);
349 i--;
350 }
351
352 return MPU_ARMV8M_OK;
353
354 }
355