18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/cache.h>
58c2ecf20Sopenharmony_ci#include <linux/highmem.h>
68c2ecf20Sopenharmony_ci#include <linux/mm.h>
78c2ecf20Sopenharmony_ci#include <asm/cache.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_civoid update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
108c2ecf20Sopenharmony_ci		      pte_t *pte)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	unsigned long addr;
138c2ecf20Sopenharmony_ci	struct page *page;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	page = pfn_to_page(pte_pfn(*pte));
168c2ecf20Sopenharmony_ci	if (page == ZERO_PAGE(0))
178c2ecf20Sopenharmony_ci		return;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	if (test_and_set_bit(PG_dcache_clean, &page->flags))
208c2ecf20Sopenharmony_ci		return;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	addr = (unsigned long) kmap_atomic(page);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	dcache_wb_range(addr, addr + PAGE_SIZE);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
278c2ecf20Sopenharmony_ci		icache_inv_range(addr, addr + PAGE_SIZE);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	kunmap_atomic((void *) addr);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_civoid flush_icache_deferred(struct mm_struct *mm)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
358c2ecf20Sopenharmony_ci	cpumask_t *mask = &mm->context.icache_stale_mask;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (cpumask_test_cpu(cpu, mask)) {
388c2ecf20Sopenharmony_ci		cpumask_clear_cpu(cpu, mask);
398c2ecf20Sopenharmony_ci		/*
408c2ecf20Sopenharmony_ci		 * Ensure the remote hart's writes are visible to this hart.
418c2ecf20Sopenharmony_ci		 * This pairs with a barrier in flush_icache_mm.
428c2ecf20Sopenharmony_ci		 */
438c2ecf20Sopenharmony_ci		smp_mb();
448c2ecf20Sopenharmony_ci		local_icache_inv_all(NULL);
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid flush_icache_mm_range(struct mm_struct *mm,
498c2ecf20Sopenharmony_ci		unsigned long start, unsigned long end)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	unsigned int cpu;
528c2ecf20Sopenharmony_ci	cpumask_t others, *mask;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	preempt_disable();
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_ICACHE_INS
578c2ecf20Sopenharmony_ci	if (mm == current->mm) {
588c2ecf20Sopenharmony_ci		icache_inv_range(start, end);
598c2ecf20Sopenharmony_ci		preempt_enable();
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci#endif
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* Mark every hart's icache as needing a flush for this MM. */
658c2ecf20Sopenharmony_ci	mask = &mm->context.icache_stale_mask;
668c2ecf20Sopenharmony_ci	cpumask_setall(mask);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* Flush this hart's I$ now, and mark it as flushed. */
698c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
708c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, mask);
718c2ecf20Sopenharmony_ci	local_icache_inv_all(NULL);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/*
748c2ecf20Sopenharmony_ci	 * Flush the I$ of other harts concurrently executing, and mark them as
758c2ecf20Sopenharmony_ci	 * flushed.
768c2ecf20Sopenharmony_ci	 */
778c2ecf20Sopenharmony_ci	cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu));
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (mm != current->active_mm || !cpumask_empty(&others)) {
808c2ecf20Sopenharmony_ci		on_each_cpu_mask(&others, local_icache_inv_all, NULL, 1);
818c2ecf20Sopenharmony_ci		cpumask_clear(mask);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	preempt_enable();
858c2ecf20Sopenharmony_ci}
86