xref: /kernel/linux/linux-5.10/arch/arc/mm/tlb.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TLB Management (flush/create/diagnostics) for ARC700
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * vineetg: Aug 2011
88c2ecf20Sopenharmony_ci *  -Reintroduce duplicate PD fixup - some customer chips still have the issue
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * vineetg: May 2011
118c2ecf20Sopenharmony_ci *  -No need to flush_cache_page( ) for each call to update_mmu_cache()
128c2ecf20Sopenharmony_ci *   some of the LMBench tests improved amazingly
138c2ecf20Sopenharmony_ci *      = page-fault thrice as fast (75 usec to 28 usec)
148c2ecf20Sopenharmony_ci *      = mmap twice as fast (9.6 msec to 4.6 msec),
158c2ecf20Sopenharmony_ci *      = fork (5.3 msec to 3.7 msec)
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * vineetg: April 2011 :
188c2ecf20Sopenharmony_ci *  -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore,
198c2ecf20Sopenharmony_ci *      helps avoid a shift when preparing PD0 from PTE
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * vineetg: April 2011 : Preparing for MMU V3
228c2ecf20Sopenharmony_ci *  -MMU v2/v3 BCRs decoded differently
238c2ecf20Sopenharmony_ci *  -Remove TLB_SIZE hardcoding as it's variable now: 256 or 512
248c2ecf20Sopenharmony_ci *  -tlb_entry_erase( ) can be void
258c2ecf20Sopenharmony_ci *  -local_flush_tlb_range( ):
268c2ecf20Sopenharmony_ci *      = need not "ceil" @end
278c2ecf20Sopenharmony_ci *      = walks MMU only if range spans < 32 entries, as opposed to 256
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Vineetg: Sept 10th 2008
308c2ecf20Sopenharmony_ci *  -Changes related to MMU v2 (Rel 4.8)
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Vineetg: Aug 29th 2008
338c2ecf20Sopenharmony_ci *  -In TLB Flush operations (Metal Fix MMU) there is a explicit command to
348c2ecf20Sopenharmony_ci *    flush Micro-TLBS. If TLB Index Reg is invalid prior to TLBIVUTLB cmd,
358c2ecf20Sopenharmony_ci *    it fails. Thus need to load it with ANY valid value before invoking
368c2ecf20Sopenharmony_ci *    TLBIVUTLB cmd
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Vineetg: Aug 21th 2008:
398c2ecf20Sopenharmony_ci *  -Reduced the duration of IRQ lockouts in TLB Flush routines
408c2ecf20Sopenharmony_ci *  -Multiple copies of TLB erase code separated into a "single" function
418c2ecf20Sopenharmony_ci *  -In TLB Flush routines, interrupt disabling moved UP to retrieve ASID
428c2ecf20Sopenharmony_ci *       in interrupt-safe region.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Vineetg: April 23rd Bug #93131
458c2ecf20Sopenharmony_ci *    Problem: tlb_flush_kernel_range() doesn't do anything if the range to
468c2ecf20Sopenharmony_ci *              flush is more than the size of TLB itself.
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * Rahul Trivedi : Codito Technologies 2004
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <linux/module.h>
528c2ecf20Sopenharmony_ci#include <linux/bug.h>
538c2ecf20Sopenharmony_ci#include <linux/mm_types.h>
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <asm/arcregs.h>
568c2ecf20Sopenharmony_ci#include <asm/setup.h>
578c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
588c2ecf20Sopenharmony_ci#include <asm/mmu.h>
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*			Need for ARC MMU v2
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * ARC700 MMU-v1 had a Joint-TLB for Code and Data and is 2 way set-assoc.
638c2ecf20Sopenharmony_ci * For a memcpy operation with 3 players (src/dst/code) such that all 3 pages
648c2ecf20Sopenharmony_ci * map into same set, there would be contention for the 2 ways causing severe
658c2ecf20Sopenharmony_ci * Thrashing.
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * Although J-TLB is 2 way set assoc, ARC700 caches J-TLB into uTLBS which has
688c2ecf20Sopenharmony_ci * much higher associativity. u-D-TLB is 8 ways, u-I-TLB is 4 ways.
698c2ecf20Sopenharmony_ci * Given this, the thrashing problem should never happen because once the 3
708c2ecf20Sopenharmony_ci * J-TLB entries are created (even though 3rd will knock out one of the prev
718c2ecf20Sopenharmony_ci * two), the u-D-TLB and u-I-TLB will have what is required to accomplish memcpy
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci * Yet we still see the Thrashing because a J-TLB Write cause flush of u-TLBs.
748c2ecf20Sopenharmony_ci * This is a simple design for keeping them in sync. So what do we do?
758c2ecf20Sopenharmony_ci * The solution which James came up was pretty neat. It utilised the assoc
768c2ecf20Sopenharmony_ci * of uTLBs by not invalidating always but only when absolutely necessary.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * - Existing TLB commands work as before
798c2ecf20Sopenharmony_ci * - New command (TLBWriteNI) for TLB write without clearing uTLBs
808c2ecf20Sopenharmony_ci * - New command (TLBIVUTLB) to invalidate uTLBs.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * The uTLBs need only be invalidated when pages are being removed from the
838c2ecf20Sopenharmony_ci * OS page table. If a 'victim' TLB entry is being overwritten in the main TLB
848c2ecf20Sopenharmony_ci * as a result of a miss, the removed entry is still allowed to exist in the
858c2ecf20Sopenharmony_ci * uTLBs as it is still valid and present in the OS page table. This allows the
868c2ecf20Sopenharmony_ci * full associativity of the uTLBs to hide the limited associativity of the main
878c2ecf20Sopenharmony_ci * TLB.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * During a miss handler, the new "TLBWriteNI" command is used to load
908c2ecf20Sopenharmony_ci * entries without clearing the uTLBs.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * When the OS page table is updated, TLB entries that may be associated with a
938c2ecf20Sopenharmony_ci * removed page are removed (flushed) from the TLB using TLBWrite. In this
948c2ecf20Sopenharmony_ci * circumstance, the uTLBs must also be cleared. This is done by using the
958c2ecf20Sopenharmony_ci * existing TLBWrite command. An explicit IVUTLB is also required for those
968c2ecf20Sopenharmony_ci * corner cases when TLBWrite was not executed at all because the corresp
978c2ecf20Sopenharmony_ci * J-TLB entry got evicted/replaced.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* A copy of the ASID from the PID reg is kept in asid_cache */
1028c2ecf20Sopenharmony_ciDEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int __read_mostly pae_exists;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * Utility Routine to erase a J-TLB entry
1088c2ecf20Sopenharmony_ci * Caller needs to setup Index Reg (manually or via getIndex)
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic inline void __tlb_entry_erase(void)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD1, 0);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (is_pae40_enabled())
1158c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBPD1HI, 0);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD0, 0);
1188c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void utlb_invalidate(void)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER >= 2)
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER == 2)
1268c2ecf20Sopenharmony_ci	/* MMU v2 introduced the uTLB Flush command.
1278c2ecf20Sopenharmony_ci	 * There was however an obscure hardware bug, where uTLB flush would
1288c2ecf20Sopenharmony_ci	 * fail when a prior probe for J-TLB (both totally unrelated) would
1298c2ecf20Sopenharmony_ci	 * return lkup err - because the entry didn't exist in MMU.
1308c2ecf20Sopenharmony_ci	 * The Workaround was to set Index reg with some valid value, prior to
1318c2ecf20Sopenharmony_ci	 * flush. This was fixed in MMU v3
1328c2ecf20Sopenharmony_ci	 */
1338c2ecf20Sopenharmony_ci	unsigned int idx;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* make sure INDEX Reg is valid */
1368c2ecf20Sopenharmony_ci	idx = read_aux_reg(ARC_REG_TLBINDEX);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* If not write some dummy val */
1398c2ecf20Sopenharmony_ci	if (unlikely(idx & TLB_LKUP_ERR))
1408c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBINDEX, 0xa);
1418c2ecf20Sopenharmony_ci#endif
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBIVUTLB);
1448c2ecf20Sopenharmony_ci#endif
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER < 4)
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline unsigned int tlb_entry_lkup(unsigned long vaddr_n_asid)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	unsigned int idx;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe);
1578c2ecf20Sopenharmony_ci	idx = read_aux_reg(ARC_REG_TLBINDEX);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return idx;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void tlb_entry_erase(unsigned int vaddr_n_asid)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	unsigned int idx;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Locate the TLB entry for this vaddr + ASID */
1678c2ecf20Sopenharmony_ci	idx = tlb_entry_lkup(vaddr_n_asid);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* No error means entry found, zero it out */
1708c2ecf20Sopenharmony_ci	if (likely(!(idx & TLB_LKUP_ERR))) {
1718c2ecf20Sopenharmony_ci		__tlb_entry_erase();
1728c2ecf20Sopenharmony_ci	} else {
1738c2ecf20Sopenharmony_ci		/* Duplicate entry error */
1748c2ecf20Sopenharmony_ci		WARN(idx == TLB_DUP_ERR, "Probe returned Dup PD for %x\n",
1758c2ecf20Sopenharmony_ci					   vaddr_n_asid);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void tlb_entry_insert(unsigned int pd0, pte_t pd1)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	unsigned int idx;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/*
1848c2ecf20Sopenharmony_ci	 * First verify if entry for this vaddr+ASID already exists
1858c2ecf20Sopenharmony_ci	 * This also sets up PD0 (vaddr, ASID..) for final commit
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	idx = tlb_entry_lkup(pd0);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * If Not already present get a free slot from MMU.
1918c2ecf20Sopenharmony_ci	 * Otherwise, Probe would have located the entry and set INDEX Reg
1928c2ecf20Sopenharmony_ci	 * with existing location. This will cause Write CMD to over-write
1938c2ecf20Sopenharmony_ci	 * existing entry with new PD0 and PD1
1948c2ecf20Sopenharmony_ci	 */
1958c2ecf20Sopenharmony_ci	if (likely(idx & TLB_LKUP_ERR))
1968c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBCOMMAND, TLBGetIndex);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* setup the other half of TLB entry (pfn, rwx..) */
1998c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD1, pd1);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * Commit the Entry to MMU
2038c2ecf20Sopenharmony_ci	 * It doesn't sound safe to use the TLBWriteNI cmd here
2048c2ecf20Sopenharmony_ci	 * which doesn't flush uTLBs. I'd rather be safe than sorry.
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci#else	/* CONFIG_ARC_MMU_VER >= 4) */
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void tlb_entry_erase(unsigned int vaddr_n_asid)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid | _PAGE_PRESENT);
2148c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBDeleteEntry);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void tlb_entry_insert(unsigned int pd0, pte_t pd1)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD0, pd0);
2208c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD1, pd1);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (is_pae40_enabled())
2238c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBPD1HI, (u64)pd1 >> 32);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBCOMMAND, TLBInsertEntry);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci#endif
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/*
2318c2ecf20Sopenharmony_ci * Un-conditionally (without lookup) erase the entire MMU contents
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cinoinline void local_flush_tlb_all(void)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
2378c2ecf20Sopenharmony_ci	unsigned long flags;
2388c2ecf20Sopenharmony_ci	unsigned int entry;
2398c2ecf20Sopenharmony_ci	int num_tlb = mmu->sets * mmu->ways;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	local_irq_save(flags);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* Load PD0 and PD1 with template for a Blank Entry */
2448c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD1, 0);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (is_pae40_enabled())
2478c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBPD1HI, 0);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_TLBPD0, 0);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	for (entry = 0; entry < num_tlb; entry++) {
2528c2ecf20Sopenharmony_ci		/* write this entry to the TLB */
2538c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBINDEX, entry);
2548c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
2588c2ecf20Sopenharmony_ci		const int stlb_idx = 0x800;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/* Blank sTLB entry */
2618c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBPD0, _PAGE_HW_SZ);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		for (entry = stlb_idx; entry < stlb_idx + 16; entry++) {
2648c2ecf20Sopenharmony_ci			write_aux_reg(ARC_REG_TLBINDEX, entry);
2658c2ecf20Sopenharmony_ci			write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI);
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	utlb_invalidate();
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	local_irq_restore(flags);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/*
2758c2ecf20Sopenharmony_ci * Flush the entire MM for userland. The fastest way is to move to Next ASID
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cinoinline void local_flush_tlb_mm(struct mm_struct *mm)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * Small optimisation courtesy IA64
2818c2ecf20Sopenharmony_ci	 * flush_mm called during fork,exit,munmap etc, multiple times as well.
2828c2ecf20Sopenharmony_ci	 * Only for fork( ) do we need to move parent to a new MMU ctxt,
2838c2ecf20Sopenharmony_ci	 * all other cases are NOPs, hence this check.
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	if (atomic_read(&mm->mm_users) == 0)
2868c2ecf20Sopenharmony_ci		return;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/*
2898c2ecf20Sopenharmony_ci	 * - Move to a new ASID, but only if the mm is still wired in
2908c2ecf20Sopenharmony_ci	 *   (Android Binder ended up calling this for vma->mm != tsk->mm,
2918c2ecf20Sopenharmony_ci	 *    causing h/w - s/w ASID to get out of sync)
2928c2ecf20Sopenharmony_ci	 * - Also get_new_mmu_context() new implementation allocates a new
2938c2ecf20Sopenharmony_ci	 *   ASID only if it is not allocated already - so unallocate first
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	destroy_context(mm);
2968c2ecf20Sopenharmony_ci	if (current->mm == mm)
2978c2ecf20Sopenharmony_ci		get_new_mmu_context(mm);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * Flush a Range of TLB entries for userland.
3028c2ecf20Sopenharmony_ci * @start is inclusive, while @end is exclusive
3038c2ecf20Sopenharmony_ci * Difference between this and Kernel Range Flush is
3048c2ecf20Sopenharmony_ci *  -Here the fastest way (if range is too large) is to move to next ASID
3058c2ecf20Sopenharmony_ci *      without doing any explicit Shootdown
3068c2ecf20Sopenharmony_ci *  -In case of kernel Flush, entry has to be shot down explicitly
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_civoid local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
3098c2ecf20Sopenharmony_ci			   unsigned long end)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	const unsigned int cpu = smp_processor_id();
3128c2ecf20Sopenharmony_ci	unsigned long flags;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* If range @start to @end is more than 32 TLB entries deep,
3158c2ecf20Sopenharmony_ci	 * its better to move to a new ASID rather than searching for
3168c2ecf20Sopenharmony_ci	 * individual entries and then shooting them down
3178c2ecf20Sopenharmony_ci	 *
3188c2ecf20Sopenharmony_ci	 * The calc above is rough, doesn't account for unaligned parts,
3198c2ecf20Sopenharmony_ci	 * since this is heuristics based anyways
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	if (unlikely((end - start) >= PAGE_SIZE * 32)) {
3228c2ecf20Sopenharmony_ci		local_flush_tlb_mm(vma->vm_mm);
3238c2ecf20Sopenharmony_ci		return;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/*
3278c2ecf20Sopenharmony_ci	 * @start moved to page start: this alone suffices for checking
3288c2ecf20Sopenharmony_ci	 * loop end condition below, w/o need for aligning @end to end
3298c2ecf20Sopenharmony_ci	 * e.g. 2000 to 4001 will anyhow loop twice
3308c2ecf20Sopenharmony_ci	 */
3318c2ecf20Sopenharmony_ci	start &= PAGE_MASK;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	local_irq_save(flags);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) {
3368c2ecf20Sopenharmony_ci		while (start < end) {
3378c2ecf20Sopenharmony_ci			tlb_entry_erase(start | hw_pid(vma->vm_mm, cpu));
3388c2ecf20Sopenharmony_ci			start += PAGE_SIZE;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	local_irq_restore(flags);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/* Flush the kernel TLB entries - vmalloc/modules (Global from MMU perspective)
3468c2ecf20Sopenharmony_ci *  @start, @end interpreted as kvaddr
3478c2ecf20Sopenharmony_ci * Interestingly, shared TLB entries can also be flushed using just
3488c2ecf20Sopenharmony_ci * @start,@end alone (interpreted as user vaddr), although technically SASID
3498c2ecf20Sopenharmony_ci * is also needed. However our smart TLbProbe lookup takes care of that.
3508c2ecf20Sopenharmony_ci */
3518c2ecf20Sopenharmony_civoid local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	unsigned long flags;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* exactly same as above, except for TLB entry not taking ASID */
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (unlikely((end - start) >= PAGE_SIZE * 32)) {
3588c2ecf20Sopenharmony_ci		local_flush_tlb_all();
3598c2ecf20Sopenharmony_ci		return;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	start &= PAGE_MASK;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	local_irq_save(flags);
3658c2ecf20Sopenharmony_ci	while (start < end) {
3668c2ecf20Sopenharmony_ci		tlb_entry_erase(start);
3678c2ecf20Sopenharmony_ci		start += PAGE_SIZE;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	local_irq_restore(flags);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/*
3748c2ecf20Sopenharmony_ci * Delete TLB entry in MMU for a given page (??? address)
3758c2ecf20Sopenharmony_ci * NOTE One TLB entry contains translation for single PAGE
3768c2ecf20Sopenharmony_ci */
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_civoid local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	const unsigned int cpu = smp_processor_id();
3818c2ecf20Sopenharmony_ci	unsigned long flags;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Note that it is critical that interrupts are DISABLED between
3848c2ecf20Sopenharmony_ci	 * checking the ASID and using it flush the TLB entry
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	local_irq_save(flags);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) {
3898c2ecf20Sopenharmony_ci		tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm, cpu));
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	local_irq_restore(flags);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistruct tlb_args {
3988c2ecf20Sopenharmony_ci	struct vm_area_struct *ta_vma;
3998c2ecf20Sopenharmony_ci	unsigned long ta_start;
4008c2ecf20Sopenharmony_ci	unsigned long ta_end;
4018c2ecf20Sopenharmony_ci};
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_page(void *arg)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct tlb_args *ta = arg;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	local_flush_tlb_page(ta->ta_vma, ta->ta_start);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_range(void *arg)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct tlb_args *ta = arg;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
4188c2ecf20Sopenharmony_cistatic inline void ipi_flush_pmd_tlb_range(void *arg)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct tlb_args *ta = arg;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	local_flush_pmd_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci#endif
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_kernel_range(void *arg)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct tlb_args *ta = (struct tlb_args *)arg;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_civoid flush_tlb_all(void)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	on_each_cpu((smp_call_func_t)local_flush_tlb_all, NULL, 1);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_civoid flush_tlb_mm(struct mm_struct *mm)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	on_each_cpu_mask(mm_cpumask(mm), (smp_call_func_t)local_flush_tlb_mm,
4418c2ecf20Sopenharmony_ci			 mm, 1);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_civoid flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct tlb_args ta = {
4478c2ecf20Sopenharmony_ci		.ta_vma = vma,
4488c2ecf20Sopenharmony_ci		.ta_start = uaddr
4498c2ecf20Sopenharmony_ci	};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, &ta, 1);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
4558c2ecf20Sopenharmony_ci		     unsigned long end)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct tlb_args ta = {
4588c2ecf20Sopenharmony_ci		.ta_vma = vma,
4598c2ecf20Sopenharmony_ci		.ta_start = start,
4608c2ecf20Sopenharmony_ci		.ta_end = end
4618c2ecf20Sopenharmony_ci	};
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, &ta, 1);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
4678c2ecf20Sopenharmony_civoid flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
4688c2ecf20Sopenharmony_ci			 unsigned long end)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct tlb_args ta = {
4718c2ecf20Sopenharmony_ci		.ta_vma = vma,
4728c2ecf20Sopenharmony_ci		.ta_start = start,
4738c2ecf20Sopenharmony_ci		.ta_end = end
4748c2ecf20Sopenharmony_ci	};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_pmd_tlb_range, &ta, 1);
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci#endif
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_civoid flush_tlb_kernel_range(unsigned long start, unsigned long end)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct tlb_args ta = {
4838c2ecf20Sopenharmony_ci		.ta_start = start,
4848c2ecf20Sopenharmony_ci		.ta_end = end
4858c2ecf20Sopenharmony_ci	};
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci#endif
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/*
4928c2ecf20Sopenharmony_ci * Routine to create a TLB entry
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_civoid create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	unsigned long flags;
4978c2ecf20Sopenharmony_ci	unsigned int asid_or_sasid, rwx;
4988c2ecf20Sopenharmony_ci	unsigned long pd0;
4998c2ecf20Sopenharmony_ci	pte_t pd1;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/*
5028c2ecf20Sopenharmony_ci	 * create_tlb() assumes that current->mm == vma->mm, since
5038c2ecf20Sopenharmony_ci	 * -it ASID for TLB entry is fetched from MMU ASID reg (valid for curr)
5048c2ecf20Sopenharmony_ci	 * -completes the lazy write to SASID reg (again valid for curr tsk)
5058c2ecf20Sopenharmony_ci	 *
5068c2ecf20Sopenharmony_ci	 * Removing the assumption involves
5078c2ecf20Sopenharmony_ci	 * -Using vma->mm->context{ASID,SASID}, as opposed to MMU reg.
5088c2ecf20Sopenharmony_ci	 * -Fix the TLB paranoid debug code to not trigger false negatives.
5098c2ecf20Sopenharmony_ci	 * -More importantly it makes this handler inconsistent with fast-path
5108c2ecf20Sopenharmony_ci	 *  TLB Refill handler which always deals with "current"
5118c2ecf20Sopenharmony_ci	 *
5128c2ecf20Sopenharmony_ci	 * Lets see the use cases when current->mm != vma->mm and we land here
5138c2ecf20Sopenharmony_ci	 *  1. execve->copy_strings()->__get_user_pages->handle_mm_fault
5148c2ecf20Sopenharmony_ci	 *     Here VM wants to pre-install a TLB entry for user stack while
5158c2ecf20Sopenharmony_ci	 *     current->mm still points to pre-execve mm (hence the condition).
5168c2ecf20Sopenharmony_ci	 *     However the stack vaddr is soon relocated (randomization) and
5178c2ecf20Sopenharmony_ci	 *     move_page_tables() tries to undo that TLB entry.
5188c2ecf20Sopenharmony_ci	 *     Thus not creating TLB entry is not any worse.
5198c2ecf20Sopenharmony_ci	 *
5208c2ecf20Sopenharmony_ci	 *  2. ptrace(POKETEXT) causes a CoW - debugger(current) inserting a
5218c2ecf20Sopenharmony_ci	 *     breakpoint in debugged task. Not creating a TLB now is not
5228c2ecf20Sopenharmony_ci	 *     performance critical.
5238c2ecf20Sopenharmony_ci	 *
5248c2ecf20Sopenharmony_ci	 * Both the cases above are not good enough for code churn.
5258c2ecf20Sopenharmony_ci	 */
5268c2ecf20Sopenharmony_ci	if (current->active_mm != vma->vm_mm)
5278c2ecf20Sopenharmony_ci		return;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	local_irq_save(flags);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), vaddr);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	vaddr &= PAGE_MASK;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* update this PTE credentials */
5368c2ecf20Sopenharmony_ci	pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* Create HW TLB(PD0,PD1) from PTE  */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* ASID for this task */
5418c2ecf20Sopenharmony_ci	asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	pd0 = vaddr | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/*
5468c2ecf20Sopenharmony_ci	 * ARC MMU provides fully orthogonal access bits for K/U mode,
5478c2ecf20Sopenharmony_ci	 * however Linux only saves 1 set to save PTE real-estate
5488c2ecf20Sopenharmony_ci	 * Here we convert 3 PTE bits into 6 MMU bits:
5498c2ecf20Sopenharmony_ci	 * -Kernel only entries have Kr Kw Kx 0 0 0
5508c2ecf20Sopenharmony_ci	 * -User entries have mirrored K and U bits
5518c2ecf20Sopenharmony_ci	 */
5528c2ecf20Sopenharmony_ci	rwx = pte_val(*ptep) & PTE_BITS_RWX;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (pte_val(*ptep) & _PAGE_GLOBAL)
5558c2ecf20Sopenharmony_ci		rwx <<= 3;		/* r w x => Kr Kw Kx 0 0 0 */
5568c2ecf20Sopenharmony_ci	else
5578c2ecf20Sopenharmony_ci		rwx |= (rwx << 3);	/* r w x => Kr Kw Kx Ur Uw Ux */
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	pd1 = rwx | (pte_val(*ptep) & PTE_BITS_NON_RWX_IN_PD1);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	tlb_entry_insert(pd0, pd1);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	local_irq_restore(flags);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci/*
5678c2ecf20Sopenharmony_ci * Called at the end of pagefault, for a userspace mapped page
5688c2ecf20Sopenharmony_ci *  -pre-install the corresponding TLB entry into MMU
5698c2ecf20Sopenharmony_ci *  -Finalize the delayed D-cache flush of kernel mapping of page due to
5708c2ecf20Sopenharmony_ci *  	flush_dcache_page(), copy_user_page()
5718c2ecf20Sopenharmony_ci *
5728c2ecf20Sopenharmony_ci * Note that flush (when done) involves both WBACK - so physical page is
5738c2ecf20Sopenharmony_ci * in sync as well as INV - so any non-congruent aliases don't remain
5748c2ecf20Sopenharmony_ci */
5758c2ecf20Sopenharmony_civoid update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned,
5768c2ecf20Sopenharmony_ci		      pte_t *ptep)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	unsigned long vaddr = vaddr_unaligned & PAGE_MASK;
5798c2ecf20Sopenharmony_ci	phys_addr_t paddr = pte_val(*ptep) & PAGE_MASK_PHYS;
5808c2ecf20Sopenharmony_ci	struct page *page = pfn_to_page(pte_pfn(*ptep));
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	create_tlb(vma, vaddr, ptep);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (page == ZERO_PAGE(0)) {
5858c2ecf20Sopenharmony_ci		return;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/*
5898c2ecf20Sopenharmony_ci	 * Exec page : Independent of aliasing/page-color considerations,
5908c2ecf20Sopenharmony_ci	 *	       since icache doesn't snoop dcache on ARC, any dirty
5918c2ecf20Sopenharmony_ci	 *	       K-mapping of a code page needs to be wback+inv so that
5928c2ecf20Sopenharmony_ci	 *	       icache fetch by userspace sees code correctly.
5938c2ecf20Sopenharmony_ci	 * !EXEC page: If K-mapping is NOT congruent to U-mapping, flush it
5948c2ecf20Sopenharmony_ci	 *	       so userspace sees the right data.
5958c2ecf20Sopenharmony_ci	 *  (Avoids the flush for Non-exec + congruent mapping case)
5968c2ecf20Sopenharmony_ci	 */
5978c2ecf20Sopenharmony_ci	if ((vma->vm_flags & VM_EXEC) ||
5988c2ecf20Sopenharmony_ci	     addr_not_cache_congruent(paddr, vaddr)) {
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		int dirty = !test_and_set_bit(PG_dc_clean, &page->flags);
6018c2ecf20Sopenharmony_ci		if (dirty) {
6028c2ecf20Sopenharmony_ci			/* wback + inv dcache lines (K-mapping) */
6038c2ecf20Sopenharmony_ci			__flush_dcache_page(paddr, paddr);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci			/* invalidate any existing icache lines (U-mapping) */
6068c2ecf20Sopenharmony_ci			if (vma->vm_flags & VM_EXEC)
6078c2ecf20Sopenharmony_ci				__inv_icache_page(paddr, vaddr);
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci/*
6158c2ecf20Sopenharmony_ci * MMUv4 in HS38x cores supports Super Pages which are basis for Linux THP
6168c2ecf20Sopenharmony_ci * support.
6178c2ecf20Sopenharmony_ci *
6188c2ecf20Sopenharmony_ci * Normal and Super pages can co-exist (ofcourse not overlap) in TLB with a
6198c2ecf20Sopenharmony_ci * new bit "SZ" in TLB page descriptor to distinguish between them.
6208c2ecf20Sopenharmony_ci * Super Page size is configurable in hardware (4K to 16M), but fixed once
6218c2ecf20Sopenharmony_ci * RTL builds.
6228c2ecf20Sopenharmony_ci *
6238c2ecf20Sopenharmony_ci * The exact THP size a Linux configuration will support is a function of:
6248c2ecf20Sopenharmony_ci *  - MMU page size (typical 8K, RTL fixed)
6258c2ecf20Sopenharmony_ci *  - software page walker address split between PGD:PTE:PFN (typical
6268c2ecf20Sopenharmony_ci *    11:8:13, but can be changed with 1 line)
6278c2ecf20Sopenharmony_ci * So for above default, THP size supported is 8K * (2^8) = 2M
6288c2ecf20Sopenharmony_ci *
6298c2ecf20Sopenharmony_ci * Default Page Walker is 2 levels, PGD:PTE:PFN, which in THP regime
6308c2ecf20Sopenharmony_ci * reduces to 1 level (as PTE is folded into PGD and canonically referred
6318c2ecf20Sopenharmony_ci * to as PMD).
6328c2ecf20Sopenharmony_ci * Thus THP PMD accessors are implemented in terms of PTE (just like sparc)
6338c2ecf20Sopenharmony_ci */
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_civoid update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr,
6368c2ecf20Sopenharmony_ci				 pmd_t *pmd)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	pte_t pte = __pte(pmd_val(*pmd));
6398c2ecf20Sopenharmony_ci	update_mmu_cache(vma, addr, &pte);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_civoid pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
6438c2ecf20Sopenharmony_ci				pgtable_t pgtable)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct list_head *lh = (struct list_head *) pgtable;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	assert_spin_locked(&mm->page_table_lock);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* FIFO */
6508c2ecf20Sopenharmony_ci	if (!pmd_huge_pte(mm, pmdp))
6518c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(lh);
6528c2ecf20Sopenharmony_ci	else
6538c2ecf20Sopenharmony_ci		list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
6548c2ecf20Sopenharmony_ci	pmd_huge_pte(mm, pmdp) = pgtable;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cipgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct list_head *lh;
6608c2ecf20Sopenharmony_ci	pgtable_t pgtable;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	assert_spin_locked(&mm->page_table_lock);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	pgtable = pmd_huge_pte(mm, pmdp);
6658c2ecf20Sopenharmony_ci	lh = (struct list_head *) pgtable;
6668c2ecf20Sopenharmony_ci	if (list_empty(lh))
6678c2ecf20Sopenharmony_ci		pmd_huge_pte(mm, pmdp) = NULL;
6688c2ecf20Sopenharmony_ci	else {
6698c2ecf20Sopenharmony_ci		pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
6708c2ecf20Sopenharmony_ci		list_del(lh);
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	pte_val(pgtable[0]) = 0;
6748c2ecf20Sopenharmony_ci	pte_val(pgtable[1]) = 0;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return pgtable;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_civoid local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
6808c2ecf20Sopenharmony_ci			       unsigned long end)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	unsigned int cpu;
6838c2ecf20Sopenharmony_ci	unsigned long flags;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	local_irq_save(flags);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (likely(asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID)) {
6908c2ecf20Sopenharmony_ci		unsigned int asid = hw_pid(vma->vm_mm, cpu);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		/* No need to loop here: this will always be for 1 Huge Page */
6938c2ecf20Sopenharmony_ci		tlb_entry_erase(start | _PAGE_HW_SZ | asid);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	local_irq_restore(flags);
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci#endif
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/* Read the Cache Build Configuration Registers, Decode them and save into
7028c2ecf20Sopenharmony_ci * the cpuinfo structure for later use.
7038c2ecf20Sopenharmony_ci * No Validation is done here, simply read/convert the BCRs
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_civoid read_decode_mmu_bcr(void)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
7088c2ecf20Sopenharmony_ci	unsigned int tmp;
7098c2ecf20Sopenharmony_ci	struct bcr_mmu_1_2 {
7108c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN
7118c2ecf20Sopenharmony_ci		unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8;
7128c2ecf20Sopenharmony_ci#else
7138c2ecf20Sopenharmony_ci		unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8;
7148c2ecf20Sopenharmony_ci#endif
7158c2ecf20Sopenharmony_ci	} *mmu2;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	struct bcr_mmu_3 {
7188c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN
7198c2ecf20Sopenharmony_ci	unsigned int ver:8, ways:4, sets:4, res:3, sasid:1, pg_sz:4,
7208c2ecf20Sopenharmony_ci		     u_itlb:4, u_dtlb:4;
7218c2ecf20Sopenharmony_ci#else
7228c2ecf20Sopenharmony_ci	unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, sasid:1, res:3, sets:4,
7238c2ecf20Sopenharmony_ci		     ways:4, ver:8;
7248c2ecf20Sopenharmony_ci#endif
7258c2ecf20Sopenharmony_ci	} *mmu3;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	struct bcr_mmu_4 {
7288c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN
7298c2ecf20Sopenharmony_ci	unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1,
7308c2ecf20Sopenharmony_ci		     n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3;
7318c2ecf20Sopenharmony_ci#else
7328c2ecf20Sopenharmony_ci	/*           DTLB      ITLB      JES        JE         JA      */
7338c2ecf20Sopenharmony_ci	unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2,
7348c2ecf20Sopenharmony_ci		     pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8;
7358c2ecf20Sopenharmony_ci#endif
7368c2ecf20Sopenharmony_ci	} *mmu4;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	tmp = read_aux_reg(ARC_REG_MMU_BCR);
7398c2ecf20Sopenharmony_ci	mmu->ver = (tmp >> 24);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (is_isa_arcompact()) {
7428c2ecf20Sopenharmony_ci		if (mmu->ver <= 2) {
7438c2ecf20Sopenharmony_ci			mmu2 = (struct bcr_mmu_1_2 *)&tmp;
7448c2ecf20Sopenharmony_ci			mmu->pg_sz_k = TO_KB(0x2000);
7458c2ecf20Sopenharmony_ci			mmu->sets = 1 << mmu2->sets;
7468c2ecf20Sopenharmony_ci			mmu->ways = 1 << mmu2->ways;
7478c2ecf20Sopenharmony_ci			mmu->u_dtlb = mmu2->u_dtlb;
7488c2ecf20Sopenharmony_ci			mmu->u_itlb = mmu2->u_itlb;
7498c2ecf20Sopenharmony_ci		} else {
7508c2ecf20Sopenharmony_ci			mmu3 = (struct bcr_mmu_3 *)&tmp;
7518c2ecf20Sopenharmony_ci			mmu->pg_sz_k = 1 << (mmu3->pg_sz - 1);
7528c2ecf20Sopenharmony_ci			mmu->sets = 1 << mmu3->sets;
7538c2ecf20Sopenharmony_ci			mmu->ways = 1 << mmu3->ways;
7548c2ecf20Sopenharmony_ci			mmu->u_dtlb = mmu3->u_dtlb;
7558c2ecf20Sopenharmony_ci			mmu->u_itlb = mmu3->u_itlb;
7568c2ecf20Sopenharmony_ci			mmu->sasid = mmu3->sasid;
7578c2ecf20Sopenharmony_ci		}
7588c2ecf20Sopenharmony_ci	} else {
7598c2ecf20Sopenharmony_ci		mmu4 = (struct bcr_mmu_4 *)&tmp;
7608c2ecf20Sopenharmony_ci		mmu->pg_sz_k = 1 << (mmu4->sz0 - 1);
7618c2ecf20Sopenharmony_ci		mmu->s_pg_sz_m = 1 << (mmu4->sz1 - 11);
7628c2ecf20Sopenharmony_ci		mmu->sets = 64 << mmu4->n_entry;
7638c2ecf20Sopenharmony_ci		mmu->ways = mmu4->n_ways * 2;
7648c2ecf20Sopenharmony_ci		mmu->u_dtlb = mmu4->u_dtlb * 4;
7658c2ecf20Sopenharmony_ci		mmu->u_itlb = mmu4->u_itlb * 4;
7668c2ecf20Sopenharmony_ci		mmu->sasid = mmu4->sasid;
7678c2ecf20Sopenharmony_ci		pae_exists = mmu->pae = mmu4->pae;
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cichar *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	int n = 0;
7748c2ecf20Sopenharmony_ci	struct cpuinfo_arc_mmu *p_mmu = &cpuinfo_arc700[cpu_id].mmu;
7758c2ecf20Sopenharmony_ci	char super_pg[64] = "";
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (p_mmu->s_pg_sz_m)
7788c2ecf20Sopenharmony_ci		scnprintf(super_pg, 64, "%dM Super Page %s",
7798c2ecf20Sopenharmony_ci			  p_mmu->s_pg_sz_m,
7808c2ecf20Sopenharmony_ci			  IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE));
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	n += scnprintf(buf + n, len - n,
7838c2ecf20Sopenharmony_ci		      "MMU [v%x]\t: %dk PAGE, %sJTLB %d (%dx%d), uDTLB %d, uITLB %d%s%s\n",
7848c2ecf20Sopenharmony_ci		       p_mmu->ver, p_mmu->pg_sz_k, super_pg,
7858c2ecf20Sopenharmony_ci		       p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways,
7868c2ecf20Sopenharmony_ci		       p_mmu->u_dtlb, p_mmu->u_itlb,
7878c2ecf20Sopenharmony_ci		       IS_AVAIL2(p_mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40));
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return buf;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ciint pae40_exist_but_not_enab(void)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	return pae_exists && !is_pae40_enabled();
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_civoid arc_mmu_init(void)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
8008c2ecf20Sopenharmony_ci	char str[256];
8018c2ecf20Sopenharmony_ci	int compat = 0;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	pr_info("%s", arc_mmu_mumbojumbo(0, str, sizeof(str)));
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/*
8068c2ecf20Sopenharmony_ci	 * Can't be done in processor.h due to header include dependencies
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!IS_ALIGNED((CONFIG_ARC_KVADDR_SIZE << 20), PMD_SIZE));
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	/*
8118c2ecf20Sopenharmony_ci	 * stack top size sanity check,
8128c2ecf20Sopenharmony_ci	 * Can't be done in processor.h due to header include dependencies
8138c2ecf20Sopenharmony_ci	 */
8148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!IS_ALIGNED(STACK_TOP, PMD_SIZE));
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/*
8178c2ecf20Sopenharmony_ci	 * Ensure that MMU features assumed by kernel exist in hardware.
8188c2ecf20Sopenharmony_ci	 * For older ARC700 cpus, it has to be exact match, since the MMU
8198c2ecf20Sopenharmony_ci	 * revisions were not backwards compatible (MMUv3 TLB layout changed
8208c2ecf20Sopenharmony_ci	 * so even if kernel for v2 didn't use any new cmds of v3, it would
8218c2ecf20Sopenharmony_ci	 * still not work.
8228c2ecf20Sopenharmony_ci	 * For HS cpus, MMUv4 was baseline and v5 is backwards compatible
8238c2ecf20Sopenharmony_ci	 * (will run older software).
8248c2ecf20Sopenharmony_ci	 */
8258c2ecf20Sopenharmony_ci	if (is_isa_arcompact() && mmu->ver == CONFIG_ARC_MMU_VER)
8268c2ecf20Sopenharmony_ci		compat = 1;
8278c2ecf20Sopenharmony_ci	else if (is_isa_arcv2() && mmu->ver >= CONFIG_ARC_MMU_VER)
8288c2ecf20Sopenharmony_ci		compat = 1;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!compat) {
8318c2ecf20Sopenharmony_ci		panic("MMU ver %d doesn't match kernel built for %d...\n",
8328c2ecf20Sopenharmony_ci		      mmu->ver, CONFIG_ARC_MMU_VER);
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (mmu->pg_sz_k != TO_KB(PAGE_SIZE))
8368c2ecf20Sopenharmony_ci		panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE));
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
8398c2ecf20Sopenharmony_ci	    mmu->s_pg_sz_m != TO_MB(HPAGE_PMD_SIZE))
8408c2ecf20Sopenharmony_ci		panic("MMU Super pg size != Linux HPAGE_PMD_SIZE (%luM)\n",
8418c2ecf20Sopenharmony_ci		      (unsigned long)TO_MB(HPAGE_PMD_SIZE));
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARC_HAS_PAE40) && !mmu->pae)
8448c2ecf20Sopenharmony_ci		panic("Hardware doesn't support PAE40\n");
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* Enable the MMU */
8478c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_PID, MMU_ENABLE);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* In smp we use this reg for interrupt 1 scratch */
8508c2ecf20Sopenharmony_ci#ifdef ARC_USE_SCRATCH_REG
8518c2ecf20Sopenharmony_ci	/* swapper_pg_dir is the pgd for the kernel, used by vmalloc */
8528c2ecf20Sopenharmony_ci	write_aux_reg(ARC_REG_SCRATCH_DATA0, swapper_pg_dir);
8538c2ecf20Sopenharmony_ci#endif
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (pae40_exist_but_not_enab())
8568c2ecf20Sopenharmony_ci		write_aux_reg(ARC_REG_TLBPD1HI, 0);
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci/*
8608c2ecf20Sopenharmony_ci * TLB Programmer's Model uses Linear Indexes: 0 to {255, 511} for 128 x {2,4}
8618c2ecf20Sopenharmony_ci * The mapping is Column-first.
8628c2ecf20Sopenharmony_ci *		---------------------	-----------
8638c2ecf20Sopenharmony_ci *		|way0|way1|way2|way3|	|way0|way1|
8648c2ecf20Sopenharmony_ci *		---------------------	-----------
8658c2ecf20Sopenharmony_ci * [set0]	|  0 |  1 |  2 |  3 |	|  0 |  1 |
8668c2ecf20Sopenharmony_ci * [set1]	|  4 |  5 |  6 |  7 |	|  2 |  3 |
8678c2ecf20Sopenharmony_ci *		~		    ~	~	  ~
8688c2ecf20Sopenharmony_ci * [set127]	| 508| 509| 510| 511|	| 254| 255|
8698c2ecf20Sopenharmony_ci *		---------------------	-----------
8708c2ecf20Sopenharmony_ci * For normal operations we don't(must not) care how above works since
8718c2ecf20Sopenharmony_ci * MMU cmd getIndex(vaddr) abstracts that out.
8728c2ecf20Sopenharmony_ci * However for walking WAYS of a SET, we need to know this
8738c2ecf20Sopenharmony_ci */
8748c2ecf20Sopenharmony_ci#define SET_WAY_TO_IDX(mmu, set, way)  ((set) * mmu->ways + (way))
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci/* Handling of Duplicate PD (TLB entry) in MMU.
8778c2ecf20Sopenharmony_ci * -Could be due to buggy customer tapeouts or obscure kernel bugs
8788c2ecf20Sopenharmony_ci * -MMU complaints not at the time of duplicate PD installation, but at the
8798c2ecf20Sopenharmony_ci *      time of lookup matching multiple ways.
8808c2ecf20Sopenharmony_ci * -Ideally these should never happen - but if they do - workaround by deleting
8818c2ecf20Sopenharmony_ci *      the duplicate one.
8828c2ecf20Sopenharmony_ci * -Knob to be verbose abt it.(TODO: hook them up to debugfs)
8838c2ecf20Sopenharmony_ci */
8848c2ecf20Sopenharmony_civolatile int dup_pd_silent; /* Be silent abt it or complain (default) */
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_civoid do_tlb_overlap_fault(unsigned long cause, unsigned long address,
8878c2ecf20Sopenharmony_ci			  struct pt_regs *regs)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
8908c2ecf20Sopenharmony_ci	unsigned long flags;
8918c2ecf20Sopenharmony_ci	int set, n_ways = mmu->ways;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	n_ways = min(n_ways, 4);
8948c2ecf20Sopenharmony_ci	BUG_ON(mmu->ways > 4);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	local_irq_save(flags);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	/* loop thru all sets of TLB */
8998c2ecf20Sopenharmony_ci	for (set = 0; set < mmu->sets; set++) {
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		int is_valid, way;
9028c2ecf20Sopenharmony_ci		unsigned int pd0[4];
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		/* read out all the ways of current set */
9058c2ecf20Sopenharmony_ci		for (way = 0, is_valid = 0; way < n_ways; way++) {
9068c2ecf20Sopenharmony_ci			write_aux_reg(ARC_REG_TLBINDEX,
9078c2ecf20Sopenharmony_ci					  SET_WAY_TO_IDX(mmu, set, way));
9088c2ecf20Sopenharmony_ci			write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead);
9098c2ecf20Sopenharmony_ci			pd0[way] = read_aux_reg(ARC_REG_TLBPD0);
9108c2ecf20Sopenharmony_ci			is_valid |= pd0[way] & _PAGE_PRESENT;
9118c2ecf20Sopenharmony_ci			pd0[way] &= PAGE_MASK;
9128c2ecf20Sopenharmony_ci		}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci		/* If all the WAYS in SET are empty, skip to next SET */
9158c2ecf20Sopenharmony_ci		if (!is_valid)
9168c2ecf20Sopenharmony_ci			continue;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		/* Scan the set for duplicate ways: needs a nested loop */
9198c2ecf20Sopenharmony_ci		for (way = 0; way < n_ways - 1; way++) {
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci			int n;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci			if (!pd0[way])
9248c2ecf20Sopenharmony_ci				continue;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci			for (n = way + 1; n < n_ways; n++) {
9278c2ecf20Sopenharmony_ci				if (pd0[way] != pd0[n])
9288c2ecf20Sopenharmony_ci					continue;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci				if (!dup_pd_silent)
9318c2ecf20Sopenharmony_ci					pr_info("Dup TLB PD0 %08x @ set %d ways %d,%d\n",
9328c2ecf20Sopenharmony_ci						pd0[way], set, way, n);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci				/*
9358c2ecf20Sopenharmony_ci				 * clear entry @way and not @n.
9368c2ecf20Sopenharmony_ci				 * This is critical to our optimised loop
9378c2ecf20Sopenharmony_ci				 */
9388c2ecf20Sopenharmony_ci				pd0[way] = 0;
9398c2ecf20Sopenharmony_ci				write_aux_reg(ARC_REG_TLBINDEX,
9408c2ecf20Sopenharmony_ci						SET_WAY_TO_IDX(mmu, set, way));
9418c2ecf20Sopenharmony_ci				__tlb_entry_erase();
9428c2ecf20Sopenharmony_ci			}
9438c2ecf20Sopenharmony_ci		}
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	local_irq_restore(flags);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci/***********************************************************************
9508c2ecf20Sopenharmony_ci * Diagnostic Routines
9518c2ecf20Sopenharmony_ci *  -Called from Low Level TLB Handlers if things don;t look good
9528c2ecf20Sopenharmony_ci **********************************************************************/
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DBG_TLB_PARANOIA
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci/*
9578c2ecf20Sopenharmony_ci * Low Level ASM TLB handler calls this if it finds that HW and SW ASIDS
9588c2ecf20Sopenharmony_ci * don't match
9598c2ecf20Sopenharmony_ci */
9608c2ecf20Sopenharmony_civoid print_asid_mismatch(int mm_asid, int mmu_asid, int is_fast_path)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	pr_emerg("ASID Mismatch in %s Path Handler: sw-pid=0x%x hw-pid=0x%x\n",
9638c2ecf20Sopenharmony_ci	       is_fast_path ? "Fast" : "Slow", mm_asid, mmu_asid);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	__asm__ __volatile__("flag 1");
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_civoid tlb_paranoid_check(unsigned int mm_asid, unsigned long addr)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	unsigned int mmu_asid;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	mmu_asid = read_aux_reg(ARC_REG_PID) & 0xff;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/*
9758c2ecf20Sopenharmony_ci	 * At the time of a TLB miss/installation
9768c2ecf20Sopenharmony_ci	 *   - HW version needs to match SW version
9778c2ecf20Sopenharmony_ci	 *   - SW needs to have a valid ASID
9788c2ecf20Sopenharmony_ci	 */
9798c2ecf20Sopenharmony_ci	if (addr < 0x70000000 &&
9808c2ecf20Sopenharmony_ci	    ((mm_asid == MM_CTXT_NO_ASID) ||
9818c2ecf20Sopenharmony_ci	      (mmu_asid != (mm_asid & MM_CTXT_ASID_MASK))))
9828c2ecf20Sopenharmony_ci		print_asid_mismatch(mm_asid, mmu_asid, 0);
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci#endif
985