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