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  */
mpu_armv8m_enable(struct mpu_armv8m_dev_t * dev,uint32_t privdef_en,uint32_t hfnmi_en)18 FIH_RET_TYPE(enum mpu_armv8m_error_t) mpu_armv8m_enable(
19                                           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 
51     FIH_RET(fih_int_encode(MPU_ARMV8M_OK));
52 }
53 
mpu_armv8m_disable(struct mpu_armv8m_dev_t * dev)54 enum mpu_armv8m_error_t mpu_armv8m_disable(struct mpu_armv8m_dev_t *dev)
55 {
56     MPU_Type *mpu = (MPU_Type *)dev->base;
57 
58     /* Reset all fields as enable does full setup */
59     mpu->CTRL = 0;
60 
61     return MPU_ARMV8M_OK;
62 }
63 
mpu_armv8m_region_enable(struct mpu_armv8m_dev_t * dev,struct mpu_armv8m_region_cfg_t * region_cfg)64 FIH_RET_TYPE(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     uint32_t ctrl_before;
71     uint32_t base_cfg;
72     uint32_t limit_cfg;
73 
74     /* FIXME : Add region-overlap error check */
75     if ((region_cfg->region_base & ~MPU_RBAR_BASE_Msk) != 0) {
76         FIH_RET(fih_int_encode(MPU_ARMV8M_ERROR));
77     }
78     if ((region_cfg->region_limit & ~MPU_RLAR_LIMIT_Msk) != 0x1F) {
79         FIH_RET(fih_int_encode(MPU_ARMV8M_ERROR));
80     }
81 
82     ctrl_before = mpu->CTRL;
83     mpu->CTRL = 0;
84 
85     mpu->RNR  = region_cfg->region_nr & MPU_RNR_REGION_Msk;
86 
87     /* This zeroes the lower bits of the base address */
88     base_cfg = region_cfg->region_base & MPU_RBAR_BASE_Msk;
89     base_cfg |= (region_cfg->attr_sh << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk;
90     base_cfg |= (region_cfg->attr_access << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk;
91     base_cfg |= (region_cfg->attr_exec << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk;
92 
93     mpu->RBAR = base_cfg;
94 
95     /* MPU Region Limit Address Register is used to set the limit address, the
96      * attribute set, and to enable/disable the seleted region.
97      * These parameters are passed through the lower 5 bits of this register.
98      * These bits are discarded and treated as ones when decoding the address.
99      */
100     limit_cfg = (region_cfg->region_limit) & MPU_RLAR_LIMIT_Msk;
101 
102     limit_cfg |= (region_cfg->region_attridx << MPU_RLAR_AttrIndx_Pos) &
103                  MPU_RLAR_AttrIndx_Msk;
104 
105 #ifdef TFM_PXN_ENABLE
106     limit_cfg |= (region_cfg->attr_pxn << MPU_RLAR_PXN_Pos) & MPU_RLAR_PXN_Msk;
107 #endif
108 
109     limit_cfg |= MPU_RLAR_EN_Msk;
110 
111     mpu->RLAR = limit_cfg;
112 
113     /*Restore main MPU control*/
114     mpu->CTRL = ctrl_before;
115 
116     /* Enable MPU before the next instruction */
117     __DSB();
118     __ISB();
119 
120     FIH_RET(fih_int_encode(MPU_ARMV8M_OK));
121 }
122 
mpu_armv8m_region_disable(struct mpu_armv8m_dev_t * dev,uint32_t region_nr)123 FIH_RET_TYPE(enum mpu_armv8m_error_t) mpu_armv8m_region_disable(
124                                                   struct mpu_armv8m_dev_t *dev,
125                                                   uint32_t region_nr)
126 {
127 
128     MPU_Type *mpu = (MPU_Type *)dev->base;
129     uint32_t ctrl_before;
130 
131     /*FIXME : Add complete error checking*/
132 
133     ctrl_before = mpu->CTRL;
134     mpu->CTRL = 0;
135 
136     mpu->RNR  = region_nr & MPU_RNR_REGION_Msk;
137 
138     mpu->RBAR = 0;
139     mpu->RLAR = 0;
140 
141     /*Restore main MPU control*/
142     mpu->CTRL = ctrl_before;
143 
144     FIH_RET(fih_int_encode(MPU_ARMV8M_OK));
145 }
146 
mpu_armv8m_clean(struct mpu_armv8m_dev_t * dev)147 enum mpu_armv8m_error_t mpu_armv8m_clean(struct mpu_armv8m_dev_t *dev)
148 {
149     MPU_Type *mpu = (MPU_Type *)dev->base;
150     uint32_t i = (mpu->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
151     fih_int fih_rc = FIH_FAILURE;
152 
153     while (i > 0) {
154         FIH_CALL(mpu_armv8m_region_disable, fih_rc, dev, i - 1);
155         i--;
156     }
157 
158     return MPU_ARMV8M_OK;
159 }
160