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