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 3 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      */
35     mpu->MAIR0 = (MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL << MPU_MAIR0_Attr0_Pos) |
36                  (MPU_ARMV8M_MAIR_ATTR_CODE_VAL << MPU_MAIR0_Attr1_Pos) |
37                  (MPU_ARMV8M_MAIR_ATTR_DATA_VAL << MPU_MAIR0_Attr2_Pos);
38 
39     mpu->CTRL =
40             (privdef_en ? MPU_CTRL_PRIVDEFENA_Msk : 0) |
41             (hfnmi_en   ? MPU_CTRL_HFNMIENA_Msk   : 0);
42 
43     /*Ensure all configuration is written before enable*/
44 
45     mpu->CTRL |= MPU_CTRL_ENABLE_Msk;
46 
47     /* Enable MPU before next instruction */
48     __DSB();
49     __ISB();
50     return MPU_ARMV8M_OK;
51 }
52 
mpu_armv8m_disable(struct mpu_armv8m_dev_t * dev)53 enum mpu_armv8m_error_t mpu_armv8m_disable(struct mpu_armv8m_dev_t *dev)
54 {
55     MPU_Type *mpu = (MPU_Type *)dev->base;
56 
57     /* Reset all fields as enable does full setup */
58     mpu->CTRL = 0;
59 
60     return MPU_ARMV8M_OK;
61 }
62 
63 
mpu_armv8m_region_enable(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)64 enum mpu_armv8m_error_t mpu_armv8m_region_enable(
65                                 struct mpu_armv8m_dev_t *dev,
66                                 struct mpu_armv8m_region_cfg_t *region_cfg)
67 {
68     MPU_Type *mpu = (MPU_Type *)dev->base;
69 
70     enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_OK;
71     uint32_t ctrl_before;
72     uint32_t base_cfg;
73     uint32_t limit_cfg;
74 
75     /*FIXME : Add complete error checking*/
76     if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0) {
77         return MPU_ARMV8M_ERROR;
78     }
79     /* region_limit doesn't need to be aligned but the scatter
80      * file needs to be setup to ensure that partitions do not overlap.
81      */
82 
83     ctrl_before = mpu->CTRL;
84     mpu->CTRL = 0;
85 
86     mpu->RNR  = region_cfg->region_nr & MPU_RNR_REGION_Msk;
87 
88     /* This zeroes the lower bits of the base address */
89     base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
90     base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
91     base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
92     base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
93 
94     mpu->RBAR = base_cfg;
95 
96     /* This zeroes the lower bits of limit address but they are treated as 1 */
97     limit_cfg = (region_cfg->region_limit-1) & MPU_RLAR_LIMIT_Msk;
98 
99     limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
100                  MPU_RLAR_AttrIndx_Msk;
101 
102     limit_cfg |= MPU_RLAR_EN_Msk;
103 
104     mpu->RLAR = limit_cfg;
105 
106     /*Restore main MPU control*/
107     mpu->CTRL = ctrl_before;
108 
109     /* Enable MPU before the next instruction */
110     __DSB();
111     __ISB();
112 
113     return ret_val;
114 }
115 
116 
mpu_armv8m_region_disable(struct mpu_armv8m_dev_t * dev,uint32_t region_nr)117 enum mpu_armv8m_error_t mpu_armv8m_region_disable(
118                                 struct mpu_armv8m_dev_t *dev,
119                                 uint32_t region_nr)
120 {
121 
122     MPU_Type *mpu = (MPU_Type *)dev->base;
123 
124     enum mpu_armv8m_error_t ret_val = MPU_ARMV8M_OK;
125     uint32_t ctrl_before;
126 
127     /*FIXME : Add complete error checking*/
128 
129     ctrl_before = mpu->CTRL;
130     mpu->CTRL = 0;
131 
132     mpu->RNR  = region_nr & MPU_RNR_REGION_Msk;
133 
134     mpu->RBAR = 0;
135     mpu->RLAR = 0;
136 
137     /*Restore main MPU control*/
138     mpu->CTRL = ctrl_before;
139 
140     return ret_val;
141 }
142 
mpu_armv8m_clean(struct mpu_armv8m_dev_t * dev)143 enum mpu_armv8m_error_t mpu_armv8m_clean(struct mpu_armv8m_dev_t *dev)
144 {
145     MPU_Type *mpu = (MPU_Type *)dev->base;
146     uint32_t i = (mpu->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
147 
148     while (i > 0) {
149         mpu_armv8m_region_disable(dev, i-1);
150         i--;
151     }
152 
153     return MPU_ARMV8M_OK;
154 
155 }
156