Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arm / cpu / armv7 / mpu_v7r.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Cortex-R Memory Protection Unit specific code
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6  *      Lokesh Vutla <lokeshvutla@ti.com>
7  */
8
9 #include <common.h>
10 #include <command.h>
11 #include <cpu_func.h>
12 #include <asm/armv7.h>
13 #include <asm/system.h>
14 #include <asm/barriers.h>
15 #include <linux/bitops.h>
16 #include <linux/compiler.h>
17
18 #include <asm/armv7_mpu.h>
19
20 /* MPU Type register definitions */
21 #define MPUIR_S_SHIFT           0
22 #define MPUIR_S_MASK            BIT(MPUIR_S_SHIFT)
23 #define MPUIR_DREGION_SHIFT     8
24 #define MPUIR_DREGION_MASK      (0xff << 8)
25
26 /**
27  * Note:
28  * The Memory Protection Unit(MPU) allows to partition memory into regions
29  * and set individual protection attributes for each region. In absence
30  * of MPU a default map[1] will take effect. make sure to run this code
31  * from a region which has execution permissions by default.
32  * [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0460d/I1002400.html
33  */
34
35 void disable_mpu(void)
36 {
37         u32 reg;
38
39         reg = get_cr();
40         reg &= ~CR_M;
41         dsb();
42         set_cr(reg);
43         isb();
44 }
45
46 void enable_mpu(void)
47 {
48         u32 reg;
49
50         reg = get_cr();
51         reg |= CR_M;
52         dsb();
53         set_cr(reg);
54         isb();
55 }
56
57 int mpu_enabled(void)
58 {
59         return get_cr() & CR_M;
60 }
61
62 void mpu_config(struct mpu_region_config *rgn)
63 {
64         u32 attr, val;
65
66         attr = get_attr_encoding(rgn->mr_attr);
67
68         /* MPU Region Number Register */
69         asm volatile ("mcr p15, 0, %0, c6, c2, 0" : : "r" (rgn->region_no));
70
71         /* MPU Region Base Address Register */
72         asm volatile ("mcr p15, 0, %0, c6, c1, 0" : : "r" (rgn->start_addr));
73
74         /* MPU Region Size and Enable Register */
75         if (rgn->reg_size)
76                 val = (rgn->reg_size << REGION_SIZE_SHIFT) | ENABLE_REGION;
77         else
78                 val = DISABLE_REGION;
79         asm volatile ("mcr p15, 0, %0, c6, c1, 2" : : "r" (val));
80
81         /* MPU Region Access Control Register */
82         val = rgn->xn << XN_SHIFT | rgn->ap << AP_SHIFT | attr;
83         asm volatile ("mcr p15, 0, %0, c6, c1, 4" : : "r" (val));
84 }
85
86 void setup_mpu_regions(struct mpu_region_config *rgns, u32 num_rgns)
87 {
88         u32 num, i;
89
90         asm volatile ("mrc p15, 0, %0, c0, c0, 4" : "=r" (num));
91         num = (num & MPUIR_DREGION_MASK) >> MPUIR_DREGION_SHIFT;
92         /* Regions to be configured cannot be greater than available regions */
93         if (num < num_rgns)
94                 num_rgns = num;
95         /**
96          * Assuming dcache might not be enabled at this point, disabling
97          * and invalidating only icache.
98          */
99         icache_disable();
100         invalidate_icache_all();
101
102         disable_mpu();
103
104         for (i = 0; i < num_rgns; i++)
105                 mpu_config(&rgns[i]);
106
107         enable_mpu();
108
109         icache_enable();
110 }
111
112 void enable_caches(void)
113 {
114         /*
115          * setup_mpu_regions() might have enabled Icache. So add a check
116          * before enabling Icache
117          */
118         if (!icache_status())
119                 icache_enable();
120         dcache_enable();
121 }