18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * xtensa mmu stuff 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Extracted from init.c 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/memblock.h> 88c2ecf20Sopenharmony_ci#include <linux/percpu.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/cache.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/tlb.h> 158c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 168c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 178c2ecf20Sopenharmony_ci#include <asm/page.h> 188c2ecf20Sopenharmony_ci#include <asm/initialize_mmu.h> 198c2ecf20Sopenharmony_ci#include <asm/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#if defined(CONFIG_HIGHMEM) 228c2ecf20Sopenharmony_cistatic void * __init init_pmd(unsigned long vaddr, unsigned long n_pages) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci pmd_t *pmd = pmd_off_k(vaddr); 258c2ecf20Sopenharmony_ci pte_t *pte; 268c2ecf20Sopenharmony_ci unsigned long i; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci n_pages = ALIGN(n_pages, PTRS_PER_PTE); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci pr_debug("%s: vaddr: 0x%08lx, n_pages: %ld\n", 318c2ecf20Sopenharmony_ci __func__, vaddr, n_pages); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci pte = memblock_alloc_low(n_pages * sizeof(pte_t), PAGE_SIZE); 348c2ecf20Sopenharmony_ci if (!pte) 358c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=%lx\n", 368c2ecf20Sopenharmony_ci __func__, n_pages * sizeof(pte_t), PAGE_SIZE); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; i < n_pages; ++i) 398c2ecf20Sopenharmony_ci pte_clear(NULL, 0, pte + i); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (i = 0; i < n_pages; i += PTRS_PER_PTE, ++pmd) { 428c2ecf20Sopenharmony_ci pte_t *cur_pte = pte + i; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci BUG_ON(!pmd_none(*pmd)); 458c2ecf20Sopenharmony_ci set_pmd(pmd, __pmd(((unsigned long)cur_pte) & PAGE_MASK)); 468c2ecf20Sopenharmony_ci BUG_ON(cur_pte != pte_offset_kernel(pmd, 0)); 478c2ecf20Sopenharmony_ci pr_debug("%s: pmd: 0x%p, pte: 0x%p\n", 488c2ecf20Sopenharmony_ci __func__, pmd, cur_pte); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci return pte; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void __init fixedrange_init(void) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci init_pmd(__fix_to_virt(0), __end_of_fixed_addresses); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid __init paging_init(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 628c2ecf20Sopenharmony_ci fixedrange_init(); 638c2ecf20Sopenharmony_ci pkmap_page_table = init_pmd(PKMAP_BASE, LAST_PKMAP); 648c2ecf20Sopenharmony_ci kmap_init(); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Flush the mmu and reset associated register to default values. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_civoid init_mmu(void) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci#if !(XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY) 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Writing zeros to the instruction and data TLBCFG special 768c2ecf20Sopenharmony_ci * registers ensure that valid values exist in the register. 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * For existing PGSZID<w> fields, zero selects the first element 798c2ecf20Sopenharmony_ci * of the page-size array. For nonexistent PGSZID<w> fields, 808c2ecf20Sopenharmony_ci * zero is the best value to write. Also, when changing PGSZID<w> 818c2ecf20Sopenharmony_ci * fields, the corresponding TLB must be flushed. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci set_itlbcfg_register(0); 848c2ecf20Sopenharmony_ci set_dtlbcfg_register(0); 858c2ecf20Sopenharmony_ci#endif 868c2ecf20Sopenharmony_ci init_kio(); 878c2ecf20Sopenharmony_ci local_flush_tlb_all(); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Set rasid register to a known value. */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci set_rasid_register(ASID_INSERT(ASID_USER_FIRST)); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Set PTEVADDR special register to the start of the page 948c2ecf20Sopenharmony_ci * table, which is in kernel mappable space (ie. not 958c2ecf20Sopenharmony_ci * statically mapped). This register's value is undefined on 968c2ecf20Sopenharmony_ci * reset. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci set_ptevaddr_register(XCHAL_PAGE_TABLE_VADDR); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_civoid init_kio(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && defined(CONFIG_USE_OF) 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Update the IO area mapping in case xtensa_kio_paddr has changed 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci write_dtlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), 1088c2ecf20Sopenharmony_ci XCHAL_KIO_CACHED_VADDR + 6); 1098c2ecf20Sopenharmony_ci write_itlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), 1108c2ecf20Sopenharmony_ci XCHAL_KIO_CACHED_VADDR + 6); 1118c2ecf20Sopenharmony_ci write_dtlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), 1128c2ecf20Sopenharmony_ci XCHAL_KIO_BYPASS_VADDR + 6); 1138c2ecf20Sopenharmony_ci write_itlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), 1148c2ecf20Sopenharmony_ci XCHAL_KIO_BYPASS_VADDR + 6); 1158c2ecf20Sopenharmony_ci#endif 1168c2ecf20Sopenharmony_ci} 117