xref: /kernel/linux/linux-5.10/arch/xtensa/mm/mmu.c (revision 8c2ecf20)
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