xref: /OK3568_Linux_fs/kernel/arch/powerpc/mm/nohash/book3e_hugetlbpage.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * PPC Huge TLB Page Support for Book3E MMU
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2009 David Gibson, IBM Corporation.
6*4882a593Smuzhiyun  * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun #include <linux/mm.h>
10*4882a593Smuzhiyun #include <linux/hugetlb.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <asm/mmu.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #ifdef CONFIG_PPC64
15*4882a593Smuzhiyun #include <asm/paca.h>
16*4882a593Smuzhiyun 
tlb1_next(void)17*4882a593Smuzhiyun static inline int tlb1_next(void)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun 	struct paca_struct *paca = get_paca();
20*4882a593Smuzhiyun 	struct tlb_core_data *tcd;
21*4882a593Smuzhiyun 	int this, next;
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun 	tcd = paca->tcd_ptr;
24*4882a593Smuzhiyun 	this = tcd->esel_next;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	next = this + 1;
27*4882a593Smuzhiyun 	if (next >= tcd->esel_max)
28*4882a593Smuzhiyun 		next = tcd->esel_first;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	tcd->esel_next = next;
31*4882a593Smuzhiyun 	return this;
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun 
book3e_tlb_lock(void)34*4882a593Smuzhiyun static inline void book3e_tlb_lock(void)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	struct paca_struct *paca = get_paca();
37*4882a593Smuzhiyun 	unsigned long tmp;
38*4882a593Smuzhiyun 	int token = smp_processor_id() + 1;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	/*
41*4882a593Smuzhiyun 	 * Besides being unnecessary in the absence of SMT, this
42*4882a593Smuzhiyun 	 * check prevents trying to do lbarx/stbcx. on e5500 which
43*4882a593Smuzhiyun 	 * doesn't implement either feature.
44*4882a593Smuzhiyun 	 */
45*4882a593Smuzhiyun 	if (!cpu_has_feature(CPU_FTR_SMT))
46*4882a593Smuzhiyun 		return;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	asm volatile("1: lbarx %0, 0, %1;"
49*4882a593Smuzhiyun 		     "cmpwi %0, 0;"
50*4882a593Smuzhiyun 		     "bne 2f;"
51*4882a593Smuzhiyun 		     "stbcx. %2, 0, %1;"
52*4882a593Smuzhiyun 		     "bne 1b;"
53*4882a593Smuzhiyun 		     "b 3f;"
54*4882a593Smuzhiyun 		     "2: lbzx %0, 0, %1;"
55*4882a593Smuzhiyun 		     "cmpwi %0, 0;"
56*4882a593Smuzhiyun 		     "bne 2b;"
57*4882a593Smuzhiyun 		     "b 1b;"
58*4882a593Smuzhiyun 		     "3:"
59*4882a593Smuzhiyun 		     : "=&r" (tmp)
60*4882a593Smuzhiyun 		     : "r" (&paca->tcd_ptr->lock), "r" (token)
61*4882a593Smuzhiyun 		     : "memory");
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun 
book3e_tlb_unlock(void)64*4882a593Smuzhiyun static inline void book3e_tlb_unlock(void)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	struct paca_struct *paca = get_paca();
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (!cpu_has_feature(CPU_FTR_SMT))
69*4882a593Smuzhiyun 		return;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	isync();
72*4882a593Smuzhiyun 	paca->tcd_ptr->lock = 0;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun #else
tlb1_next(void)75*4882a593Smuzhiyun static inline int tlb1_next(void)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	int index, ncams;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	index = this_cpu_read(next_tlbcam_idx);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	/* Just round-robin the entries and wrap when we hit the end */
84*4882a593Smuzhiyun 	if (unlikely(index == ncams - 1))
85*4882a593Smuzhiyun 		__this_cpu_write(next_tlbcam_idx, tlbcam_index);
86*4882a593Smuzhiyun 	else
87*4882a593Smuzhiyun 		__this_cpu_inc(next_tlbcam_idx);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return index;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
book3e_tlb_lock(void)92*4882a593Smuzhiyun static inline void book3e_tlb_lock(void)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun 
book3e_tlb_unlock(void)96*4882a593Smuzhiyun static inline void book3e_tlb_unlock(void)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun #endif
100*4882a593Smuzhiyun 
book3e_tlb_exists(unsigned long ea,unsigned long pid)101*4882a593Smuzhiyun static inline int book3e_tlb_exists(unsigned long ea, unsigned long pid)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	int found = 0;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	mtspr(SPRN_MAS6, pid << 16);
106*4882a593Smuzhiyun 	if (mmu_has_feature(MMU_FTR_USE_TLBRSRV)) {
107*4882a593Smuzhiyun 		asm volatile(
108*4882a593Smuzhiyun 			"li	%0,0\n"
109*4882a593Smuzhiyun 			"tlbsx.	0,%1\n"
110*4882a593Smuzhiyun 			"bne	1f\n"
111*4882a593Smuzhiyun 			"li	%0,1\n"
112*4882a593Smuzhiyun 			"1:\n"
113*4882a593Smuzhiyun 			: "=&r"(found) : "r"(ea));
114*4882a593Smuzhiyun 	} else {
115*4882a593Smuzhiyun 		asm volatile(
116*4882a593Smuzhiyun 			"tlbsx	0,%1\n"
117*4882a593Smuzhiyun 			"mfspr	%0,0x271\n"
118*4882a593Smuzhiyun 			"srwi	%0,%0,31\n"
119*4882a593Smuzhiyun 			: "=&r"(found) : "r"(ea));
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	return found;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun static void
book3e_hugetlb_preload(struct vm_area_struct * vma,unsigned long ea,pte_t pte)126*4882a593Smuzhiyun book3e_hugetlb_preload(struct vm_area_struct *vma, unsigned long ea, pte_t pte)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	unsigned long mas1, mas2;
129*4882a593Smuzhiyun 	u64 mas7_3;
130*4882a593Smuzhiyun 	unsigned long psize, tsize, shift;
131*4882a593Smuzhiyun 	unsigned long flags;
132*4882a593Smuzhiyun 	struct mm_struct *mm;
133*4882a593Smuzhiyun 	int index;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	if (unlikely(is_kernel_addr(ea)))
136*4882a593Smuzhiyun 		return;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	mm = vma->vm_mm;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	psize = vma_mmu_pagesize(vma);
141*4882a593Smuzhiyun 	shift = __ilog2(psize);
142*4882a593Smuzhiyun 	tsize = shift - 10;
143*4882a593Smuzhiyun 	/*
144*4882a593Smuzhiyun 	 * We can't be interrupted while we're setting up the MAS
145*4882a593Smuzhiyun 	 * regusters or after we've confirmed that no tlb exists.
146*4882a593Smuzhiyun 	 */
147*4882a593Smuzhiyun 	local_irq_save(flags);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	book3e_tlb_lock();
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	if (unlikely(book3e_tlb_exists(ea, mm->context.id))) {
152*4882a593Smuzhiyun 		book3e_tlb_unlock();
153*4882a593Smuzhiyun 		local_irq_restore(flags);
154*4882a593Smuzhiyun 		return;
155*4882a593Smuzhiyun 	}
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	/* We have to use the CAM(TLB1) on FSL parts for hugepages */
158*4882a593Smuzhiyun 	index = tlb1_next();
159*4882a593Smuzhiyun 	mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1));
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize);
162*4882a593Smuzhiyun 	mas2 = ea & ~((1UL << shift) - 1);
163*4882a593Smuzhiyun 	mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK;
164*4882a593Smuzhiyun 	mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT;
165*4882a593Smuzhiyun 	mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK;
166*4882a593Smuzhiyun 	if (!pte_dirty(pte))
167*4882a593Smuzhiyun 		mas7_3 &= ~(MAS3_SW|MAS3_UW);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	mtspr(SPRN_MAS1, mas1);
170*4882a593Smuzhiyun 	mtspr(SPRN_MAS2, mas2);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	if (mmu_has_feature(MMU_FTR_USE_PAIRED_MAS)) {
173*4882a593Smuzhiyun 		mtspr(SPRN_MAS7_MAS3, mas7_3);
174*4882a593Smuzhiyun 	} else {
175*4882a593Smuzhiyun 		if (mmu_has_feature(MMU_FTR_BIG_PHYS))
176*4882a593Smuzhiyun 			mtspr(SPRN_MAS7, upper_32_bits(mas7_3));
177*4882a593Smuzhiyun 		mtspr(SPRN_MAS3, lower_32_bits(mas7_3));
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	asm volatile ("tlbwe");
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	book3e_tlb_unlock();
183*4882a593Smuzhiyun 	local_irq_restore(flags);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun /*
187*4882a593Smuzhiyun  * This is called at the end of handling a user page fault, when the
188*4882a593Smuzhiyun  * fault has been handled by updating a PTE in the linux page tables.
189*4882a593Smuzhiyun  *
190*4882a593Smuzhiyun  * This must always be called with the pte lock held.
191*4882a593Smuzhiyun  */
update_mmu_cache(struct vm_area_struct * vma,unsigned long address,pte_t * ptep)192*4882a593Smuzhiyun void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	if (is_vm_hugetlb_page(vma))
195*4882a593Smuzhiyun 		book3e_hugetlb_preload(vma, address, *ptep);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
flush_hugetlb_page(struct vm_area_struct * vma,unsigned long vmaddr)198*4882a593Smuzhiyun void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	struct hstate *hstate = hstate_file(vma->vm_file);
201*4882a593Smuzhiyun 	unsigned long tsize = huge_page_shift(hstate) - 10;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	__flush_tlb_page(vma->vm_mm, vmaddr, tsize, 0);
204*4882a593Smuzhiyun }
205