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