183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2ea0364f1SPeter Tyser /*
3ea0364f1SPeter Tyser * (C) Copyright 2002
4ea0364f1SPeter Tyser * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5ea0364f1SPeter Tyser */
6ea0364f1SPeter Tyser
7ea0364f1SPeter Tyser #include <common.h>
8ea0364f1SPeter Tyser #include <asm/system.h>
996fdbec2SR Sricharan #include <asm/cache.h>
1096fdbec2SR Sricharan #include <linux/compiler.h>
11*a43d46a7SLokesh Vutla #include <asm/armv7_mpu.h>
12ea0364f1SPeter Tyser
13e47f2db5SAneesh V #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
14880eff5cSHeiko Schocher
15880eff5cSHeiko Schocher DECLARE_GLOBAL_DATA_PTR;
16880eff5cSHeiko Schocher
17*a43d46a7SLokesh Vutla #ifdef CONFIG_SYS_ARM_MMU
arm_init_before_mmu(void)18fcfddfd5SJeroen Hofstee __weak void arm_init_before_mmu(void)
19c2dd0d45SAneesh V {
20c2dd0d45SAneesh V }
21c2dd0d45SAneesh V
arm_init_domains(void)22de63ac27SR Sricharan __weak void arm_init_domains(void)
23de63ac27SR Sricharan {
24de63ac27SR Sricharan }
25de63ac27SR Sricharan
set_section_dcache(int section,enum dcache_option option)260dde7f53SSimon Glass void set_section_dcache(int section, enum dcache_option option)
27f1d2b313SHeiko Schocher {
28d990f5c8SAlexander Graf #ifdef CONFIG_ARMV7_LPAE
29d990f5c8SAlexander Graf u64 *page_table = (u64 *)gd->arch.tlb_addr;
30d990f5c8SAlexander Graf /* Need to set the access flag to not fault */
31d990f5c8SAlexander Graf u64 value = TTB_SECT_AP | TTB_SECT_AF;
32d990f5c8SAlexander Graf #else
3334fd5d25SSimon Glass u32 *page_table = (u32 *)gd->arch.tlb_addr;
34d990f5c8SAlexander Graf u32 value = TTB_SECT_AP;
35d990f5c8SAlexander Graf #endif
360dde7f53SSimon Glass
37d990f5c8SAlexander Graf /* Add the page offset */
38d990f5c8SAlexander Graf value |= ((u32)section << MMU_SECTION_SHIFT);
39d990f5c8SAlexander Graf
40d990f5c8SAlexander Graf /* Add caching bits */
410dde7f53SSimon Glass value |= option;
42d990f5c8SAlexander Graf
43d990f5c8SAlexander Graf /* Set PTE */
440dde7f53SSimon Glass page_table[section] = value;
450dde7f53SSimon Glass }
460dde7f53SSimon Glass
mmu_page_table_flush(unsigned long start,unsigned long stop)47fcfddfd5SJeroen Hofstee __weak void mmu_page_table_flush(unsigned long start, unsigned long stop)
480dde7f53SSimon Glass {
490dde7f53SSimon Glass debug("%s: Warning: not implemented\n", __func__);
500dde7f53SSimon Glass }
510dde7f53SSimon Glass
mmu_set_region_dcache_behaviour(phys_addr_t start,size_t size,enum dcache_option option)5225026fa9SThierry Reding void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
530dde7f53SSimon Glass enum dcache_option option)
540dde7f53SSimon Glass {
55c5b3cabfSStefan Agner #ifdef CONFIG_ARMV7_LPAE
56c5b3cabfSStefan Agner u64 *page_table = (u64 *)gd->arch.tlb_addr;
57c5b3cabfSStefan Agner #else
5834fd5d25SSimon Glass u32 *page_table = (u32 *)gd->arch.tlb_addr;
59c5b3cabfSStefan Agner #endif
608f894a4dSStefan Agner unsigned long startpt, stoppt;
6125026fa9SThierry Reding unsigned long upto, end;
620dde7f53SSimon Glass
630dde7f53SSimon Glass end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT;
640dde7f53SSimon Glass start = start >> MMU_SECTION_SHIFT;
6506d43c80SKeerthy #ifdef CONFIG_ARMV7_LPAE
6606d43c80SKeerthy debug("%s: start=%pa, size=%zu, option=%llx\n", __func__, &start, size,
6706d43c80SKeerthy option);
6806d43c80SKeerthy #else
692b373cb8SKeerthy debug("%s: start=%pa, size=%zu, option=0x%x\n", __func__, &start, size,
700dde7f53SSimon Glass option);
7106d43c80SKeerthy #endif
720dde7f53SSimon Glass for (upto = start; upto < end; upto++)
730dde7f53SSimon Glass set_section_dcache(upto, option);
748f894a4dSStefan Agner
758f894a4dSStefan Agner /*
768f894a4dSStefan Agner * Make sure range is cache line aligned
778f894a4dSStefan Agner * Only CPU maintains page tables, hence it is safe to always
788f894a4dSStefan Agner * flush complete cache lines...
798f894a4dSStefan Agner */
808f894a4dSStefan Agner
818f894a4dSStefan Agner startpt = (unsigned long)&page_table[start];
828f894a4dSStefan Agner startpt &= ~(CONFIG_SYS_CACHELINE_SIZE - 1);
838f894a4dSStefan Agner stoppt = (unsigned long)&page_table[end];
848f894a4dSStefan Agner stoppt = ALIGN(stoppt, CONFIG_SYS_CACHELINE_SIZE);
858f894a4dSStefan Agner mmu_page_table_flush(startpt, stoppt);
860dde7f53SSimon Glass }
870dde7f53SSimon Glass
dram_bank_mmu_setup(int bank)8896fdbec2SR Sricharan __weak void dram_bank_mmu_setup(int bank)
890dde7f53SSimon Glass {
90f1d2b313SHeiko Schocher bd_t *bd = gd->bd;
91f1d2b313SHeiko Schocher int i;
92f1d2b313SHeiko Schocher
93f1d2b313SHeiko Schocher debug("%s: bank: %d\n", __func__, bank);
94d990f5c8SAlexander Graf for (i = bd->bi_dram[bank].start >> MMU_SECTION_SHIFT;
95d990f5c8SAlexander Graf i < (bd->bi_dram[bank].start >> MMU_SECTION_SHIFT) +
96d990f5c8SAlexander Graf (bd->bi_dram[bank].size >> MMU_SECTION_SHIFT);
97f1d2b313SHeiko Schocher i++) {
980dde7f53SSimon Glass #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
990dde7f53SSimon Glass set_section_dcache(i, DCACHE_WRITETHROUGH);
100ff7e9700SMarek Vasut #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
101ff7e9700SMarek Vasut set_section_dcache(i, DCACHE_WRITEALLOC);
1020dde7f53SSimon Glass #else
1030dde7f53SSimon Glass set_section_dcache(i, DCACHE_WRITEBACK);
1040dde7f53SSimon Glass #endif
105f1d2b313SHeiko Schocher }
106f1d2b313SHeiko Schocher }
107f1d2b313SHeiko Schocher
108f1d2b313SHeiko Schocher /* to activate the MMU we need to set up virtual memory: use 1M areas */
mmu_setup(void)109880eff5cSHeiko Schocher static inline void mmu_setup(void)
110880eff5cSHeiko Schocher {
111f1d2b313SHeiko Schocher int i;
112880eff5cSHeiko Schocher u32 reg;
113880eff5cSHeiko Schocher
114c2dd0d45SAneesh V arm_init_before_mmu();
115880eff5cSHeiko Schocher /* Set up an identity-mapping for all 4GB, rw for everyone */
116d990f5c8SAlexander Graf for (i = 0; i < ((4096ULL * 1024 * 1024) >> MMU_SECTION_SHIFT); i++)
1170dde7f53SSimon Glass set_section_dcache(i, DCACHE_OFF);
118f1d2b313SHeiko Schocher
119f1d2b313SHeiko Schocher for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
120f1d2b313SHeiko Schocher dram_bank_mmu_setup(i);
121f1d2b313SHeiko Schocher }
122880eff5cSHeiko Schocher
12310d602acSSimon Glass #if defined(CONFIG_ARMV7_LPAE) && __LINUX_ARM_ARCH__ != 4
124d990f5c8SAlexander Graf /* Set up 4 PTE entries pointing to our 4 1GB page tables */
125d990f5c8SAlexander Graf for (i = 0; i < 4; i++) {
126d990f5c8SAlexander Graf u64 *page_table = (u64 *)(gd->arch.tlb_addr + (4096 * 4));
127d990f5c8SAlexander Graf u64 tpt = gd->arch.tlb_addr + (4096 * i);
128d990f5c8SAlexander Graf page_table[i] = tpt | TTB_PAGETABLE;
129d990f5c8SAlexander Graf }
130d990f5c8SAlexander Graf
131d990f5c8SAlexander Graf reg = TTBCR_EAE;
132d990f5c8SAlexander Graf #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
133d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WT | TTBCR_IRGN0_WT;
134d990f5c8SAlexander Graf #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
135d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WBWA | TTBCR_IRGN0_WBWA;
136d990f5c8SAlexander Graf #else
137d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WBNWA | TTBCR_IRGN0_WBNWA;
138d990f5c8SAlexander Graf #endif
139d990f5c8SAlexander Graf
140d990f5c8SAlexander Graf if (is_hyp()) {
141579dfca2SSimon Glass /* Set HTCR to enable LPAE */
142d990f5c8SAlexander Graf asm volatile("mcr p15, 4, %0, c2, c0, 2"
143d990f5c8SAlexander Graf : : "r" (reg) : "memory");
144d990f5c8SAlexander Graf /* Set HTTBR0 */
145d990f5c8SAlexander Graf asm volatile("mcrr p15, 4, %0, %1, c2"
146d990f5c8SAlexander Graf :
147d990f5c8SAlexander Graf : "r"(gd->arch.tlb_addr + (4096 * 4)), "r"(0)
148d990f5c8SAlexander Graf : "memory");
149d990f5c8SAlexander Graf /* Set HMAIR */
150d990f5c8SAlexander Graf asm volatile("mcr p15, 4, %0, c10, c2, 0"
151d990f5c8SAlexander Graf : : "r" (MEMORY_ATTRIBUTES) : "memory");
152d990f5c8SAlexander Graf } else {
153d990f5c8SAlexander Graf /* Set TTBCR to enable LPAE */
154d990f5c8SAlexander Graf asm volatile("mcr p15, 0, %0, c2, c0, 2"
155d990f5c8SAlexander Graf : : "r" (reg) : "memory");
156d990f5c8SAlexander Graf /* Set 64-bit TTBR0 */
157d990f5c8SAlexander Graf asm volatile("mcrr p15, 0, %0, %1, c2"
158d990f5c8SAlexander Graf :
159d990f5c8SAlexander Graf : "r"(gd->arch.tlb_addr + (4096 * 4)), "r"(0)
160d990f5c8SAlexander Graf : "memory");
161d990f5c8SAlexander Graf /* Set MAIR */
162d990f5c8SAlexander Graf asm volatile("mcr p15, 0, %0, c10, c2, 0"
163d990f5c8SAlexander Graf : : "r" (MEMORY_ATTRIBUTES) : "memory");
164d990f5c8SAlexander Graf }
165acf15001SLokesh Vutla #elif defined(CONFIG_CPU_V7A)
16650a4886bSSimon Glass if (is_hyp()) {
16750a4886bSSimon Glass /* Set HTCR to disable LPAE */
16850a4886bSSimon Glass asm volatile("mcr p15, 4, %0, c2, c0, 2"
16950a4886bSSimon Glass : : "r" (0) : "memory");
17050a4886bSSimon Glass } else {
17150a4886bSSimon Glass /* Set TTBCR to disable LPAE */
17250a4886bSSimon Glass asm volatile("mcr p15, 0, %0, c2, c0, 2"
17350a4886bSSimon Glass : : "r" (0) : "memory");
17450a4886bSSimon Glass }
17597840b5dSBryan Brinsko /* Set TTBR0 */
17697840b5dSBryan Brinsko reg = gd->arch.tlb_addr & TTBR0_BASE_ADDR_MASK;
17797840b5dSBryan Brinsko #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
17897840b5dSBryan Brinsko reg |= TTBR0_RGN_WT | TTBR0_IRGN_WT;
17997840b5dSBryan Brinsko #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
18097840b5dSBryan Brinsko reg |= TTBR0_RGN_WBWA | TTBR0_IRGN_WBWA;
18197840b5dSBryan Brinsko #else
18297840b5dSBryan Brinsko reg |= TTBR0_RGN_WB | TTBR0_IRGN_WB;
18397840b5dSBryan Brinsko #endif
18497840b5dSBryan Brinsko asm volatile("mcr p15, 0, %0, c2, c0, 0"
18597840b5dSBryan Brinsko : : "r" (reg) : "memory");
18697840b5dSBryan Brinsko #else
187880eff5cSHeiko Schocher /* Copy the page table address to cp15 */
188880eff5cSHeiko Schocher asm volatile("mcr p15, 0, %0, c2, c0, 0"
18934fd5d25SSimon Glass : : "r" (gd->arch.tlb_addr) : "memory");
19097840b5dSBryan Brinsko #endif
191880eff5cSHeiko Schocher /* Set the access control to all-supervisor */
192880eff5cSHeiko Schocher asm volatile("mcr p15, 0, %0, c3, c0, 0"
193880eff5cSHeiko Schocher : : "r" (~0));
194de63ac27SR Sricharan
195de63ac27SR Sricharan arm_init_domains();
196de63ac27SR Sricharan
197880eff5cSHeiko Schocher /* and enable the mmu */
198880eff5cSHeiko Schocher reg = get_cr(); /* get control reg. */
199880eff5cSHeiko Schocher set_cr(reg | CR_M);
200ea0364f1SPeter Tyser }
201ea0364f1SPeter Tyser
mmu_enabled(void)202e05f0079SAneesh V static int mmu_enabled(void)
203e05f0079SAneesh V {
204e05f0079SAneesh V return get_cr() & CR_M;
205e05f0079SAneesh V }
206*a43d46a7SLokesh Vutla #endif /* CONFIG_SYS_ARM_MMU */
207e05f0079SAneesh V
208ea0364f1SPeter Tyser /* cache_bit must be either CR_I or CR_C */
cache_enable(uint32_t cache_bit)209ea0364f1SPeter Tyser static void cache_enable(uint32_t cache_bit)
210ea0364f1SPeter Tyser {
211ea0364f1SPeter Tyser uint32_t reg;
212ea0364f1SPeter Tyser
213*a43d46a7SLokesh Vutla /* The data cache is not active unless the mmu/mpu is enabled too */
214*a43d46a7SLokesh Vutla #ifdef CONFIG_SYS_ARM_MMU
215e05f0079SAneesh V if ((cache_bit == CR_C) && !mmu_enabled())
216880eff5cSHeiko Schocher mmu_setup();
217*a43d46a7SLokesh Vutla #elif defined(CONFIG_SYS_ARM_MPU)
218*a43d46a7SLokesh Vutla if ((cache_bit == CR_C) && !mpu_enabled()) {
219*a43d46a7SLokesh Vutla printf("Consider enabling MPU before enabling caches\n");
220*a43d46a7SLokesh Vutla return;
221*a43d46a7SLokesh Vutla }
222*a43d46a7SLokesh Vutla #endif
223ea0364f1SPeter Tyser reg = get_cr(); /* get control reg. */
224ea0364f1SPeter Tyser set_cr(reg | cache_bit);
225ea0364f1SPeter Tyser }
226ea0364f1SPeter Tyser
227ea0364f1SPeter Tyser /* cache_bit must be either CR_I or CR_C */
cache_disable(uint32_t cache_bit)228ea0364f1SPeter Tyser static void cache_disable(uint32_t cache_bit)
229ea0364f1SPeter Tyser {
230ea0364f1SPeter Tyser uint32_t reg;
231ea0364f1SPeter Tyser
232d702b081SSRICHARAN R reg = get_cr();
233d702b081SSRICHARAN R
234880eff5cSHeiko Schocher if (cache_bit == CR_C) {
235f1d2b313SHeiko Schocher /* if cache isn;t enabled no need to disable */
236f1d2b313SHeiko Schocher if ((reg & CR_C) != CR_C)
237f1d2b313SHeiko Schocher return;
238880eff5cSHeiko Schocher /* if disabling data cache, disable mmu too */
239880eff5cSHeiko Schocher cache_bit |= CR_M;
240880eff5cSHeiko Schocher }
24144df5e8dSArun Mankuzhi reg = get_cr();
24253d4ed70SLothar Waßmann
24344df5e8dSArun Mankuzhi if (cache_bit == (CR_C | CR_M))
24444df5e8dSArun Mankuzhi flush_dcache_all();
245ea0364f1SPeter Tyser set_cr(reg & ~cache_bit);
246ea0364f1SPeter Tyser }
247ea0364f1SPeter Tyser #endif
248ea0364f1SPeter Tyser
249e47f2db5SAneesh V #ifdef CONFIG_SYS_ICACHE_OFF
icache_enable(void)250ea0364f1SPeter Tyser void icache_enable (void)
251ea0364f1SPeter Tyser {
252ea0364f1SPeter Tyser return;
253ea0364f1SPeter Tyser }
254ea0364f1SPeter Tyser
icache_disable(void)255ea0364f1SPeter Tyser void icache_disable (void)
256ea0364f1SPeter Tyser {
257ea0364f1SPeter Tyser return;
258ea0364f1SPeter Tyser }
259ea0364f1SPeter Tyser
icache_status(void)260ea0364f1SPeter Tyser int icache_status (void)
261ea0364f1SPeter Tyser {
262ea0364f1SPeter Tyser return 0; /* always off */
263ea0364f1SPeter Tyser }
264ea0364f1SPeter Tyser #else
icache_enable(void)265ea0364f1SPeter Tyser void icache_enable(void)
266ea0364f1SPeter Tyser {
267ea0364f1SPeter Tyser cache_enable(CR_I);
268ea0364f1SPeter Tyser }
269ea0364f1SPeter Tyser
icache_disable(void)270ea0364f1SPeter Tyser void icache_disable(void)
271ea0364f1SPeter Tyser {
272ea0364f1SPeter Tyser cache_disable(CR_I);
273ea0364f1SPeter Tyser }
274ea0364f1SPeter Tyser
icache_status(void)275ea0364f1SPeter Tyser int icache_status(void)
276ea0364f1SPeter Tyser {
277ea0364f1SPeter Tyser return (get_cr() & CR_I) != 0;
278ea0364f1SPeter Tyser }
279ea0364f1SPeter Tyser #endif
280ea0364f1SPeter Tyser
281e47f2db5SAneesh V #ifdef CONFIG_SYS_DCACHE_OFF
dcache_enable(void)282ea0364f1SPeter Tyser void dcache_enable (void)
283ea0364f1SPeter Tyser {
284ea0364f1SPeter Tyser return;
285ea0364f1SPeter Tyser }
286ea0364f1SPeter Tyser
dcache_disable(void)287ea0364f1SPeter Tyser void dcache_disable (void)
288ea0364f1SPeter Tyser {
289ea0364f1SPeter Tyser return;
290ea0364f1SPeter Tyser }
291ea0364f1SPeter Tyser
dcache_status(void)292ea0364f1SPeter Tyser int dcache_status (void)
293ea0364f1SPeter Tyser {
294ea0364f1SPeter Tyser return 0; /* always off */
295ea0364f1SPeter Tyser }
296ea0364f1SPeter Tyser #else
dcache_enable(void)297ea0364f1SPeter Tyser void dcache_enable(void)
298ea0364f1SPeter Tyser {
299ea0364f1SPeter Tyser cache_enable(CR_C);
300ea0364f1SPeter Tyser }
301ea0364f1SPeter Tyser
dcache_disable(void)302ea0364f1SPeter Tyser void dcache_disable(void)
303ea0364f1SPeter Tyser {
304ea0364f1SPeter Tyser cache_disable(CR_C);
305ea0364f1SPeter Tyser }
306ea0364f1SPeter Tyser
dcache_status(void)307ea0364f1SPeter Tyser int dcache_status(void)
308ea0364f1SPeter Tyser {
309ea0364f1SPeter Tyser return (get_cr() & CR_C) != 0;
310ea0364f1SPeter Tyser }
311ea0364f1SPeter Tyser #endif
312