162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TLB flush routines for radix kernels.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/mm.h>
962306a36Sopenharmony_ci#include <linux/hugetlb.h>
1062306a36Sopenharmony_ci#include <linux/memblock.h>
1162306a36Sopenharmony_ci#include <linux/mmu_context.h>
1262306a36Sopenharmony_ci#include <linux/sched/mm.h>
1362306a36Sopenharmony_ci#include <linux/debugfs.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/ppc-opcode.h>
1662306a36Sopenharmony_ci#include <asm/tlb.h>
1762306a36Sopenharmony_ci#include <asm/tlbflush.h>
1862306a36Sopenharmony_ci#include <asm/trace.h>
1962306a36Sopenharmony_ci#include <asm/cputhreads.h>
2062306a36Sopenharmony_ci#include <asm/plpar_wrappers.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "internal.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * tlbiel instruction for radix, set invalidation
2662306a36Sopenharmony_ci * i.e., r=1 and is=01 or is=10 or is=11
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic __always_inline void tlbiel_radix_set_isa300(unsigned int set, unsigned int is,
2962306a36Sopenharmony_ci					unsigned int pid,
3062306a36Sopenharmony_ci					unsigned int ric, unsigned int prs)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned long rb;
3362306a36Sopenharmony_ci	unsigned long rs;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	rb = (set << PPC_BITLSHIFT(51)) | (is << PPC_BITLSHIFT(53));
3662306a36Sopenharmony_ci	rs = ((unsigned long)pid << PPC_BITLSHIFT(31));
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	asm volatile(PPC_TLBIEL(%0, %1, %2, %3, 1)
3962306a36Sopenharmony_ci		     : : "r"(rb), "r"(rs), "i"(ric), "i"(prs)
4062306a36Sopenharmony_ci		     : "memory");
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void tlbiel_all_isa300(unsigned int num_sets, unsigned int is)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	unsigned int set;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * Flush the first set of the TLB, and the entire Page Walk Cache
5162306a36Sopenharmony_ci	 * and partition table entries. Then flush the remaining sets of the
5262306a36Sopenharmony_ci	 * TLB.
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (early_cpu_has_feature(CPU_FTR_HVMODE)) {
5662306a36Sopenharmony_ci		/* MSR[HV] should flush partition scope translations first. */
5762306a36Sopenharmony_ci		tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 0);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) {
6062306a36Sopenharmony_ci			for (set = 1; set < num_sets; set++)
6162306a36Sopenharmony_ci				tlbiel_radix_set_isa300(set, is, 0,
6262306a36Sopenharmony_ci							RIC_FLUSH_TLB, 0);
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* Flush process scoped entries. */
6762306a36Sopenharmony_ci	tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 1);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) {
7062306a36Sopenharmony_ci		for (set = 1; set < num_sets; set++)
7162306a36Sopenharmony_ci			tlbiel_radix_set_isa300(set, is, 0, RIC_FLUSH_TLB, 1);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	ppc_after_tlbiel_barrier();
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_civoid radix__tlbiel_all(unsigned int action)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	unsigned int is;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	switch (action) {
8262306a36Sopenharmony_ci	case TLB_INVAL_SCOPE_GLOBAL:
8362306a36Sopenharmony_ci		is = 3;
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	case TLB_INVAL_SCOPE_LPID:
8662306a36Sopenharmony_ci		is = 2;
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	default:
8962306a36Sopenharmony_ci		BUG();
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (early_cpu_has_feature(CPU_FTR_ARCH_300))
9362306a36Sopenharmony_ci		tlbiel_all_isa300(POWER9_TLB_SETS_RADIX, is);
9462306a36Sopenharmony_ci	else
9562306a36Sopenharmony_ci		WARN(1, "%s called on pre-POWER9 CPU\n", __func__);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory");
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic __always_inline void __tlbiel_pid(unsigned long pid, int set,
10162306a36Sopenharmony_ci				unsigned long ric)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	rb = PPC_BIT(53); /* IS = 1 */
10662306a36Sopenharmony_ci	rb |= set << PPC_BITLSHIFT(51);
10762306a36Sopenharmony_ci	rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
10862306a36Sopenharmony_ci	prs = 1; /* process scoped */
10962306a36Sopenharmony_ci	r = 1;   /* radix format */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
11262306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
11362306a36Sopenharmony_ci	trace_tlbie(0, 1, rb, rs, ric, prs, r);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic __always_inline void __tlbie_pid(unsigned long pid, unsigned long ric)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	rb = PPC_BIT(53); /* IS = 1 */
12162306a36Sopenharmony_ci	rs = pid << PPC_BITLSHIFT(31);
12262306a36Sopenharmony_ci	prs = 1; /* process scoped */
12362306a36Sopenharmony_ci	r = 1;   /* radix format */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
12662306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
12762306a36Sopenharmony_ci	trace_tlbie(0, 0, rb, rs, ric, prs, r);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid(unsigned long lpid, unsigned long ric)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	rb = PPC_BIT(52); /* IS = 2 */
13562306a36Sopenharmony_ci	rs = lpid;
13662306a36Sopenharmony_ci	prs = 0; /* partition scoped */
13762306a36Sopenharmony_ci	r = 1;   /* radix format */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
14062306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
14162306a36Sopenharmony_ci	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid_guest(unsigned long lpid, unsigned long ric)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	rb = PPC_BIT(52); /* IS = 2 */
14962306a36Sopenharmony_ci	rs = lpid;
15062306a36Sopenharmony_ci	prs = 1; /* process scoped */
15162306a36Sopenharmony_ci	r = 1;   /* radix format */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
15462306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
15562306a36Sopenharmony_ci	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic __always_inline void __tlbiel_va(unsigned long va, unsigned long pid,
15962306a36Sopenharmony_ci					unsigned long ap, unsigned long ric)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	rb = va & ~(PPC_BITMASK(52, 63));
16462306a36Sopenharmony_ci	rb |= ap << PPC_BITLSHIFT(58);
16562306a36Sopenharmony_ci	rs = pid << PPC_BITLSHIFT(31);
16662306a36Sopenharmony_ci	prs = 1; /* process scoped */
16762306a36Sopenharmony_ci	r = 1;   /* radix format */
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
17062306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
17162306a36Sopenharmony_ci	trace_tlbie(0, 1, rb, rs, ric, prs, r);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic __always_inline void __tlbie_va(unsigned long va, unsigned long pid,
17562306a36Sopenharmony_ci				       unsigned long ap, unsigned long ric)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	rb = va & ~(PPC_BITMASK(52, 63));
18062306a36Sopenharmony_ci	rb |= ap << PPC_BITLSHIFT(58);
18162306a36Sopenharmony_ci	rs = pid << PPC_BITLSHIFT(31);
18262306a36Sopenharmony_ci	prs = 1; /* process scoped */
18362306a36Sopenharmony_ci	r = 1;   /* radix format */
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
18662306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
18762306a36Sopenharmony_ci	trace_tlbie(0, 0, rb, rs, ric, prs, r);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid_va(unsigned long va, unsigned long lpid,
19162306a36Sopenharmony_ci					    unsigned long ap, unsigned long ric)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	unsigned long rb,rs,prs,r;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	rb = va & ~(PPC_BITMASK(52, 63));
19662306a36Sopenharmony_ci	rb |= ap << PPC_BITLSHIFT(58);
19762306a36Sopenharmony_ci	rs = lpid;
19862306a36Sopenharmony_ci	prs = 0; /* partition scoped */
19962306a36Sopenharmony_ci	r = 1;   /* radix format */
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
20262306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
20362306a36Sopenharmony_ci	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic inline void fixup_tlbie_va(unsigned long va, unsigned long pid,
20862306a36Sopenharmony_ci				  unsigned long ap)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
21162306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
21262306a36Sopenharmony_ci		__tlbie_va(va, 0, ap, RIC_FLUSH_TLB);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
21662306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
21762306a36Sopenharmony_ci		__tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline void fixup_tlbie_va_range(unsigned long va, unsigned long pid,
22262306a36Sopenharmony_ci					unsigned long ap)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
22562306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
22662306a36Sopenharmony_ci		__tlbie_pid(0, RIC_FLUSH_TLB);
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
23062306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
23162306a36Sopenharmony_ci		__tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic inline void fixup_tlbie_pid(unsigned long pid)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	/*
23862306a36Sopenharmony_ci	 * We can use any address for the invalidation, pick one which is
23962306a36Sopenharmony_ci	 * probably unused as an optimisation.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	unsigned long va = ((1UL << 52) - 1);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
24462306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
24562306a36Sopenharmony_ci		__tlbie_pid(0, RIC_FLUSH_TLB);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
24962306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
25062306a36Sopenharmony_ci		__tlbie_va(va, pid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic inline void fixup_tlbie_lpid_va(unsigned long va, unsigned long lpid,
25562306a36Sopenharmony_ci				       unsigned long ap)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
25862306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
25962306a36Sopenharmony_ci		__tlbie_lpid_va(va, 0, ap, RIC_FLUSH_TLB);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
26362306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
26462306a36Sopenharmony_ci		__tlbie_lpid_va(va, lpid, ap, RIC_FLUSH_TLB);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic inline void fixup_tlbie_lpid(unsigned long lpid)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	/*
27162306a36Sopenharmony_ci	 * We can use any address for the invalidation, pick one which is
27262306a36Sopenharmony_ci	 * probably unused as an optimisation.
27362306a36Sopenharmony_ci	 */
27462306a36Sopenharmony_ci	unsigned long va = ((1UL << 52) - 1);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
27762306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
27862306a36Sopenharmony_ci		__tlbie_lpid(0, RIC_FLUSH_TLB);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
28262306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
28362306a36Sopenharmony_ci		__tlbie_lpid_va(va, lpid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * We use 128 set in radix mode and 256 set in hpt mode.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic inline void _tlbiel_pid(unsigned long pid, unsigned long ric)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int set;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch (ric) {
29762306a36Sopenharmony_ci	case RIC_FLUSH_PWC:
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		/* For PWC, only one flush is needed */
30062306a36Sopenharmony_ci		__tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
30162306a36Sopenharmony_ci		ppc_after_tlbiel_barrier();
30262306a36Sopenharmony_ci		return;
30362306a36Sopenharmony_ci	case RIC_FLUSH_TLB:
30462306a36Sopenharmony_ci		__tlbiel_pid(pid, 0, RIC_FLUSH_TLB);
30562306a36Sopenharmony_ci		break;
30662306a36Sopenharmony_ci	case RIC_FLUSH_ALL:
30762306a36Sopenharmony_ci	default:
30862306a36Sopenharmony_ci		/*
30962306a36Sopenharmony_ci		 * Flush the first set of the TLB, and if
31062306a36Sopenharmony_ci		 * we're doing a RIC_FLUSH_ALL, also flush
31162306a36Sopenharmony_ci		 * the entire Page Walk Cache.
31262306a36Sopenharmony_ci		 */
31362306a36Sopenharmony_ci		__tlbiel_pid(pid, 0, RIC_FLUSH_ALL);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_ARCH_31)) {
31762306a36Sopenharmony_ci		/* For the remaining sets, just flush the TLB */
31862306a36Sopenharmony_ci		for (set = 1; set < POWER9_TLB_SETS_RADIX ; set++)
31962306a36Sopenharmony_ci			__tlbiel_pid(pid, set, RIC_FLUSH_TLB);
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	ppc_after_tlbiel_barrier();
32362306a36Sopenharmony_ci	asm volatile(PPC_RADIX_INVALIDATE_ERAT_USER "; isync" : : :"memory");
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic inline void _tlbie_pid(unsigned long pid, unsigned long ric)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * Workaround the fact that the "ric" argument to __tlbie_pid
33262306a36Sopenharmony_ci	 * must be a compile-time constraint to match the "i" constraint
33362306a36Sopenharmony_ci	 * in the asm statement.
33462306a36Sopenharmony_ci	 */
33562306a36Sopenharmony_ci	switch (ric) {
33662306a36Sopenharmony_ci	case RIC_FLUSH_TLB:
33762306a36Sopenharmony_ci		__tlbie_pid(pid, RIC_FLUSH_TLB);
33862306a36Sopenharmony_ci		fixup_tlbie_pid(pid);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case RIC_FLUSH_PWC:
34162306a36Sopenharmony_ci		__tlbie_pid(pid, RIC_FLUSH_PWC);
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	case RIC_FLUSH_ALL:
34462306a36Sopenharmony_ci	default:
34562306a36Sopenharmony_ci		__tlbie_pid(pid, RIC_FLUSH_ALL);
34662306a36Sopenharmony_ci		fixup_tlbie_pid(pid);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistruct tlbiel_pid {
35262306a36Sopenharmony_ci	unsigned long pid;
35362306a36Sopenharmony_ci	unsigned long ric;
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic void do_tlbiel_pid(void *info)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct tlbiel_pid *t = info;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (t->ric == RIC_FLUSH_TLB)
36162306a36Sopenharmony_ci		_tlbiel_pid(t->pid, RIC_FLUSH_TLB);
36262306a36Sopenharmony_ci	else if (t->ric == RIC_FLUSH_PWC)
36362306a36Sopenharmony_ci		_tlbiel_pid(t->pid, RIC_FLUSH_PWC);
36462306a36Sopenharmony_ci	else
36562306a36Sopenharmony_ci		_tlbiel_pid(t->pid, RIC_FLUSH_ALL);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic inline void _tlbiel_pid_multicast(struct mm_struct *mm,
36962306a36Sopenharmony_ci				unsigned long pid, unsigned long ric)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct cpumask *cpus = mm_cpumask(mm);
37262306a36Sopenharmony_ci	struct tlbiel_pid t = { .pid = pid, .ric = ric };
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	on_each_cpu_mask(cpus, do_tlbiel_pid, &t, 1);
37562306a36Sopenharmony_ci	/*
37662306a36Sopenharmony_ci	 * Always want the CPU translations to be invalidated with tlbiel in
37762306a36Sopenharmony_ci	 * these paths, so while coprocessors must use tlbie, we can not
37862306a36Sopenharmony_ci	 * optimise away the tlbiel component.
37962306a36Sopenharmony_ci	 */
38062306a36Sopenharmony_ci	if (atomic_read(&mm->context.copros) > 0)
38162306a36Sopenharmony_ci		_tlbie_pid(pid, RIC_FLUSH_ALL);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic inline void _tlbie_lpid(unsigned long lpid, unsigned long ric)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/*
38962306a36Sopenharmony_ci	 * Workaround the fact that the "ric" argument to __tlbie_pid
39062306a36Sopenharmony_ci	 * must be a compile-time contraint to match the "i" constraint
39162306a36Sopenharmony_ci	 * in the asm statement.
39262306a36Sopenharmony_ci	 */
39362306a36Sopenharmony_ci	switch (ric) {
39462306a36Sopenharmony_ci	case RIC_FLUSH_TLB:
39562306a36Sopenharmony_ci		__tlbie_lpid(lpid, RIC_FLUSH_TLB);
39662306a36Sopenharmony_ci		fixup_tlbie_lpid(lpid);
39762306a36Sopenharmony_ci		break;
39862306a36Sopenharmony_ci	case RIC_FLUSH_PWC:
39962306a36Sopenharmony_ci		__tlbie_lpid(lpid, RIC_FLUSH_PWC);
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	case RIC_FLUSH_ALL:
40262306a36Sopenharmony_ci	default:
40362306a36Sopenharmony_ci		__tlbie_lpid(lpid, RIC_FLUSH_ALL);
40462306a36Sopenharmony_ci		fixup_tlbie_lpid(lpid);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic __always_inline void _tlbie_lpid_guest(unsigned long lpid, unsigned long ric)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	/*
41262306a36Sopenharmony_ci	 * Workaround the fact that the "ric" argument to __tlbie_pid
41362306a36Sopenharmony_ci	 * must be a compile-time contraint to match the "i" constraint
41462306a36Sopenharmony_ci	 * in the asm statement.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	switch (ric) {
41762306a36Sopenharmony_ci	case RIC_FLUSH_TLB:
41862306a36Sopenharmony_ci		__tlbie_lpid_guest(lpid, RIC_FLUSH_TLB);
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	case RIC_FLUSH_PWC:
42162306a36Sopenharmony_ci		__tlbie_lpid_guest(lpid, RIC_FLUSH_PWC);
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	case RIC_FLUSH_ALL:
42462306a36Sopenharmony_ci	default:
42562306a36Sopenharmony_ci		__tlbie_lpid_guest(lpid, RIC_FLUSH_ALL);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	fixup_tlbie_lpid(lpid);
42862306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic inline void __tlbiel_va_range(unsigned long start, unsigned long end,
43262306a36Sopenharmony_ci				    unsigned long pid, unsigned long page_size,
43362306a36Sopenharmony_ci				    unsigned long psize)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	unsigned long addr;
43662306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	for (addr = start; addr < end; addr += page_size)
43962306a36Sopenharmony_ci		__tlbiel_va(addr, pid, ap, RIC_FLUSH_TLB);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic __always_inline void _tlbiel_va(unsigned long va, unsigned long pid,
44362306a36Sopenharmony_ci				       unsigned long psize, unsigned long ric)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
44862306a36Sopenharmony_ci	__tlbiel_va(va, pid, ap, ric);
44962306a36Sopenharmony_ci	ppc_after_tlbiel_barrier();
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic inline void _tlbiel_va_range(unsigned long start, unsigned long end,
45362306a36Sopenharmony_ci				    unsigned long pid, unsigned long page_size,
45462306a36Sopenharmony_ci				    unsigned long psize, bool also_pwc)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
45762306a36Sopenharmony_ci	if (also_pwc)
45862306a36Sopenharmony_ci		__tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
45962306a36Sopenharmony_ci	__tlbiel_va_range(start, end, pid, page_size, psize);
46062306a36Sopenharmony_ci	ppc_after_tlbiel_barrier();
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic inline void __tlbie_va_range(unsigned long start, unsigned long end,
46462306a36Sopenharmony_ci				    unsigned long pid, unsigned long page_size,
46562306a36Sopenharmony_ci				    unsigned long psize)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	unsigned long addr;
46862306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	for (addr = start; addr < end; addr += page_size)
47162306a36Sopenharmony_ci		__tlbie_va(addr, pid, ap, RIC_FLUSH_TLB);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	fixup_tlbie_va_range(addr - page_size, pid, ap);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic __always_inline void _tlbie_va(unsigned long va, unsigned long pid,
47762306a36Sopenharmony_ci				      unsigned long psize, unsigned long ric)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
48262306a36Sopenharmony_ci	__tlbie_va(va, pid, ap, ric);
48362306a36Sopenharmony_ci	fixup_tlbie_va(va, pid, ap);
48462306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistruct tlbiel_va {
48862306a36Sopenharmony_ci	unsigned long pid;
48962306a36Sopenharmony_ci	unsigned long va;
49062306a36Sopenharmony_ci	unsigned long psize;
49162306a36Sopenharmony_ci	unsigned long ric;
49262306a36Sopenharmony_ci};
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void do_tlbiel_va(void *info)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct tlbiel_va *t = info;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (t->ric == RIC_FLUSH_TLB)
49962306a36Sopenharmony_ci		_tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_TLB);
50062306a36Sopenharmony_ci	else if (t->ric == RIC_FLUSH_PWC)
50162306a36Sopenharmony_ci		_tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_PWC);
50262306a36Sopenharmony_ci	else
50362306a36Sopenharmony_ci		_tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_ALL);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic inline void _tlbiel_va_multicast(struct mm_struct *mm,
50762306a36Sopenharmony_ci				unsigned long va, unsigned long pid,
50862306a36Sopenharmony_ci				unsigned long psize, unsigned long ric)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct cpumask *cpus = mm_cpumask(mm);
51162306a36Sopenharmony_ci	struct tlbiel_va t = { .va = va, .pid = pid, .psize = psize, .ric = ric };
51262306a36Sopenharmony_ci	on_each_cpu_mask(cpus, do_tlbiel_va, &t, 1);
51362306a36Sopenharmony_ci	if (atomic_read(&mm->context.copros) > 0)
51462306a36Sopenharmony_ci		_tlbie_va(va, pid, psize, RIC_FLUSH_TLB);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistruct tlbiel_va_range {
51862306a36Sopenharmony_ci	unsigned long pid;
51962306a36Sopenharmony_ci	unsigned long start;
52062306a36Sopenharmony_ci	unsigned long end;
52162306a36Sopenharmony_ci	unsigned long page_size;
52262306a36Sopenharmony_ci	unsigned long psize;
52362306a36Sopenharmony_ci	bool also_pwc;
52462306a36Sopenharmony_ci};
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic void do_tlbiel_va_range(void *info)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct tlbiel_va_range *t = info;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	_tlbiel_va_range(t->start, t->end, t->pid, t->page_size,
53162306a36Sopenharmony_ci				    t->psize, t->also_pwc);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic __always_inline void _tlbie_lpid_va(unsigned long va, unsigned long lpid,
53562306a36Sopenharmony_ci			      unsigned long psize, unsigned long ric)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
54062306a36Sopenharmony_ci	__tlbie_lpid_va(va, lpid, ap, ric);
54162306a36Sopenharmony_ci	fixup_tlbie_lpid_va(va, lpid, ap);
54262306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic inline void _tlbie_va_range(unsigned long start, unsigned long end,
54662306a36Sopenharmony_ci				    unsigned long pid, unsigned long page_size,
54762306a36Sopenharmony_ci				    unsigned long psize, bool also_pwc)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
55062306a36Sopenharmony_ci	if (also_pwc)
55162306a36Sopenharmony_ci		__tlbie_pid(pid, RIC_FLUSH_PWC);
55262306a36Sopenharmony_ci	__tlbie_va_range(start, end, pid, page_size, psize);
55362306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic inline void _tlbiel_va_range_multicast(struct mm_struct *mm,
55762306a36Sopenharmony_ci				unsigned long start, unsigned long end,
55862306a36Sopenharmony_ci				unsigned long pid, unsigned long page_size,
55962306a36Sopenharmony_ci				unsigned long psize, bool also_pwc)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct cpumask *cpus = mm_cpumask(mm);
56262306a36Sopenharmony_ci	struct tlbiel_va_range t = { .start = start, .end = end,
56362306a36Sopenharmony_ci				.pid = pid, .page_size = page_size,
56462306a36Sopenharmony_ci				.psize = psize, .also_pwc = also_pwc };
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	on_each_cpu_mask(cpus, do_tlbiel_va_range, &t, 1);
56762306a36Sopenharmony_ci	if (atomic_read(&mm->context.copros) > 0)
56862306a36Sopenharmony_ci		_tlbie_va_range(start, end, pid, page_size, psize, also_pwc);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/*
57262306a36Sopenharmony_ci * Base TLB flushing operations:
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
57562306a36Sopenharmony_ci *  - flush_tlb_page(vma, vmaddr) flushes one page
57662306a36Sopenharmony_ci *  - flush_tlb_range(vma, start, end) flushes a range of pages
57762306a36Sopenharmony_ci *  - flush_tlb_kernel_range(start, end) flushes kernel pages
57862306a36Sopenharmony_ci *
57962306a36Sopenharmony_ci *  - local_* variants of page and mm only apply to the current
58062306a36Sopenharmony_ci *    processor
58162306a36Sopenharmony_ci */
58262306a36Sopenharmony_civoid radix__local_flush_tlb_mm(struct mm_struct *mm)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	unsigned long pid = mm->context.id;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
58762306a36Sopenharmony_ci		return;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	preempt_disable();
59062306a36Sopenharmony_ci	_tlbiel_pid(pid, RIC_FLUSH_TLB);
59162306a36Sopenharmony_ci	preempt_enable();
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_tlb_mm);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci#ifndef CONFIG_SMP
59662306a36Sopenharmony_civoid radix__local_flush_all_mm(struct mm_struct *mm)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	unsigned long pid = mm->context.id;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
60162306a36Sopenharmony_ci		return;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	preempt_disable();
60462306a36Sopenharmony_ci	_tlbiel_pid(pid, RIC_FLUSH_ALL);
60562306a36Sopenharmony_ci	preempt_enable();
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_all_mm);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void __flush_all_mm(struct mm_struct *mm, bool fullmm)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	radix__local_flush_all_mm(mm);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci#endif /* CONFIG_SMP */
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_civoid radix__local_flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
61662306a36Sopenharmony_ci				       int psize)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	unsigned long pid = mm->context.id;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
62162306a36Sopenharmony_ci		return;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	preempt_disable();
62462306a36Sopenharmony_ci	_tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
62562306a36Sopenharmony_ci	preempt_enable();
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_civoid radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE
63162306a36Sopenharmony_ci	/* need the return fix for nohash.c */
63262306a36Sopenharmony_ci	if (is_vm_hugetlb_page(vma))
63362306a36Sopenharmony_ci		return radix__local_flush_hugetlb_page(vma, vmaddr);
63462306a36Sopenharmony_ci#endif
63562306a36Sopenharmony_ci	radix__local_flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_tlb_page);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic bool mm_needs_flush_escalation(struct mm_struct *mm)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	/*
64262306a36Sopenharmony_ci	 * The P9 nest MMU has issues with the page walk cache caching PTEs
64362306a36Sopenharmony_ci	 * and not flushing them when RIC = 0 for a PID/LPID invalidate.
64462306a36Sopenharmony_ci	 *
64562306a36Sopenharmony_ci	 * This may have been fixed in shipping firmware (by disabling PWC
64662306a36Sopenharmony_ci	 * or preventing it from caching PTEs), but until that is confirmed,
64762306a36Sopenharmony_ci	 * this workaround is required - escalate all RIC=0 IS=1/2/3 flushes
64862306a36Sopenharmony_ci	 * to RIC=2.
64962306a36Sopenharmony_ci	 *
65062306a36Sopenharmony_ci	 * POWER10 (and P9P) does not have this problem.
65162306a36Sopenharmony_ci	 */
65262306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_31))
65362306a36Sopenharmony_ci		return false;
65462306a36Sopenharmony_ci	if (atomic_read(&mm->context.copros) > 0)
65562306a36Sopenharmony_ci		return true;
65662306a36Sopenharmony_ci	return false;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci/*
66062306a36Sopenharmony_ci * If always_flush is true, then flush even if this CPU can't be removed
66162306a36Sopenharmony_ci * from mm_cpumask.
66262306a36Sopenharmony_ci */
66362306a36Sopenharmony_civoid exit_lazy_flush_tlb(struct mm_struct *mm, bool always_flush)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	unsigned long pid = mm->context.id;
66662306a36Sopenharmony_ci	int cpu = smp_processor_id();
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/*
66962306a36Sopenharmony_ci	 * A kthread could have done a mmget_not_zero() after the flushing CPU
67062306a36Sopenharmony_ci	 * checked mm_cpumask, and be in the process of kthread_use_mm when
67162306a36Sopenharmony_ci	 * interrupted here. In that case, current->mm will be set to mm,
67262306a36Sopenharmony_ci	 * because kthread_use_mm() setting ->mm and switching to the mm is
67362306a36Sopenharmony_ci	 * done with interrupts off.
67462306a36Sopenharmony_ci	 */
67562306a36Sopenharmony_ci	if (current->mm == mm)
67662306a36Sopenharmony_ci		goto out;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (current->active_mm == mm) {
67962306a36Sopenharmony_ci		unsigned long flags;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		WARN_ON_ONCE(current->mm != NULL);
68262306a36Sopenharmony_ci		/*
68362306a36Sopenharmony_ci		 * It is a kernel thread and is using mm as the lazy tlb, so
68462306a36Sopenharmony_ci		 * switch it to init_mm. This is not always called from IPI
68562306a36Sopenharmony_ci		 * (e.g., flush_type_needed), so must disable irqs.
68662306a36Sopenharmony_ci		 */
68762306a36Sopenharmony_ci		local_irq_save(flags);
68862306a36Sopenharmony_ci		mmgrab_lazy_tlb(&init_mm);
68962306a36Sopenharmony_ci		current->active_mm = &init_mm;
69062306a36Sopenharmony_ci		switch_mm_irqs_off(mm, &init_mm, current);
69162306a36Sopenharmony_ci		mmdrop_lazy_tlb(mm);
69262306a36Sopenharmony_ci		local_irq_restore(flags);
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	/*
69662306a36Sopenharmony_ci	 * This IPI may be initiated from any source including those not
69762306a36Sopenharmony_ci	 * running the mm, so there may be a racing IPI that comes after
69862306a36Sopenharmony_ci	 * this one which finds the cpumask already clear. Check and avoid
69962306a36Sopenharmony_ci	 * underflowing the active_cpus count in that case. The race should
70062306a36Sopenharmony_ci	 * not otherwise be a problem, but the TLB must be flushed because
70162306a36Sopenharmony_ci	 * that's what the caller expects.
70262306a36Sopenharmony_ci	 */
70362306a36Sopenharmony_ci	if (cpumask_test_cpu(cpu, mm_cpumask(mm))) {
70462306a36Sopenharmony_ci		dec_mm_active_cpus(mm);
70562306a36Sopenharmony_ci		cpumask_clear_cpu(cpu, mm_cpumask(mm));
70662306a36Sopenharmony_ci		always_flush = true;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ciout:
71062306a36Sopenharmony_ci	if (always_flush)
71162306a36Sopenharmony_ci		_tlbiel_pid(pid, RIC_FLUSH_ALL);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci#ifdef CONFIG_SMP
71562306a36Sopenharmony_cistatic void do_exit_flush_lazy_tlb(void *arg)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct mm_struct *mm = arg;
71862306a36Sopenharmony_ci	exit_lazy_flush_tlb(mm, true);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic void exit_flush_lazy_tlbs(struct mm_struct *mm)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	/*
72462306a36Sopenharmony_ci	 * Would be nice if this was async so it could be run in
72562306a36Sopenharmony_ci	 * parallel with our local flush, but generic code does not
72662306a36Sopenharmony_ci	 * give a good API for it. Could extend the generic code or
72762306a36Sopenharmony_ci	 * make a special powerpc IPI for flushing TLBs.
72862306a36Sopenharmony_ci	 * For now it's not too performance critical.
72962306a36Sopenharmony_ci	 */
73062306a36Sopenharmony_ci	smp_call_function_many(mm_cpumask(mm), do_exit_flush_lazy_tlb,
73162306a36Sopenharmony_ci				(void *)mm, 1);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci#else /* CONFIG_SMP */
73562306a36Sopenharmony_cistatic inline void exit_flush_lazy_tlbs(struct mm_struct *mm) { }
73662306a36Sopenharmony_ci#endif /* CONFIG_SMP */
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, mm_cpumask_trim_clock);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/*
74162306a36Sopenharmony_ci * Interval between flushes at which we send out IPIs to check whether the
74262306a36Sopenharmony_ci * mm_cpumask can be trimmed for the case where it's not a single-threaded
74362306a36Sopenharmony_ci * process flushing its own mm. The intent is to reduce the cost of later
74462306a36Sopenharmony_ci * flushes. Don't want this to be so low that it adds noticable cost to TLB
74562306a36Sopenharmony_ci * flushing, or so high that it doesn't help reduce global TLBIEs.
74662306a36Sopenharmony_ci */
74762306a36Sopenharmony_cistatic unsigned long tlb_mm_cpumask_trim_timer = 1073;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic bool tick_and_test_trim_clock(void)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	if (__this_cpu_inc_return(mm_cpumask_trim_clock) ==
75262306a36Sopenharmony_ci			tlb_mm_cpumask_trim_timer) {
75362306a36Sopenharmony_ci		__this_cpu_write(mm_cpumask_trim_clock, 0);
75462306a36Sopenharmony_ci		return true;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	return false;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cienum tlb_flush_type {
76062306a36Sopenharmony_ci	FLUSH_TYPE_NONE,
76162306a36Sopenharmony_ci	FLUSH_TYPE_LOCAL,
76262306a36Sopenharmony_ci	FLUSH_TYPE_GLOBAL,
76362306a36Sopenharmony_ci};
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic enum tlb_flush_type flush_type_needed(struct mm_struct *mm, bool fullmm)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	int active_cpus = atomic_read(&mm->context.active_cpus);
76862306a36Sopenharmony_ci	int cpu = smp_processor_id();
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (active_cpus == 0)
77162306a36Sopenharmony_ci		return FLUSH_TYPE_NONE;
77262306a36Sopenharmony_ci	if (active_cpus == 1 && cpumask_test_cpu(cpu, mm_cpumask(mm))) {
77362306a36Sopenharmony_ci		if (current->mm != mm) {
77462306a36Sopenharmony_ci			/*
77562306a36Sopenharmony_ci			 * Asynchronous flush sources may trim down to nothing
77662306a36Sopenharmony_ci			 * if the process is not running, so occasionally try
77762306a36Sopenharmony_ci			 * to trim.
77862306a36Sopenharmony_ci			 */
77962306a36Sopenharmony_ci			if (tick_and_test_trim_clock()) {
78062306a36Sopenharmony_ci				exit_lazy_flush_tlb(mm, true);
78162306a36Sopenharmony_ci				return FLUSH_TYPE_NONE;
78262306a36Sopenharmony_ci			}
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		return FLUSH_TYPE_LOCAL;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Coprocessors require TLBIE to invalidate nMMU. */
78862306a36Sopenharmony_ci	if (atomic_read(&mm->context.copros) > 0)
78962306a36Sopenharmony_ci		return FLUSH_TYPE_GLOBAL;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/*
79262306a36Sopenharmony_ci	 * In the fullmm case there's no point doing the exit_flush_lazy_tlbs
79362306a36Sopenharmony_ci	 * because the mm is being taken down anyway, and a TLBIE tends to
79462306a36Sopenharmony_ci	 * be faster than an IPI+TLBIEL.
79562306a36Sopenharmony_ci	 */
79662306a36Sopenharmony_ci	if (fullmm)
79762306a36Sopenharmony_ci		return FLUSH_TYPE_GLOBAL;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/*
80062306a36Sopenharmony_ci	 * If we are running the only thread of a single-threaded process,
80162306a36Sopenharmony_ci	 * then we should almost always be able to trim off the rest of the
80262306a36Sopenharmony_ci	 * CPU mask (except in the case of use_mm() races), so always try
80362306a36Sopenharmony_ci	 * trimming the mask.
80462306a36Sopenharmony_ci	 */
80562306a36Sopenharmony_ci	if (atomic_read(&mm->mm_users) <= 1 && current->mm == mm) {
80662306a36Sopenharmony_ci		exit_flush_lazy_tlbs(mm);
80762306a36Sopenharmony_ci		/*
80862306a36Sopenharmony_ci		 * use_mm() race could prevent IPIs from being able to clear
80962306a36Sopenharmony_ci		 * the cpumask here, however those users are established
81062306a36Sopenharmony_ci		 * after our first check (and so after the PTEs are removed),
81162306a36Sopenharmony_ci		 * and the TLB still gets flushed by the IPI, so this CPU
81262306a36Sopenharmony_ci		 * will only require a local flush.
81362306a36Sopenharmony_ci		 */
81462306a36Sopenharmony_ci		return FLUSH_TYPE_LOCAL;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/*
81862306a36Sopenharmony_ci	 * Occasionally try to trim down the cpumask. It's possible this can
81962306a36Sopenharmony_ci	 * bring the mask to zero, which results in no flush.
82062306a36Sopenharmony_ci	 */
82162306a36Sopenharmony_ci	if (tick_and_test_trim_clock()) {
82262306a36Sopenharmony_ci		exit_flush_lazy_tlbs(mm);
82362306a36Sopenharmony_ci		if (current->mm == mm)
82462306a36Sopenharmony_ci			return FLUSH_TYPE_LOCAL;
82562306a36Sopenharmony_ci		if (cpumask_test_cpu(cpu, mm_cpumask(mm)))
82662306a36Sopenharmony_ci			exit_lazy_flush_tlb(mm, true);
82762306a36Sopenharmony_ci		return FLUSH_TYPE_NONE;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	return FLUSH_TYPE_GLOBAL;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci#ifdef CONFIG_SMP
83462306a36Sopenharmony_civoid radix__flush_tlb_mm(struct mm_struct *mm)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	unsigned long pid;
83762306a36Sopenharmony_ci	enum tlb_flush_type type;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	pid = mm->context.id;
84062306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
84162306a36Sopenharmony_ci		return;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	preempt_disable();
84462306a36Sopenharmony_ci	/*
84562306a36Sopenharmony_ci	 * Order loads of mm_cpumask (in flush_type_needed) vs previous
84662306a36Sopenharmony_ci	 * stores to clear ptes before the invalidate. See barrier in
84762306a36Sopenharmony_ci	 * switch_mm_irqs_off
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci	smp_mb();
85062306a36Sopenharmony_ci	type = flush_type_needed(mm, false);
85162306a36Sopenharmony_ci	if (type == FLUSH_TYPE_LOCAL) {
85262306a36Sopenharmony_ci		_tlbiel_pid(pid, RIC_FLUSH_TLB);
85362306a36Sopenharmony_ci	} else if (type == FLUSH_TYPE_GLOBAL) {
85462306a36Sopenharmony_ci		if (!mmu_has_feature(MMU_FTR_GTSE)) {
85562306a36Sopenharmony_ci			unsigned long tgt = H_RPTI_TARGET_CMMU;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci			if (atomic_read(&mm->context.copros) > 0)
85862306a36Sopenharmony_ci				tgt |= H_RPTI_TARGET_NMMU;
85962306a36Sopenharmony_ci			pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB,
86062306a36Sopenharmony_ci					       H_RPTI_PAGE_ALL, 0, -1UL);
86162306a36Sopenharmony_ci		} else if (cputlb_use_tlbie()) {
86262306a36Sopenharmony_ci			if (mm_needs_flush_escalation(mm))
86362306a36Sopenharmony_ci				_tlbie_pid(pid, RIC_FLUSH_ALL);
86462306a36Sopenharmony_ci			else
86562306a36Sopenharmony_ci				_tlbie_pid(pid, RIC_FLUSH_TLB);
86662306a36Sopenharmony_ci		} else {
86762306a36Sopenharmony_ci			_tlbiel_pid_multicast(mm, pid, RIC_FLUSH_TLB);
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci	preempt_enable();
87162306a36Sopenharmony_ci	mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_mm);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic void __flush_all_mm(struct mm_struct *mm, bool fullmm)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	unsigned long pid;
87862306a36Sopenharmony_ci	enum tlb_flush_type type;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	pid = mm->context.id;
88162306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
88262306a36Sopenharmony_ci		return;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	preempt_disable();
88562306a36Sopenharmony_ci	smp_mb(); /* see radix__flush_tlb_mm */
88662306a36Sopenharmony_ci	type = flush_type_needed(mm, fullmm);
88762306a36Sopenharmony_ci	if (type == FLUSH_TYPE_LOCAL) {
88862306a36Sopenharmony_ci		_tlbiel_pid(pid, RIC_FLUSH_ALL);
88962306a36Sopenharmony_ci	} else if (type == FLUSH_TYPE_GLOBAL) {
89062306a36Sopenharmony_ci		if (!mmu_has_feature(MMU_FTR_GTSE)) {
89162306a36Sopenharmony_ci			unsigned long tgt = H_RPTI_TARGET_CMMU;
89262306a36Sopenharmony_ci			unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
89362306a36Sopenharmony_ci					     H_RPTI_TYPE_PRT;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci			if (atomic_read(&mm->context.copros) > 0)
89662306a36Sopenharmony_ci				tgt |= H_RPTI_TARGET_NMMU;
89762306a36Sopenharmony_ci			pseries_rpt_invalidate(pid, tgt, type,
89862306a36Sopenharmony_ci					       H_RPTI_PAGE_ALL, 0, -1UL);
89962306a36Sopenharmony_ci		} else if (cputlb_use_tlbie())
90062306a36Sopenharmony_ci			_tlbie_pid(pid, RIC_FLUSH_ALL);
90162306a36Sopenharmony_ci		else
90262306a36Sopenharmony_ci			_tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL);
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	preempt_enable();
90562306a36Sopenharmony_ci	mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_civoid radix__flush_all_mm(struct mm_struct *mm)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	__flush_all_mm(mm, false);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_all_mm);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_civoid radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
91562306a36Sopenharmony_ci				 int psize)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	unsigned long pid;
91862306a36Sopenharmony_ci	enum tlb_flush_type type;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	pid = mm->context.id;
92162306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
92262306a36Sopenharmony_ci		return;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	preempt_disable();
92562306a36Sopenharmony_ci	smp_mb(); /* see radix__flush_tlb_mm */
92662306a36Sopenharmony_ci	type = flush_type_needed(mm, false);
92762306a36Sopenharmony_ci	if (type == FLUSH_TYPE_LOCAL) {
92862306a36Sopenharmony_ci		_tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
92962306a36Sopenharmony_ci	} else if (type == FLUSH_TYPE_GLOBAL) {
93062306a36Sopenharmony_ci		if (!mmu_has_feature(MMU_FTR_GTSE)) {
93162306a36Sopenharmony_ci			unsigned long tgt, pg_sizes, size;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci			tgt = H_RPTI_TARGET_CMMU;
93462306a36Sopenharmony_ci			pg_sizes = psize_to_rpti_pgsize(psize);
93562306a36Sopenharmony_ci			size = 1UL << mmu_psize_to_shift(psize);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci			if (atomic_read(&mm->context.copros) > 0)
93862306a36Sopenharmony_ci				tgt |= H_RPTI_TARGET_NMMU;
93962306a36Sopenharmony_ci			pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB,
94062306a36Sopenharmony_ci					       pg_sizes, vmaddr,
94162306a36Sopenharmony_ci					       vmaddr + size);
94262306a36Sopenharmony_ci		} else if (cputlb_use_tlbie())
94362306a36Sopenharmony_ci			_tlbie_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
94462306a36Sopenharmony_ci		else
94562306a36Sopenharmony_ci			_tlbiel_va_multicast(mm, vmaddr, pid, psize, RIC_FLUSH_TLB);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	preempt_enable();
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_civoid radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE
95362306a36Sopenharmony_ci	if (is_vm_hugetlb_page(vma))
95462306a36Sopenharmony_ci		return radix__flush_hugetlb_page(vma, vmaddr);
95562306a36Sopenharmony_ci#endif
95662306a36Sopenharmony_ci	radix__flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_page);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci#endif /* CONFIG_SMP */
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void do_tlbiel_kernel(void *info)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	_tlbiel_pid(0, RIC_FLUSH_ALL);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic inline void _tlbiel_kernel_broadcast(void)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	on_each_cpu(do_tlbiel_kernel, NULL, 1);
97062306a36Sopenharmony_ci	if (tlbie_capable) {
97162306a36Sopenharmony_ci		/*
97262306a36Sopenharmony_ci		 * Coherent accelerators don't refcount kernel memory mappings,
97362306a36Sopenharmony_ci		 * so have to always issue a tlbie for them. This is quite a
97462306a36Sopenharmony_ci		 * slow path anyway.
97562306a36Sopenharmony_ci		 */
97662306a36Sopenharmony_ci		_tlbie_pid(0, RIC_FLUSH_ALL);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/*
98162306a36Sopenharmony_ci * If kernel TLBIs ever become local rather than global, then
98262306a36Sopenharmony_ci * drivers/misc/ocxl/link.c:ocxl_link_add_pe will need some work, as it
98362306a36Sopenharmony_ci * assumes kernel TLBIs are global.
98462306a36Sopenharmony_ci */
98562306a36Sopenharmony_civoid radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_GTSE)) {
98862306a36Sopenharmony_ci		unsigned long tgt = H_RPTI_TARGET_CMMU | H_RPTI_TARGET_NMMU;
98962306a36Sopenharmony_ci		unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
99062306a36Sopenharmony_ci				     H_RPTI_TYPE_PRT;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		pseries_rpt_invalidate(0, tgt, type, H_RPTI_PAGE_ALL,
99362306a36Sopenharmony_ci				       start, end);
99462306a36Sopenharmony_ci	} else if (cputlb_use_tlbie())
99562306a36Sopenharmony_ci		_tlbie_pid(0, RIC_FLUSH_ALL);
99662306a36Sopenharmony_ci	else
99762306a36Sopenharmony_ci		_tlbiel_kernel_broadcast();
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_kernel_range);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci/*
100262306a36Sopenharmony_ci * Doesn't appear to be used anywhere. Remove.
100362306a36Sopenharmony_ci */
100462306a36Sopenharmony_ci#define TLB_FLUSH_ALL -1UL
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci/*
100762306a36Sopenharmony_ci * Number of pages above which we invalidate the entire PID rather than
100862306a36Sopenharmony_ci * flush individual pages, for local and global flushes respectively.
100962306a36Sopenharmony_ci *
101062306a36Sopenharmony_ci * tlbie goes out to the interconnect and individual ops are more costly.
101162306a36Sopenharmony_ci * It also does not iterate over sets like the local tlbiel variant when
101262306a36Sopenharmony_ci * invalidating a full PID, so it has a far lower threshold to change from
101362306a36Sopenharmony_ci * individual page flushes to full-pid flushes.
101462306a36Sopenharmony_ci */
101562306a36Sopenharmony_cistatic u32 tlb_single_page_flush_ceiling __read_mostly = 33;
101662306a36Sopenharmony_cistatic u32 tlb_local_single_page_flush_ceiling __read_mostly = POWER9_TLB_SETS_RADIX * 2;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic inline void __radix__flush_tlb_range(struct mm_struct *mm,
101962306a36Sopenharmony_ci					    unsigned long start, unsigned long end)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	unsigned long pid;
102262306a36Sopenharmony_ci	unsigned int page_shift = mmu_psize_defs[mmu_virtual_psize].shift;
102362306a36Sopenharmony_ci	unsigned long page_size = 1UL << page_shift;
102462306a36Sopenharmony_ci	unsigned long nr_pages = (end - start) >> page_shift;
102562306a36Sopenharmony_ci	bool flush_pid, flush_pwc = false;
102662306a36Sopenharmony_ci	enum tlb_flush_type type;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	pid = mm->context.id;
102962306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
103062306a36Sopenharmony_ci		return;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	WARN_ON_ONCE(end == TLB_FLUSH_ALL);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	preempt_disable();
103562306a36Sopenharmony_ci	smp_mb(); /* see radix__flush_tlb_mm */
103662306a36Sopenharmony_ci	type = flush_type_needed(mm, false);
103762306a36Sopenharmony_ci	if (type == FLUSH_TYPE_NONE)
103862306a36Sopenharmony_ci		goto out;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	if (type == FLUSH_TYPE_GLOBAL)
104162306a36Sopenharmony_ci		flush_pid = nr_pages > tlb_single_page_flush_ceiling;
104262306a36Sopenharmony_ci	else
104362306a36Sopenharmony_ci		flush_pid = nr_pages > tlb_local_single_page_flush_ceiling;
104462306a36Sopenharmony_ci	/*
104562306a36Sopenharmony_ci	 * full pid flush already does the PWC flush. if it is not full pid
104662306a36Sopenharmony_ci	 * flush check the range is more than PMD and force a pwc flush
104762306a36Sopenharmony_ci	 * mremap() depends on this behaviour.
104862306a36Sopenharmony_ci	 */
104962306a36Sopenharmony_ci	if (!flush_pid && (end - start) >= PMD_SIZE)
105062306a36Sopenharmony_ci		flush_pwc = true;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) {
105362306a36Sopenharmony_ci		unsigned long type = H_RPTI_TYPE_TLB;
105462306a36Sopenharmony_ci		unsigned long tgt = H_RPTI_TARGET_CMMU;
105562306a36Sopenharmony_ci		unsigned long pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
105862306a36Sopenharmony_ci			pg_sizes |= psize_to_rpti_pgsize(MMU_PAGE_2M);
105962306a36Sopenharmony_ci		if (atomic_read(&mm->context.copros) > 0)
106062306a36Sopenharmony_ci			tgt |= H_RPTI_TARGET_NMMU;
106162306a36Sopenharmony_ci		if (flush_pwc)
106262306a36Sopenharmony_ci			type |= H_RPTI_TYPE_PWC;
106362306a36Sopenharmony_ci		pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end);
106462306a36Sopenharmony_ci	} else if (flush_pid) {
106562306a36Sopenharmony_ci		/*
106662306a36Sopenharmony_ci		 * We are now flushing a range larger than PMD size force a RIC_FLUSH_ALL
106762306a36Sopenharmony_ci		 */
106862306a36Sopenharmony_ci		if (type == FLUSH_TYPE_LOCAL) {
106962306a36Sopenharmony_ci			_tlbiel_pid(pid, RIC_FLUSH_ALL);
107062306a36Sopenharmony_ci		} else {
107162306a36Sopenharmony_ci			if (cputlb_use_tlbie()) {
107262306a36Sopenharmony_ci				_tlbie_pid(pid, RIC_FLUSH_ALL);
107362306a36Sopenharmony_ci			} else {
107462306a36Sopenharmony_ci				_tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL);
107562306a36Sopenharmony_ci			}
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci	} else {
107862306a36Sopenharmony_ci		bool hflush;
107962306a36Sopenharmony_ci		unsigned long hstart, hend;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		hstart = (start + PMD_SIZE - 1) & PMD_MASK;
108262306a36Sopenharmony_ci		hend = end & PMD_MASK;
108362306a36Sopenharmony_ci		hflush = IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && hstart < hend;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		if (type == FLUSH_TYPE_LOCAL) {
108662306a36Sopenharmony_ci			asm volatile("ptesync": : :"memory");
108762306a36Sopenharmony_ci			if (flush_pwc)
108862306a36Sopenharmony_ci				/* For PWC, only one flush is needed */
108962306a36Sopenharmony_ci				__tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
109062306a36Sopenharmony_ci			__tlbiel_va_range(start, end, pid, page_size, mmu_virtual_psize);
109162306a36Sopenharmony_ci			if (hflush)
109262306a36Sopenharmony_ci				__tlbiel_va_range(hstart, hend, pid,
109362306a36Sopenharmony_ci						PMD_SIZE, MMU_PAGE_2M);
109462306a36Sopenharmony_ci			ppc_after_tlbiel_barrier();
109562306a36Sopenharmony_ci		} else if (cputlb_use_tlbie()) {
109662306a36Sopenharmony_ci			asm volatile("ptesync": : :"memory");
109762306a36Sopenharmony_ci			if (flush_pwc)
109862306a36Sopenharmony_ci				__tlbie_pid(pid, RIC_FLUSH_PWC);
109962306a36Sopenharmony_ci			__tlbie_va_range(start, end, pid, page_size, mmu_virtual_psize);
110062306a36Sopenharmony_ci			if (hflush)
110162306a36Sopenharmony_ci				__tlbie_va_range(hstart, hend, pid,
110262306a36Sopenharmony_ci						PMD_SIZE, MMU_PAGE_2M);
110362306a36Sopenharmony_ci			asm volatile("eieio; tlbsync; ptesync": : :"memory");
110462306a36Sopenharmony_ci		} else {
110562306a36Sopenharmony_ci			_tlbiel_va_range_multicast(mm,
110662306a36Sopenharmony_ci					start, end, pid, page_size, mmu_virtual_psize, flush_pwc);
110762306a36Sopenharmony_ci			if (hflush)
110862306a36Sopenharmony_ci				_tlbiel_va_range_multicast(mm,
110962306a36Sopenharmony_ci					hstart, hend, pid, PMD_SIZE, MMU_PAGE_2M, flush_pwc);
111062306a36Sopenharmony_ci		}
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ciout:
111362306a36Sopenharmony_ci	preempt_enable();
111462306a36Sopenharmony_ci	mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_civoid radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
111862306a36Sopenharmony_ci		     unsigned long end)
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE
112262306a36Sopenharmony_ci	if (is_vm_hugetlb_page(vma))
112362306a36Sopenharmony_ci		return radix__flush_hugetlb_tlb_range(vma, start, end);
112462306a36Sopenharmony_ci#endif
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	__radix__flush_tlb_range(vma->vm_mm, start, end);
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_range);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int radix_get_mmu_psize(int page_size)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	int psize;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (page_size == (1UL << mmu_psize_defs[mmu_virtual_psize].shift))
113562306a36Sopenharmony_ci		psize = mmu_virtual_psize;
113662306a36Sopenharmony_ci	else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_2M].shift))
113762306a36Sopenharmony_ci		psize = MMU_PAGE_2M;
113862306a36Sopenharmony_ci	else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_1G].shift))
113962306a36Sopenharmony_ci		psize = MMU_PAGE_1G;
114062306a36Sopenharmony_ci	else
114162306a36Sopenharmony_ci		return -1;
114262306a36Sopenharmony_ci	return psize;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/*
114662306a36Sopenharmony_ci * Flush partition scoped LPID address translation for all CPUs.
114762306a36Sopenharmony_ci */
114862306a36Sopenharmony_civoid radix__flush_tlb_lpid_page(unsigned int lpid,
114962306a36Sopenharmony_ci					unsigned long addr,
115062306a36Sopenharmony_ci					unsigned long page_size)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	int psize = radix_get_mmu_psize(page_size);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	_tlbie_lpid_va(addr, lpid, psize, RIC_FLUSH_TLB);
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_tlb_lpid_page);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci/*
115962306a36Sopenharmony_ci * Flush partition scoped PWC from LPID for all CPUs.
116062306a36Sopenharmony_ci */
116162306a36Sopenharmony_civoid radix__flush_pwc_lpid(unsigned int lpid)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	_tlbie_lpid(lpid, RIC_FLUSH_PWC);
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_pwc_lpid);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci/*
116862306a36Sopenharmony_ci * Flush partition scoped translations from LPID (=LPIDR)
116962306a36Sopenharmony_ci */
117062306a36Sopenharmony_civoid radix__flush_all_lpid(unsigned int lpid)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	_tlbie_lpid(lpid, RIC_FLUSH_ALL);
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_all_lpid);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci/*
117762306a36Sopenharmony_ci * Flush process scoped translations from LPID (=LPIDR)
117862306a36Sopenharmony_ci */
117962306a36Sopenharmony_civoid radix__flush_all_lpid_guest(unsigned int lpid)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	_tlbie_lpid_guest(lpid, RIC_FLUSH_ALL);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_civoid radix__tlb_flush(struct mmu_gather *tlb)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	int psize = 0;
118762306a36Sopenharmony_ci	struct mm_struct *mm = tlb->mm;
118862306a36Sopenharmony_ci	int page_size = tlb->page_size;
118962306a36Sopenharmony_ci	unsigned long start = tlb->start;
119062306a36Sopenharmony_ci	unsigned long end = tlb->end;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	/*
119362306a36Sopenharmony_ci	 * if page size is not something we understand, do a full mm flush
119462306a36Sopenharmony_ci	 *
119562306a36Sopenharmony_ci	 * A "fullmm" flush must always do a flush_all_mm (RIC=2) flush
119662306a36Sopenharmony_ci	 * that flushes the process table entry cache upon process teardown.
119762306a36Sopenharmony_ci	 * See the comment for radix in arch_exit_mmap().
119862306a36Sopenharmony_ci	 */
119962306a36Sopenharmony_ci	if (tlb->fullmm) {
120062306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_MMU_LAZY_TLB_SHOOTDOWN)) {
120162306a36Sopenharmony_ci			/*
120262306a36Sopenharmony_ci			 * Shootdown based lazy tlb mm refcounting means we
120362306a36Sopenharmony_ci			 * have to IPI everyone in the mm_cpumask anyway soon
120462306a36Sopenharmony_ci			 * when the mm goes away, so might as well do it as
120562306a36Sopenharmony_ci			 * part of the final flush now.
120662306a36Sopenharmony_ci			 *
120762306a36Sopenharmony_ci			 * If lazy shootdown was improved to reduce IPIs (e.g.,
120862306a36Sopenharmony_ci			 * by batching), then it may end up being better to use
120962306a36Sopenharmony_ci			 * tlbies here instead.
121062306a36Sopenharmony_ci			 */
121162306a36Sopenharmony_ci			preempt_disable();
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci			smp_mb(); /* see radix__flush_tlb_mm */
121462306a36Sopenharmony_ci			exit_flush_lazy_tlbs(mm);
121562306a36Sopenharmony_ci			__flush_all_mm(mm, true);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci			preempt_enable();
121862306a36Sopenharmony_ci		} else {
121962306a36Sopenharmony_ci			__flush_all_mm(mm, true);
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	} else if ( (psize = radix_get_mmu_psize(page_size)) == -1) {
122362306a36Sopenharmony_ci		if (!tlb->freed_tables)
122462306a36Sopenharmony_ci			radix__flush_tlb_mm(mm);
122562306a36Sopenharmony_ci		else
122662306a36Sopenharmony_ci			radix__flush_all_mm(mm);
122762306a36Sopenharmony_ci	} else {
122862306a36Sopenharmony_ci		if (!tlb->freed_tables)
122962306a36Sopenharmony_ci			radix__flush_tlb_range_psize(mm, start, end, psize);
123062306a36Sopenharmony_ci		else
123162306a36Sopenharmony_ci			radix__flush_tlb_pwc_range_psize(mm, start, end, psize);
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic void __radix__flush_tlb_range_psize(struct mm_struct *mm,
123662306a36Sopenharmony_ci				unsigned long start, unsigned long end,
123762306a36Sopenharmony_ci				int psize, bool also_pwc)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	unsigned long pid;
124062306a36Sopenharmony_ci	unsigned int page_shift = mmu_psize_defs[psize].shift;
124162306a36Sopenharmony_ci	unsigned long page_size = 1UL << page_shift;
124262306a36Sopenharmony_ci	unsigned long nr_pages = (end - start) >> page_shift;
124362306a36Sopenharmony_ci	bool flush_pid;
124462306a36Sopenharmony_ci	enum tlb_flush_type type;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	pid = mm->context.id;
124762306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
124862306a36Sopenharmony_ci		return;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	WARN_ON_ONCE(end == TLB_FLUSH_ALL);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	preempt_disable();
125362306a36Sopenharmony_ci	smp_mb(); /* see radix__flush_tlb_mm */
125462306a36Sopenharmony_ci	type = flush_type_needed(mm, false);
125562306a36Sopenharmony_ci	if (type == FLUSH_TYPE_NONE)
125662306a36Sopenharmony_ci		goto out;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (type == FLUSH_TYPE_GLOBAL)
125962306a36Sopenharmony_ci		flush_pid = nr_pages > tlb_single_page_flush_ceiling;
126062306a36Sopenharmony_ci	else
126162306a36Sopenharmony_ci		flush_pid = nr_pages > tlb_local_single_page_flush_ceiling;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) {
126462306a36Sopenharmony_ci		unsigned long tgt = H_RPTI_TARGET_CMMU;
126562306a36Sopenharmony_ci		unsigned long type = H_RPTI_TYPE_TLB;
126662306a36Sopenharmony_ci		unsigned long pg_sizes = psize_to_rpti_pgsize(psize);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci		if (also_pwc)
126962306a36Sopenharmony_ci			type |= H_RPTI_TYPE_PWC;
127062306a36Sopenharmony_ci		if (atomic_read(&mm->context.copros) > 0)
127162306a36Sopenharmony_ci			tgt |= H_RPTI_TARGET_NMMU;
127262306a36Sopenharmony_ci		pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end);
127362306a36Sopenharmony_ci	} else if (flush_pid) {
127462306a36Sopenharmony_ci		if (type == FLUSH_TYPE_LOCAL) {
127562306a36Sopenharmony_ci			_tlbiel_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
127662306a36Sopenharmony_ci		} else {
127762306a36Sopenharmony_ci			if (cputlb_use_tlbie()) {
127862306a36Sopenharmony_ci				if (mm_needs_flush_escalation(mm))
127962306a36Sopenharmony_ci					also_pwc = true;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci				_tlbie_pid(pid,
128262306a36Sopenharmony_ci					also_pwc ?  RIC_FLUSH_ALL : RIC_FLUSH_TLB);
128362306a36Sopenharmony_ci			} else {
128462306a36Sopenharmony_ci				_tlbiel_pid_multicast(mm, pid,
128562306a36Sopenharmony_ci					also_pwc ?  RIC_FLUSH_ALL : RIC_FLUSH_TLB);
128662306a36Sopenharmony_ci			}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci	} else {
129062306a36Sopenharmony_ci		if (type == FLUSH_TYPE_LOCAL)
129162306a36Sopenharmony_ci			_tlbiel_va_range(start, end, pid, page_size, psize, also_pwc);
129262306a36Sopenharmony_ci		else if (cputlb_use_tlbie())
129362306a36Sopenharmony_ci			_tlbie_va_range(start, end, pid, page_size, psize, also_pwc);
129462306a36Sopenharmony_ci		else
129562306a36Sopenharmony_ci			_tlbiel_va_range_multicast(mm,
129662306a36Sopenharmony_ci					start, end, pid, page_size, psize, also_pwc);
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ciout:
129962306a36Sopenharmony_ci	preempt_enable();
130062306a36Sopenharmony_ci	mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end);
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_civoid radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
130462306a36Sopenharmony_ci				  unsigned long end, int psize)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	return __radix__flush_tlb_range_psize(mm, start, end, psize, false);
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_civoid radix__flush_tlb_pwc_range_psize(struct mm_struct *mm, unsigned long start,
131062306a36Sopenharmony_ci				      unsigned long end, int psize)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	__radix__flush_tlb_range_psize(mm, start, end, psize, true);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
131662306a36Sopenharmony_civoid radix__flush_tlb_collapsed_pmd(struct mm_struct *mm, unsigned long addr)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	unsigned long pid, end;
131962306a36Sopenharmony_ci	enum tlb_flush_type type;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	pid = mm->context.id;
132262306a36Sopenharmony_ci	if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT))
132362306a36Sopenharmony_ci		return;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	/* 4k page size, just blow the world */
132662306a36Sopenharmony_ci	if (PAGE_SIZE == 0x1000) {
132762306a36Sopenharmony_ci		radix__flush_all_mm(mm);
132862306a36Sopenharmony_ci		return;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	end = addr + HPAGE_PMD_SIZE;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/* Otherwise first do the PWC, then iterate the pages. */
133462306a36Sopenharmony_ci	preempt_disable();
133562306a36Sopenharmony_ci	smp_mb(); /* see radix__flush_tlb_mm */
133662306a36Sopenharmony_ci	type = flush_type_needed(mm, false);
133762306a36Sopenharmony_ci	if (type == FLUSH_TYPE_LOCAL) {
133862306a36Sopenharmony_ci		_tlbiel_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
133962306a36Sopenharmony_ci	} else if (type == FLUSH_TYPE_GLOBAL) {
134062306a36Sopenharmony_ci		if (!mmu_has_feature(MMU_FTR_GTSE)) {
134162306a36Sopenharmony_ci			unsigned long tgt, type, pg_sizes;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci			tgt = H_RPTI_TARGET_CMMU;
134462306a36Sopenharmony_ci			type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
134562306a36Sopenharmony_ci			       H_RPTI_TYPE_PRT;
134662306a36Sopenharmony_ci			pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci			if (atomic_read(&mm->context.copros) > 0)
134962306a36Sopenharmony_ci				tgt |= H_RPTI_TARGET_NMMU;
135062306a36Sopenharmony_ci			pseries_rpt_invalidate(pid, tgt, type, pg_sizes,
135162306a36Sopenharmony_ci					       addr, end);
135262306a36Sopenharmony_ci		} else if (cputlb_use_tlbie())
135362306a36Sopenharmony_ci			_tlbie_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
135462306a36Sopenharmony_ci		else
135562306a36Sopenharmony_ci			_tlbiel_va_range_multicast(mm,
135662306a36Sopenharmony_ci					addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	preempt_enable();
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_civoid radix__flush_pmd_tlb_range(struct vm_area_struct *vma,
136462306a36Sopenharmony_ci				unsigned long start, unsigned long end)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_2M);
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_pmd_tlb_range);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_civoid radix__flush_pud_tlb_range(struct vm_area_struct *vma,
137162306a36Sopenharmony_ci				unsigned long start, unsigned long end)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_1G);
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_pud_tlb_range);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_civoid radix__flush_tlb_all(void)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	unsigned long rb,prs,r,rs;
138062306a36Sopenharmony_ci	unsigned long ric = RIC_FLUSH_ALL;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	rb = 0x3 << PPC_BITLSHIFT(53); /* IS = 3 */
138362306a36Sopenharmony_ci	prs = 0; /* partition scoped */
138462306a36Sopenharmony_ci	r = 1;   /* radix format */
138562306a36Sopenharmony_ci	rs = 1 & ((1UL << 32) - 1); /* any LPID value to flush guest mappings */
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
138862306a36Sopenharmony_ci	/*
138962306a36Sopenharmony_ci	 * now flush guest entries by passing PRS = 1 and LPID != 0
139062306a36Sopenharmony_ci	 */
139162306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
139262306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(1), "i"(ric), "r"(rs) : "memory");
139362306a36Sopenharmony_ci	/*
139462306a36Sopenharmony_ci	 * now flush host entires by passing PRS = 0 and LPID == 0
139562306a36Sopenharmony_ci	 */
139662306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
139762306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(0) : "memory");
139862306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync": : :"memory");
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
140262306a36Sopenharmony_cistatic __always_inline void __tlbie_pid_lpid(unsigned long pid,
140362306a36Sopenharmony_ci					     unsigned long lpid,
140462306a36Sopenharmony_ci					     unsigned long ric)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	unsigned long rb, rs, prs, r;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	rb = PPC_BIT(53); /* IS = 1 */
140962306a36Sopenharmony_ci	rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31)));
141062306a36Sopenharmony_ci	prs = 1; /* process scoped */
141162306a36Sopenharmony_ci	r = 1;   /* radix format */
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
141462306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
141562306a36Sopenharmony_ci	trace_tlbie(0, 0, rb, rs, ric, prs, r);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic __always_inline void __tlbie_va_lpid(unsigned long va, unsigned long pid,
141962306a36Sopenharmony_ci					    unsigned long lpid,
142062306a36Sopenharmony_ci					    unsigned long ap, unsigned long ric)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	unsigned long rb, rs, prs, r;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	rb = va & ~(PPC_BITMASK(52, 63));
142562306a36Sopenharmony_ci	rb |= ap << PPC_BITLSHIFT(58);
142662306a36Sopenharmony_ci	rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31)));
142762306a36Sopenharmony_ci	prs = 1; /* process scoped */
142862306a36Sopenharmony_ci	r = 1;   /* radix format */
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
143162306a36Sopenharmony_ci		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
143262306a36Sopenharmony_ci	trace_tlbie(0, 0, rb, rs, ric, prs, r);
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic inline void fixup_tlbie_pid_lpid(unsigned long pid, unsigned long lpid)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	/*
143862306a36Sopenharmony_ci	 * We can use any address for the invalidation, pick one which is
143962306a36Sopenharmony_ci	 * probably unused as an optimisation.
144062306a36Sopenharmony_ci	 */
144162306a36Sopenharmony_ci	unsigned long va = ((1UL << 52) - 1);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
144462306a36Sopenharmony_ci		asm volatile("ptesync" : : : "memory");
144562306a36Sopenharmony_ci		__tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB);
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
144962306a36Sopenharmony_ci		asm volatile("ptesync" : : : "memory");
145062306a36Sopenharmony_ci		__tlbie_va_lpid(va, pid, lpid, mmu_get_ap(MMU_PAGE_64K),
145162306a36Sopenharmony_ci				RIC_FLUSH_TLB);
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic inline void _tlbie_pid_lpid(unsigned long pid, unsigned long lpid,
145662306a36Sopenharmony_ci				   unsigned long ric)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	asm volatile("ptesync" : : : "memory");
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	/*
146162306a36Sopenharmony_ci	 * Workaround the fact that the "ric" argument to __tlbie_pid
146262306a36Sopenharmony_ci	 * must be a compile-time contraint to match the "i" constraint
146362306a36Sopenharmony_ci	 * in the asm statement.
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	switch (ric) {
146662306a36Sopenharmony_ci	case RIC_FLUSH_TLB:
146762306a36Sopenharmony_ci		__tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
146862306a36Sopenharmony_ci		fixup_tlbie_pid_lpid(pid, lpid);
146962306a36Sopenharmony_ci		break;
147062306a36Sopenharmony_ci	case RIC_FLUSH_PWC:
147162306a36Sopenharmony_ci		__tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
147262306a36Sopenharmony_ci		break;
147362306a36Sopenharmony_ci	case RIC_FLUSH_ALL:
147462306a36Sopenharmony_ci	default:
147562306a36Sopenharmony_ci		__tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL);
147662306a36Sopenharmony_ci		fixup_tlbie_pid_lpid(pid, lpid);
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync" : : : "memory");
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic inline void fixup_tlbie_va_range_lpid(unsigned long va,
148262306a36Sopenharmony_ci					     unsigned long pid,
148362306a36Sopenharmony_ci					     unsigned long lpid,
148462306a36Sopenharmony_ci					     unsigned long ap)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
148762306a36Sopenharmony_ci		asm volatile("ptesync" : : : "memory");
148862306a36Sopenharmony_ci		__tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
149262306a36Sopenharmony_ci		asm volatile("ptesync" : : : "memory");
149362306a36Sopenharmony_ci		__tlbie_va_lpid(va, pid, lpid, ap, RIC_FLUSH_TLB);
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic inline void __tlbie_va_range_lpid(unsigned long start, unsigned long end,
149862306a36Sopenharmony_ci					 unsigned long pid, unsigned long lpid,
149962306a36Sopenharmony_ci					 unsigned long page_size,
150062306a36Sopenharmony_ci					 unsigned long psize)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	unsigned long addr;
150362306a36Sopenharmony_ci	unsigned long ap = mmu_get_ap(psize);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	for (addr = start; addr < end; addr += page_size)
150662306a36Sopenharmony_ci		__tlbie_va_lpid(addr, pid, lpid, ap, RIC_FLUSH_TLB);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	fixup_tlbie_va_range_lpid(addr - page_size, pid, lpid, ap);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic inline void _tlbie_va_range_lpid(unsigned long start, unsigned long end,
151262306a36Sopenharmony_ci					unsigned long pid, unsigned long lpid,
151362306a36Sopenharmony_ci					unsigned long page_size,
151462306a36Sopenharmony_ci					unsigned long psize, bool also_pwc)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	asm volatile("ptesync" : : : "memory");
151762306a36Sopenharmony_ci	if (also_pwc)
151862306a36Sopenharmony_ci		__tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
151962306a36Sopenharmony_ci	__tlbie_va_range_lpid(start, end, pid, lpid, page_size, psize);
152062306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync" : : : "memory");
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci/*
152462306a36Sopenharmony_ci * Performs process-scoped invalidations for a given LPID
152562306a36Sopenharmony_ci * as part of H_RPT_INVALIDATE hcall.
152662306a36Sopenharmony_ci */
152762306a36Sopenharmony_civoid do_h_rpt_invalidate_prt(unsigned long pid, unsigned long lpid,
152862306a36Sopenharmony_ci			     unsigned long type, unsigned long pg_sizes,
152962306a36Sopenharmony_ci			     unsigned long start, unsigned long end)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	unsigned long psize, nr_pages;
153262306a36Sopenharmony_ci	struct mmu_psize_def *def;
153362306a36Sopenharmony_ci	bool flush_pid;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/*
153662306a36Sopenharmony_ci	 * A H_RPTI_TYPE_ALL request implies RIC=3, hence
153762306a36Sopenharmony_ci	 * do a single IS=1 based flush.
153862306a36Sopenharmony_ci	 */
153962306a36Sopenharmony_ci	if ((type & H_RPTI_TYPE_ALL) == H_RPTI_TYPE_ALL) {
154062306a36Sopenharmony_ci		_tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL);
154162306a36Sopenharmony_ci		return;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (type & H_RPTI_TYPE_PWC)
154562306a36Sopenharmony_ci		_tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	/* Full PID flush */
154862306a36Sopenharmony_ci	if (start == 0 && end == -1)
154962306a36Sopenharmony_ci		return _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	/* Do range invalidation for all the valid page sizes */
155262306a36Sopenharmony_ci	for (psize = 0; psize < MMU_PAGE_COUNT; psize++) {
155362306a36Sopenharmony_ci		def = &mmu_psize_defs[psize];
155462306a36Sopenharmony_ci		if (!(pg_sizes & def->h_rpt_pgsize))
155562306a36Sopenharmony_ci			continue;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		nr_pages = (end - start) >> def->shift;
155862306a36Sopenharmony_ci		flush_pid = nr_pages > tlb_single_page_flush_ceiling;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci		/*
156162306a36Sopenharmony_ci		 * If the number of pages spanning the range is above
156262306a36Sopenharmony_ci		 * the ceiling, convert the request into a full PID flush.
156362306a36Sopenharmony_ci		 * And since PID flush takes out all the page sizes, there
156462306a36Sopenharmony_ci		 * is no need to consider remaining page sizes.
156562306a36Sopenharmony_ci		 */
156662306a36Sopenharmony_ci		if (flush_pid) {
156762306a36Sopenharmony_ci			_tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
156862306a36Sopenharmony_ci			return;
156962306a36Sopenharmony_ci		}
157062306a36Sopenharmony_ci		_tlbie_va_range_lpid(start, end, pid, lpid,
157162306a36Sopenharmony_ci				     (1UL << def->shift), psize, false);
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci}
157462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(do_h_rpt_invalidate_prt);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int __init create_tlb_single_page_flush_ceiling(void)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	debugfs_create_u32("tlb_single_page_flush_ceiling", 0600,
158162306a36Sopenharmony_ci			   arch_debugfs_dir, &tlb_single_page_flush_ceiling);
158262306a36Sopenharmony_ci	debugfs_create_u32("tlb_local_single_page_flush_ceiling", 0600,
158362306a36Sopenharmony_ci			   arch_debugfs_dir, &tlb_local_single_page_flush_ceiling);
158462306a36Sopenharmony_ci	return 0;
158562306a36Sopenharmony_ci}
158662306a36Sopenharmony_cilate_initcall(create_tlb_single_page_flush_ceiling);
158762306a36Sopenharmony_ci
1588