1ea0364f1SPeter Tyser /*
2ea0364f1SPeter Tyser * (C) Copyright 2002
3ea0364f1SPeter Tyser * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4ea0364f1SPeter Tyser *
51a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
6ea0364f1SPeter Tyser */
7ea0364f1SPeter Tyser
8ea0364f1SPeter Tyser #include <common.h>
9ea0364f1SPeter Tyser #include <asm/system.h>
1096fdbec2SR Sricharan #include <asm/cache.h>
1196fdbec2SR Sricharan #include <linux/compiler.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
arm_init_before_mmu(void)17fcfddfd5SJeroen Hofstee __weak void arm_init_before_mmu(void)
18c2dd0d45SAneesh V {
19c2dd0d45SAneesh V }
20c2dd0d45SAneesh V
arm_init_domains(void)21de63ac27SR Sricharan __weak void arm_init_domains(void)
22de63ac27SR Sricharan {
23de63ac27SR Sricharan }
24de63ac27SR Sricharan
set_section_dcache(int section,enum dcache_option option)250dde7f53SSimon Glass void set_section_dcache(int section, enum dcache_option option)
26f1d2b313SHeiko Schocher {
27d990f5c8SAlexander Graf #ifdef CONFIG_ARMV7_LPAE
28d990f5c8SAlexander Graf u64 *page_table = (u64 *)gd->arch.tlb_addr;
29d990f5c8SAlexander Graf /* Need to set the access flag to not fault */
30d990f5c8SAlexander Graf u64 value = TTB_SECT_AP | TTB_SECT_AF;
31d990f5c8SAlexander Graf #else
3234fd5d25SSimon Glass u32 *page_table = (u32 *)gd->arch.tlb_addr;
33d990f5c8SAlexander Graf u32 value = TTB_SECT_AP;
34d990f5c8SAlexander Graf #endif
350dde7f53SSimon Glass
36d990f5c8SAlexander Graf /* Add the page offset */
37d990f5c8SAlexander Graf value |= ((u32)section << MMU_SECTION_SHIFT);
38d990f5c8SAlexander Graf
39d990f5c8SAlexander Graf /* Add caching bits */
400dde7f53SSimon Glass value |= option;
41d990f5c8SAlexander Graf
42d990f5c8SAlexander Graf /* Set PTE */
430dde7f53SSimon Glass page_table[section] = value;
440dde7f53SSimon Glass }
450dde7f53SSimon Glass
mmu_page_table_flush(unsigned long start,unsigned long stop)46fcfddfd5SJeroen Hofstee __weak void mmu_page_table_flush(unsigned long start, unsigned long stop)
470dde7f53SSimon Glass {
480dde7f53SSimon Glass debug("%s: Warning: not implemented\n", __func__);
490dde7f53SSimon Glass }
500dde7f53SSimon Glass
mmu_set_region_dcache_behaviour(phys_addr_t start,size_t size,enum dcache_option option)5125026fa9SThierry Reding void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
520dde7f53SSimon Glass enum dcache_option option)
530dde7f53SSimon Glass {
54c5b3cabfSStefan Agner #ifdef CONFIG_ARMV7_LPAE
55c5b3cabfSStefan Agner u64 *page_table = (u64 *)gd->arch.tlb_addr;
56c5b3cabfSStefan Agner #else
5734fd5d25SSimon Glass u32 *page_table = (u32 *)gd->arch.tlb_addr;
58c5b3cabfSStefan Agner #endif
598f894a4dSStefan Agner unsigned long startpt, stoppt;
6025026fa9SThierry Reding unsigned long upto, end;
610dde7f53SSimon Glass
620dde7f53SSimon Glass end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT;
630dde7f53SSimon Glass start = start >> MMU_SECTION_SHIFT;
6406d43c80SKeerthy #ifdef CONFIG_ARMV7_LPAE
6506d43c80SKeerthy debug("%s: start=%pa, size=%zu, option=%llx\n", __func__, &start, size,
6606d43c80SKeerthy option);
6706d43c80SKeerthy #else
682b373cb8SKeerthy debug("%s: start=%pa, size=%zu, option=0x%x\n", __func__, &start, size,
690dde7f53SSimon Glass option);
7006d43c80SKeerthy #endif
710dde7f53SSimon Glass for (upto = start; upto < end; upto++)
720dde7f53SSimon Glass set_section_dcache(upto, option);
738f894a4dSStefan Agner
748f894a4dSStefan Agner /*
758f894a4dSStefan Agner * Make sure range is cache line aligned
768f894a4dSStefan Agner * Only CPU maintains page tables, hence it is safe to always
778f894a4dSStefan Agner * flush complete cache lines...
788f894a4dSStefan Agner */
798f894a4dSStefan Agner
808f894a4dSStefan Agner startpt = (unsigned long)&page_table[start];
818f894a4dSStefan Agner startpt &= ~(CONFIG_SYS_CACHELINE_SIZE - 1);
828f894a4dSStefan Agner stoppt = (unsigned long)&page_table[end];
838f894a4dSStefan Agner stoppt = ALIGN(stoppt, CONFIG_SYS_CACHELINE_SIZE);
848f894a4dSStefan Agner mmu_page_table_flush(startpt, stoppt);
850dde7f53SSimon Glass }
860dde7f53SSimon Glass
dram_bank_mmu_setup(int bank)8796fdbec2SR Sricharan __weak void dram_bank_mmu_setup(int bank)
880dde7f53SSimon Glass {
89f1d2b313SHeiko Schocher bd_t *bd = gd->bd;
90f1d2b313SHeiko Schocher int i;
91f1d2b313SHeiko Schocher
92f1d2b313SHeiko Schocher debug("%s: bank: %d\n", __func__, bank);
93d990f5c8SAlexander Graf for (i = bd->bi_dram[bank].start >> MMU_SECTION_SHIFT;
94d990f5c8SAlexander Graf i < (bd->bi_dram[bank].start >> MMU_SECTION_SHIFT) +
95d990f5c8SAlexander Graf (bd->bi_dram[bank].size >> MMU_SECTION_SHIFT);
96f1d2b313SHeiko Schocher i++) {
970dde7f53SSimon Glass #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
980dde7f53SSimon Glass set_section_dcache(i, DCACHE_WRITETHROUGH);
99ff7e9700SMarek Vasut #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
100ff7e9700SMarek Vasut set_section_dcache(i, DCACHE_WRITEALLOC);
1010dde7f53SSimon Glass #else
1020dde7f53SSimon Glass set_section_dcache(i, DCACHE_WRITEBACK);
1030dde7f53SSimon Glass #endif
104f1d2b313SHeiko Schocher }
105f1d2b313SHeiko Schocher }
106f1d2b313SHeiko Schocher
107f1d2b313SHeiko Schocher /* to activate the MMU we need to set up virtual memory: use 1M areas */
mmu_setup(void)108880eff5cSHeiko Schocher static inline void mmu_setup(void)
109880eff5cSHeiko Schocher {
1102a3fb7bbSJoseph Chen int i, end;
111880eff5cSHeiko Schocher u32 reg;
112880eff5cSHeiko Schocher
1135c8c82beSJason Zhu #ifndef CONFIG_SPL_BUILD
1145c8c82beSJason Zhu /* bootrom and ddr didn't initial dcache,
1155c8c82beSJason Zhu * skip this to save boot time.
1165c8c82beSJason Zhu */
117c2dd0d45SAneesh V arm_init_before_mmu();
1185c8c82beSJason Zhu #endif
1192a3fb7bbSJoseph Chen
1202a3fb7bbSJoseph Chen /*
1212a3fb7bbSJoseph Chen * SPL thunder-boot:
1222a3fb7bbSJoseph Chen * only map periph device region to save boot time.
1232a3fb7bbSJoseph Chen */
1242a3fb7bbSJoseph Chen #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_KERNEL_BOOT) && \
125*1d22de7fSJoseph Chen defined(CONFIG_PERIPH_DEVICE_START_ADDR)
126*1d22de7fSJoseph Chen i = CONFIG_PERIPH_DEVICE_START_ADDR >> MMU_SECTION_SHIFT;
127*1d22de7fSJoseph Chen end = CONFIG_PERIPH_DEVICE_END_ADDR >> MMU_SECTION_SHIFT;
1282a3fb7bbSJoseph Chen #else
1292a3fb7bbSJoseph Chen i = 0;
1302a3fb7bbSJoseph Chen end = (4096ULL * 1024 * 1024) >> MMU_SECTION_SHIFT;
1312a3fb7bbSJoseph Chen #endif
132880eff5cSHeiko Schocher /* Set up an identity-mapping for all 4GB, rw for everyone */
1332a3fb7bbSJoseph Chen for (; i < end; i++)
1340dde7f53SSimon Glass set_section_dcache(i, DCACHE_OFF);
135f1d2b313SHeiko Schocher
136f1d2b313SHeiko Schocher for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
137f1d2b313SHeiko Schocher dram_bank_mmu_setup(i);
138f1d2b313SHeiko Schocher }
139880eff5cSHeiko Schocher
14010d602acSSimon Glass #if defined(CONFIG_ARMV7_LPAE) && __LINUX_ARM_ARCH__ != 4
141d990f5c8SAlexander Graf /* Set up 4 PTE entries pointing to our 4 1GB page tables */
142d990f5c8SAlexander Graf for (i = 0; i < 4; i++) {
143d990f5c8SAlexander Graf u64 *page_table = (u64 *)(gd->arch.tlb_addr + (4096 * 4));
144d990f5c8SAlexander Graf u64 tpt = gd->arch.tlb_addr + (4096 * i);
145d990f5c8SAlexander Graf page_table[i] = tpt | TTB_PAGETABLE;
146d990f5c8SAlexander Graf }
147d990f5c8SAlexander Graf
148d990f5c8SAlexander Graf reg = TTBCR_EAE;
149d990f5c8SAlexander Graf #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
150d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WT | TTBCR_IRGN0_WT;
151d990f5c8SAlexander Graf #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
152d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WBWA | TTBCR_IRGN0_WBWA;
153d990f5c8SAlexander Graf #else
154d990f5c8SAlexander Graf reg |= TTBCR_ORGN0_WBNWA | TTBCR_IRGN0_WBNWA;
155d990f5c8SAlexander Graf #endif
156d990f5c8SAlexander Graf
157d990f5c8SAlexander Graf if (is_hyp()) {
158579dfca2SSimon Glass /* Set HTCR to enable LPAE */
159d990f5c8SAlexander Graf asm volatile("mcr p15, 4, %0, c2, c0, 2"
160d990f5c8SAlexander Graf : : "r" (reg) : "memory");
161d990f5c8SAlexander Graf /* Set HTTBR0 */
162d990f5c8SAlexander Graf asm volatile("mcrr p15, 4, %0, %1, c2"
163d990f5c8SAlexander Graf :
164d990f5c8SAlexander Graf : "r"(gd->arch.tlb_addr + (4096 * 4)), "r"(0)
165d990f5c8SAlexander Graf : "memory");
166d990f5c8SAlexander Graf /* Set HMAIR */
167d990f5c8SAlexander Graf asm volatile("mcr p15, 4, %0, c10, c2, 0"
168d990f5c8SAlexander Graf : : "r" (MEMORY_ATTRIBUTES) : "memory");
169d990f5c8SAlexander Graf } else {
170d990f5c8SAlexander Graf /* Set TTBCR to enable LPAE */
171d990f5c8SAlexander Graf asm volatile("mcr p15, 0, %0, c2, c0, 2"
172d990f5c8SAlexander Graf : : "r" (reg) : "memory");
173d990f5c8SAlexander Graf /* Set 64-bit TTBR0 */
174d990f5c8SAlexander Graf asm volatile("mcrr p15, 0, %0, %1, c2"
175d990f5c8SAlexander Graf :
176d990f5c8SAlexander Graf : "r"(gd->arch.tlb_addr + (4096 * 4)), "r"(0)
177d990f5c8SAlexander Graf : "memory");
178d990f5c8SAlexander Graf /* Set MAIR */
179d990f5c8SAlexander Graf asm volatile("mcr p15, 0, %0, c10, c2, 0"
180d990f5c8SAlexander Graf : : "r" (MEMORY_ATTRIBUTES) : "memory");
181d990f5c8SAlexander Graf }
182d990f5c8SAlexander Graf #elif defined(CONFIG_CPU_V7)
18350a4886bSSimon Glass if (is_hyp()) {
18450a4886bSSimon Glass /* Set HTCR to disable LPAE */
18550a4886bSSimon Glass asm volatile("mcr p15, 4, %0, c2, c0, 2"
18650a4886bSSimon Glass : : "r" (0) : "memory");
18750a4886bSSimon Glass } else {
18850a4886bSSimon Glass /* Set TTBCR to disable LPAE */
18950a4886bSSimon Glass asm volatile("mcr p15, 0, %0, c2, c0, 2"
19050a4886bSSimon Glass : : "r" (0) : "memory");
19150a4886bSSimon Glass }
19297840b5dSBryan Brinsko /* Set TTBR0 */
19397840b5dSBryan Brinsko reg = gd->arch.tlb_addr & TTBR0_BASE_ADDR_MASK;
19497840b5dSBryan Brinsko #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
19597840b5dSBryan Brinsko reg |= TTBR0_RGN_WT | TTBR0_IRGN_WT;
19697840b5dSBryan Brinsko #elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC)
19797840b5dSBryan Brinsko reg |= TTBR0_RGN_WBWA | TTBR0_IRGN_WBWA;
19897840b5dSBryan Brinsko #else
19997840b5dSBryan Brinsko reg |= TTBR0_RGN_WB | TTBR0_IRGN_WB;
20097840b5dSBryan Brinsko #endif
20197840b5dSBryan Brinsko asm volatile("mcr p15, 0, %0, c2, c0, 0"
20297840b5dSBryan Brinsko : : "r" (reg) : "memory");
20397840b5dSBryan Brinsko #else
204880eff5cSHeiko Schocher /* Copy the page table address to cp15 */
205880eff5cSHeiko Schocher asm volatile("mcr p15, 0, %0, c2, c0, 0"
20634fd5d25SSimon Glass : : "r" (gd->arch.tlb_addr) : "memory");
20797840b5dSBryan Brinsko #endif
208880eff5cSHeiko Schocher /* Set the access control to all-supervisor */
209880eff5cSHeiko Schocher asm volatile("mcr p15, 0, %0, c3, c0, 0"
210880eff5cSHeiko Schocher : : "r" (~0));
211de63ac27SR Sricharan
212de63ac27SR Sricharan arm_init_domains();
213de63ac27SR Sricharan
214880eff5cSHeiko Schocher /* and enable the mmu */
215880eff5cSHeiko Schocher reg = get_cr(); /* get control reg. */
216880eff5cSHeiko Schocher set_cr(reg | CR_M);
217ea0364f1SPeter Tyser }
218ea0364f1SPeter Tyser
mmu_enabled(void)219e05f0079SAneesh V static int mmu_enabled(void)
220e05f0079SAneesh V {
221e05f0079SAneesh V return get_cr() & CR_M;
222e05f0079SAneesh V }
223e05f0079SAneesh V
224ea0364f1SPeter Tyser /* cache_bit must be either CR_I or CR_C */
cache_enable(uint32_t cache_bit)225ea0364f1SPeter Tyser static void cache_enable(uint32_t cache_bit)
226ea0364f1SPeter Tyser {
227ea0364f1SPeter Tyser uint32_t reg;
228ea0364f1SPeter Tyser
229880eff5cSHeiko Schocher /* The data cache is not active unless the mmu is enabled too */
230e05f0079SAneesh V if ((cache_bit == CR_C) && !mmu_enabled())
231880eff5cSHeiko Schocher mmu_setup();
232ea0364f1SPeter Tyser reg = get_cr(); /* get control reg. */
233ea0364f1SPeter Tyser set_cr(reg | cache_bit);
234ea0364f1SPeter Tyser }
235ea0364f1SPeter Tyser
236ea0364f1SPeter Tyser /* cache_bit must be either CR_I or CR_C */
cache_disable(uint32_t cache_bit)237ea0364f1SPeter Tyser static void cache_disable(uint32_t cache_bit)
238ea0364f1SPeter Tyser {
239ea0364f1SPeter Tyser uint32_t reg;
240ea0364f1SPeter Tyser
241d702b081SSRICHARAN R reg = get_cr();
242d702b081SSRICHARAN R
243880eff5cSHeiko Schocher if (cache_bit == CR_C) {
244f1d2b313SHeiko Schocher /* if cache isn;t enabled no need to disable */
245f1d2b313SHeiko Schocher if ((reg & CR_C) != CR_C)
246f1d2b313SHeiko Schocher return;
247880eff5cSHeiko Schocher /* if disabling data cache, disable mmu too */
248880eff5cSHeiko Schocher cache_bit |= CR_M;
249880eff5cSHeiko Schocher }
25044df5e8dSArun Mankuzhi reg = get_cr();
25153d4ed70SLothar Waßmann
25244df5e8dSArun Mankuzhi if (cache_bit == (CR_C | CR_M))
25344df5e8dSArun Mankuzhi flush_dcache_all();
254ea0364f1SPeter Tyser set_cr(reg & ~cache_bit);
255ea0364f1SPeter Tyser }
256ea0364f1SPeter Tyser #endif
257ea0364f1SPeter Tyser
258e47f2db5SAneesh V #ifdef CONFIG_SYS_ICACHE_OFF
icache_enable(void)259ea0364f1SPeter Tyser void icache_enable (void)
260ea0364f1SPeter Tyser {
261ea0364f1SPeter Tyser return;
262ea0364f1SPeter Tyser }
263ea0364f1SPeter Tyser
icache_disable(void)264ea0364f1SPeter Tyser void icache_disable (void)
265ea0364f1SPeter Tyser {
266ea0364f1SPeter Tyser return;
267ea0364f1SPeter Tyser }
268ea0364f1SPeter Tyser
icache_status(void)269ea0364f1SPeter Tyser int icache_status (void)
270ea0364f1SPeter Tyser {
271ea0364f1SPeter Tyser return 0; /* always off */
272ea0364f1SPeter Tyser }
273ea0364f1SPeter Tyser #else
icache_enable(void)274ea0364f1SPeter Tyser void icache_enable(void)
275ea0364f1SPeter Tyser {
276ea0364f1SPeter Tyser cache_enable(CR_I);
277ea0364f1SPeter Tyser }
278ea0364f1SPeter Tyser
icache_disable(void)279ea0364f1SPeter Tyser void icache_disable(void)
280ea0364f1SPeter Tyser {
281ea0364f1SPeter Tyser cache_disable(CR_I);
282ea0364f1SPeter Tyser }
283ea0364f1SPeter Tyser
icache_status(void)284ea0364f1SPeter Tyser int icache_status(void)
285ea0364f1SPeter Tyser {
286ea0364f1SPeter Tyser return (get_cr() & CR_I) != 0;
287ea0364f1SPeter Tyser }
288ea0364f1SPeter Tyser #endif
289ea0364f1SPeter Tyser
290e47f2db5SAneesh V #ifdef CONFIG_SYS_DCACHE_OFF
dcache_enable(void)291ea0364f1SPeter Tyser void dcache_enable (void)
292ea0364f1SPeter Tyser {
293ea0364f1SPeter Tyser return;
294ea0364f1SPeter Tyser }
295ea0364f1SPeter Tyser
dcache_disable(void)296ea0364f1SPeter Tyser void dcache_disable (void)
297ea0364f1SPeter Tyser {
298ea0364f1SPeter Tyser return;
299ea0364f1SPeter Tyser }
300ea0364f1SPeter Tyser
dcache_status(void)301ea0364f1SPeter Tyser int dcache_status (void)
302ea0364f1SPeter Tyser {
303ea0364f1SPeter Tyser return 0; /* always off */
304ea0364f1SPeter Tyser }
305ea0364f1SPeter Tyser #else
dcache_enable(void)306ea0364f1SPeter Tyser void dcache_enable(void)
307ea0364f1SPeter Tyser {
308ea0364f1SPeter Tyser cache_enable(CR_C);
309ea0364f1SPeter Tyser }
310ea0364f1SPeter Tyser
dcache_disable(void)311ea0364f1SPeter Tyser void dcache_disable(void)
312ea0364f1SPeter Tyser {
313ea0364f1SPeter Tyser cache_disable(CR_C);
314ea0364f1SPeter Tyser }
315ea0364f1SPeter Tyser
dcache_status(void)316ea0364f1SPeter Tyser int dcache_status(void)
317ea0364f1SPeter Tyser {
318ea0364f1SPeter Tyser return (get_cr() & CR_C) != 0;
319ea0364f1SPeter Tyser }
320ea0364f1SPeter Tyser #endif
321