18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 SiFive
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/sbi.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic void ipi_remote_fence_i(void *info)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	return local_flush_icache_all();
158c2ecf20Sopenharmony_ci}
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_civoid flush_icache_all(void)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	local_flush_icache_all();
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_RISCV_SBI))
228c2ecf20Sopenharmony_ci		sbi_remote_fence_i(NULL);
238c2ecf20Sopenharmony_ci	else
248c2ecf20Sopenharmony_ci		on_each_cpu(ipi_remote_fence_i, NULL, 1);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_icache_all);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Performs an icache flush for the given MM context.  RISC-V has no direct
308c2ecf20Sopenharmony_ci * mechanism for instruction cache shoot downs, so instead we send an IPI that
318c2ecf20Sopenharmony_ci * informs the remote harts they need to flush their local instruction caches.
328c2ecf20Sopenharmony_ci * To avoid pathologically slow behavior in a common case (a bunch of
338c2ecf20Sopenharmony_ci * single-hart processes on a many-hart machine, ie 'make -j') we avoid the
348c2ecf20Sopenharmony_ci * IPIs for harts that are not currently executing a MM context and instead
358c2ecf20Sopenharmony_ci * schedule a deferred local instruction cache flush to be performed before
368c2ecf20Sopenharmony_ci * execution resumes on each hart.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_civoid flush_icache_mm(struct mm_struct *mm, bool local)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned int cpu;
418c2ecf20Sopenharmony_ci	cpumask_t others, *mask;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	preempt_disable();
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Mark every hart's icache as needing a flush for this MM. */
468c2ecf20Sopenharmony_ci	mask = &mm->context.icache_stale_mask;
478c2ecf20Sopenharmony_ci	cpumask_setall(mask);
488c2ecf20Sopenharmony_ci	/* Flush this hart's I$ now, and mark it as flushed. */
498c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
508c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, mask);
518c2ecf20Sopenharmony_ci	local_flush_icache_all();
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/*
548c2ecf20Sopenharmony_ci	 * Flush the I$ of other harts concurrently executing, and mark them as
558c2ecf20Sopenharmony_ci	 * flushed.
568c2ecf20Sopenharmony_ci	 */
578c2ecf20Sopenharmony_ci	cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu));
588c2ecf20Sopenharmony_ci	local |= cpumask_empty(&others);
598c2ecf20Sopenharmony_ci	if (mm == current->active_mm && local) {
608c2ecf20Sopenharmony_ci		/*
618c2ecf20Sopenharmony_ci		 * It's assumed that at least one strongly ordered operation is
628c2ecf20Sopenharmony_ci		 * performed on this hart between setting a hart's cpumask bit
638c2ecf20Sopenharmony_ci		 * and scheduling this MM context on that hart.  Sending an SBI
648c2ecf20Sopenharmony_ci		 * remote message will do this, but in the case where no
658c2ecf20Sopenharmony_ci		 * messages are sent we still need to order this hart's writes
668c2ecf20Sopenharmony_ci		 * with flush_icache_deferred().
678c2ecf20Sopenharmony_ci		 */
688c2ecf20Sopenharmony_ci		smp_mb();
698c2ecf20Sopenharmony_ci	} else if (IS_ENABLED(CONFIG_RISCV_SBI)) {
708c2ecf20Sopenharmony_ci		cpumask_t hartid_mask;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		riscv_cpuid_to_hartid_mask(&others, &hartid_mask);
738c2ecf20Sopenharmony_ci		sbi_remote_fence_i(cpumask_bits(&hartid_mask));
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		on_each_cpu_mask(&others, ipi_remote_fence_i, NULL, 1);
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	preempt_enable();
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU
848c2ecf20Sopenharmony_civoid flush_icache_pte(pte_t pte)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct page *page = pte_page(pte);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (!test_bit(PG_dcache_clean, &page->flags)) {
898c2ecf20Sopenharmony_ci		flush_icache_all();
908c2ecf20Sopenharmony_ci		set_bit(PG_dcache_clean, &page->flags);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */
94