1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * linux/arch/sparc/mm/leon_m.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2004 Konrad Eisele (eiselekd@web.de, konrad@gaisler.com) Gaisler Research
6*4882a593Smuzhiyun * Copyright (C) 2009 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB
7*4882a593Smuzhiyun * Copyright (C) 2009 Konrad Eisele (konrad@gaisler.com) Aeroflex Gaisler AB
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * do srmmu probe in software
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/mm.h>
15*4882a593Smuzhiyun #include <asm/asi.h>
16*4882a593Smuzhiyun #include <asm/leon.h>
17*4882a593Smuzhiyun #include <asm/tlbflush.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include "mm_32.h"
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun int leon_flush_during_switch = 1;
22*4882a593Smuzhiyun static int srmmu_swprobe_trace;
23*4882a593Smuzhiyun
leon_get_ctable_ptr(void)24*4882a593Smuzhiyun static inline unsigned long leon_get_ctable_ptr(void)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun unsigned int retval;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun __asm__ __volatile__("lda [%1] %2, %0\n\t" :
29*4882a593Smuzhiyun "=r" (retval) :
30*4882a593Smuzhiyun "r" (SRMMU_CTXTBL_PTR),
31*4882a593Smuzhiyun "i" (ASI_LEON_MMUREGS));
32*4882a593Smuzhiyun return (retval & SRMMU_CTX_PMASK) << 4;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun
leon_swprobe(unsigned long vaddr,unsigned long * paddr)36*4882a593Smuzhiyun unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun unsigned int ctxtbl;
40*4882a593Smuzhiyun unsigned int pgd, pmd, ped;
41*4882a593Smuzhiyun unsigned int ptr;
42*4882a593Smuzhiyun unsigned int lvl, pte, paddrbase;
43*4882a593Smuzhiyun unsigned int ctx;
44*4882a593Smuzhiyun unsigned int paddr_calc;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun paddrbase = 0;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun if (srmmu_swprobe_trace)
49*4882a593Smuzhiyun printk(KERN_INFO "swprobe: trace on\n");
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun ctxtbl = leon_get_ctable_ptr();
52*4882a593Smuzhiyun if (!(ctxtbl)) {
53*4882a593Smuzhiyun if (srmmu_swprobe_trace)
54*4882a593Smuzhiyun printk(KERN_INFO "swprobe: leon_get_ctable_ptr returned 0=>0\n");
55*4882a593Smuzhiyun return 0;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun if (!_pfn_valid(PFN(ctxtbl))) {
58*4882a593Smuzhiyun if (srmmu_swprobe_trace)
59*4882a593Smuzhiyun printk(KERN_INFO
60*4882a593Smuzhiyun "swprobe: !_pfn_valid(%x)=>0\n",
61*4882a593Smuzhiyun PFN(ctxtbl));
62*4882a593Smuzhiyun return 0;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun ctx = srmmu_get_context();
66*4882a593Smuzhiyun if (srmmu_swprobe_trace)
67*4882a593Smuzhiyun printk(KERN_INFO "swprobe: --- ctx (%x) ---\n", ctx);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun pgd = LEON_BYPASS_LOAD_PA(ctxtbl + (ctx * 4));
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun if (((pgd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
72*4882a593Smuzhiyun if (srmmu_swprobe_trace)
73*4882a593Smuzhiyun printk(KERN_INFO "swprobe: pgd is entry level 3\n");
74*4882a593Smuzhiyun lvl = 3;
75*4882a593Smuzhiyun pte = pgd;
76*4882a593Smuzhiyun paddrbase = pgd & _SRMMU_PTE_PMASK_LEON;
77*4882a593Smuzhiyun goto ready;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun if (((pgd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
80*4882a593Smuzhiyun if (srmmu_swprobe_trace)
81*4882a593Smuzhiyun printk(KERN_INFO "swprobe: pgd is invalid => 0\n");
82*4882a593Smuzhiyun return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (srmmu_swprobe_trace)
86*4882a593Smuzhiyun printk(KERN_INFO "swprobe: --- pgd (%x) ---\n", pgd);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun ptr = (pgd & SRMMU_PTD_PMASK) << 4;
89*4882a593Smuzhiyun ptr += ((((vaddr) >> LEON_PGD_SH) & LEON_PGD_M) * 4);
90*4882a593Smuzhiyun if (!_pfn_valid(PFN(ptr)))
91*4882a593Smuzhiyun return 0;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun pmd = LEON_BYPASS_LOAD_PA(ptr);
94*4882a593Smuzhiyun if (((pmd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
95*4882a593Smuzhiyun if (srmmu_swprobe_trace)
96*4882a593Smuzhiyun printk(KERN_INFO "swprobe: pmd is entry level 2\n");
97*4882a593Smuzhiyun lvl = 2;
98*4882a593Smuzhiyun pte = pmd;
99*4882a593Smuzhiyun paddrbase = pmd & _SRMMU_PTE_PMASK_LEON;
100*4882a593Smuzhiyun goto ready;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun if (((pmd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
103*4882a593Smuzhiyun if (srmmu_swprobe_trace)
104*4882a593Smuzhiyun printk(KERN_INFO "swprobe: pmd is invalid => 0\n");
105*4882a593Smuzhiyun return 0;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (srmmu_swprobe_trace)
109*4882a593Smuzhiyun printk(KERN_INFO "swprobe: --- pmd (%x) ---\n", pmd);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun ptr = (pmd & SRMMU_PTD_PMASK) << 4;
112*4882a593Smuzhiyun ptr += (((vaddr >> LEON_PMD_SH) & LEON_PMD_M) * 4);
113*4882a593Smuzhiyun if (!_pfn_valid(PFN(ptr))) {
114*4882a593Smuzhiyun if (srmmu_swprobe_trace)
115*4882a593Smuzhiyun printk(KERN_INFO "swprobe: !_pfn_valid(%x)=>0\n",
116*4882a593Smuzhiyun PFN(ptr));
117*4882a593Smuzhiyun return 0;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun ped = LEON_BYPASS_LOAD_PA(ptr);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (((ped & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
123*4882a593Smuzhiyun if (srmmu_swprobe_trace)
124*4882a593Smuzhiyun printk(KERN_INFO "swprobe: ped is entry level 1\n");
125*4882a593Smuzhiyun lvl = 1;
126*4882a593Smuzhiyun pte = ped;
127*4882a593Smuzhiyun paddrbase = ped & _SRMMU_PTE_PMASK_LEON;
128*4882a593Smuzhiyun goto ready;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun if (((ped & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
131*4882a593Smuzhiyun if (srmmu_swprobe_trace)
132*4882a593Smuzhiyun printk(KERN_INFO "swprobe: ped is invalid => 0\n");
133*4882a593Smuzhiyun return 0;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (srmmu_swprobe_trace)
137*4882a593Smuzhiyun printk(KERN_INFO "swprobe: --- ped (%x) ---\n", ped);
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun ptr = (ped & SRMMU_PTD_PMASK) << 4;
140*4882a593Smuzhiyun ptr += (((vaddr >> LEON_PTE_SH) & LEON_PTE_M) * 4);
141*4882a593Smuzhiyun if (!_pfn_valid(PFN(ptr)))
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun ptr = LEON_BYPASS_LOAD_PA(ptr);
145*4882a593Smuzhiyun if (((ptr & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
146*4882a593Smuzhiyun if (srmmu_swprobe_trace)
147*4882a593Smuzhiyun printk(KERN_INFO "swprobe: ptr is entry level 0\n");
148*4882a593Smuzhiyun lvl = 0;
149*4882a593Smuzhiyun pte = ptr;
150*4882a593Smuzhiyun paddrbase = ptr & _SRMMU_PTE_PMASK_LEON;
151*4882a593Smuzhiyun goto ready;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun if (srmmu_swprobe_trace)
154*4882a593Smuzhiyun printk(KERN_INFO "swprobe: ptr is invalid => 0\n");
155*4882a593Smuzhiyun return 0;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun ready:
158*4882a593Smuzhiyun switch (lvl) {
159*4882a593Smuzhiyun case 0:
160*4882a593Smuzhiyun paddr_calc =
161*4882a593Smuzhiyun (vaddr & ~(-1 << LEON_PTE_SH)) | ((pte & ~0xff) << 4);
162*4882a593Smuzhiyun break;
163*4882a593Smuzhiyun case 1:
164*4882a593Smuzhiyun paddr_calc =
165*4882a593Smuzhiyun (vaddr & ~(-1 << LEON_PMD_SH)) | ((pte & ~0xff) << 4);
166*4882a593Smuzhiyun break;
167*4882a593Smuzhiyun case 2:
168*4882a593Smuzhiyun paddr_calc =
169*4882a593Smuzhiyun (vaddr & ~(-1 << LEON_PGD_SH)) | ((pte & ~0xff) << 4);
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun default:
172*4882a593Smuzhiyun case 3:
173*4882a593Smuzhiyun paddr_calc = vaddr;
174*4882a593Smuzhiyun break;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun if (srmmu_swprobe_trace)
177*4882a593Smuzhiyun printk(KERN_INFO "swprobe: padde %x\n", paddr_calc);
178*4882a593Smuzhiyun if (paddr)
179*4882a593Smuzhiyun *paddr = paddr_calc;
180*4882a593Smuzhiyun return pte;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
leon_flush_icache_all(void)183*4882a593Smuzhiyun void leon_flush_icache_all(void)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun __asm__ __volatile__(" flush "); /*iflush*/
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
leon_flush_dcache_all(void)188*4882a593Smuzhiyun void leon_flush_dcache_all(void)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
191*4882a593Smuzhiyun "i"(ASI_LEON_DFLUSH) : "memory");
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
leon_flush_pcache_all(struct vm_area_struct * vma,unsigned long page)194*4882a593Smuzhiyun void leon_flush_pcache_all(struct vm_area_struct *vma, unsigned long page)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun if (vma->vm_flags & VM_EXEC)
197*4882a593Smuzhiyun leon_flush_icache_all();
198*4882a593Smuzhiyun leon_flush_dcache_all();
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
leon_flush_cache_all(void)201*4882a593Smuzhiyun void leon_flush_cache_all(void)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun __asm__ __volatile__(" flush "); /*iflush*/
204*4882a593Smuzhiyun __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
205*4882a593Smuzhiyun "i"(ASI_LEON_DFLUSH) : "memory");
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
leon_flush_tlb_all(void)208*4882a593Smuzhiyun void leon_flush_tlb_all(void)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun leon_flush_cache_all();
211*4882a593Smuzhiyun __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r"(0x400),
212*4882a593Smuzhiyun "i"(ASI_LEON_MMUFLUSH) : "memory");
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* get all cache regs */
leon3_getCacheRegs(struct leon3_cacheregs * regs)216*4882a593Smuzhiyun void leon3_getCacheRegs(struct leon3_cacheregs *regs)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun unsigned long ccr, iccr, dccr;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun if (!regs)
221*4882a593Smuzhiyun return;
222*4882a593Smuzhiyun /* Get Cache regs from "Cache ASI" address 0x0, 0x8 and 0xC */
223*4882a593Smuzhiyun __asm__ __volatile__("lda [%%g0] %3, %0\n\t"
224*4882a593Smuzhiyun "mov 0x08, %%g1\n\t"
225*4882a593Smuzhiyun "lda [%%g1] %3, %1\n\t"
226*4882a593Smuzhiyun "mov 0x0c, %%g1\n\t"
227*4882a593Smuzhiyun "lda [%%g1] %3, %2\n\t"
228*4882a593Smuzhiyun : "=r"(ccr), "=r"(iccr), "=r"(dccr)
229*4882a593Smuzhiyun /* output */
230*4882a593Smuzhiyun : "i"(ASI_LEON_CACHEREGS) /* input */
231*4882a593Smuzhiyun : "g1" /* clobber list */
232*4882a593Smuzhiyun );
233*4882a593Smuzhiyun regs->ccr = ccr;
234*4882a593Smuzhiyun regs->iccr = iccr;
235*4882a593Smuzhiyun regs->dccr = dccr;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* Due to virtual cache we need to check cache configuration if
239*4882a593Smuzhiyun * it is possible to skip flushing in some cases.
240*4882a593Smuzhiyun *
241*4882a593Smuzhiyun * Leon2 and Leon3 differ in their way of telling cache information
242*4882a593Smuzhiyun *
243*4882a593Smuzhiyun */
leon_flush_needed(void)244*4882a593Smuzhiyun int __init leon_flush_needed(void)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun int flush_needed = -1;
247*4882a593Smuzhiyun unsigned int ssize, sets;
248*4882a593Smuzhiyun char *setStr[4] =
249*4882a593Smuzhiyun { "direct mapped", "2-way associative", "3-way associative",
250*4882a593Smuzhiyun "4-way associative"
251*4882a593Smuzhiyun };
252*4882a593Smuzhiyun /* leon 3 */
253*4882a593Smuzhiyun struct leon3_cacheregs cregs;
254*4882a593Smuzhiyun leon3_getCacheRegs(&cregs);
255*4882a593Smuzhiyun sets = (cregs.dccr & LEON3_XCCR_SETS_MASK) >> 24;
256*4882a593Smuzhiyun /* (ssize=>realsize) 0=>1k, 1=>2k, 2=>4k, 3=>8k ... */
257*4882a593Smuzhiyun ssize = 1 << ((cregs.dccr & LEON3_XCCR_SSIZE_MASK) >> 20);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun printk(KERN_INFO "CACHE: %s cache, set size %dk\n",
260*4882a593Smuzhiyun sets > 3 ? "unknown" : setStr[sets], ssize);
261*4882a593Smuzhiyun if ((ssize <= (PAGE_SIZE / 1024)) && (sets == 0)) {
262*4882a593Smuzhiyun /* Set Size <= Page size ==>
263*4882a593Smuzhiyun flush on every context switch not needed. */
264*4882a593Smuzhiyun flush_needed = 0;
265*4882a593Smuzhiyun printk(KERN_INFO "CACHE: not flushing on every context switch\n");
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun return flush_needed;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
leon_switch_mm(void)270*4882a593Smuzhiyun void leon_switch_mm(void)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun flush_tlb_mm((void *)0);
273*4882a593Smuzhiyun if (leon_flush_during_switch)
274*4882a593Smuzhiyun leon_flush_cache_all();
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
leon_flush_cache_mm(struct mm_struct * mm)277*4882a593Smuzhiyun static void leon_flush_cache_mm(struct mm_struct *mm)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun leon_flush_cache_all();
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
leon_flush_cache_page(struct vm_area_struct * vma,unsigned long page)282*4882a593Smuzhiyun static void leon_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun leon_flush_pcache_all(vma, page);
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
leon_flush_cache_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)287*4882a593Smuzhiyun static void leon_flush_cache_range(struct vm_area_struct *vma,
288*4882a593Smuzhiyun unsigned long start,
289*4882a593Smuzhiyun unsigned long end)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun leon_flush_cache_all();
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
leon_flush_tlb_mm(struct mm_struct * mm)294*4882a593Smuzhiyun static void leon_flush_tlb_mm(struct mm_struct *mm)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun leon_flush_tlb_all();
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
leon_flush_tlb_page(struct vm_area_struct * vma,unsigned long page)299*4882a593Smuzhiyun static void leon_flush_tlb_page(struct vm_area_struct *vma,
300*4882a593Smuzhiyun unsigned long page)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun leon_flush_tlb_all();
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
leon_flush_tlb_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)305*4882a593Smuzhiyun static void leon_flush_tlb_range(struct vm_area_struct *vma,
306*4882a593Smuzhiyun unsigned long start,
307*4882a593Smuzhiyun unsigned long end)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun leon_flush_tlb_all();
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
leon_flush_page_to_ram(unsigned long page)312*4882a593Smuzhiyun static void leon_flush_page_to_ram(unsigned long page)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun leon_flush_cache_all();
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
leon_flush_sig_insns(struct mm_struct * mm,unsigned long page)317*4882a593Smuzhiyun static void leon_flush_sig_insns(struct mm_struct *mm, unsigned long page)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun leon_flush_cache_all();
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
leon_flush_page_for_dma(unsigned long page)322*4882a593Smuzhiyun static void leon_flush_page_for_dma(unsigned long page)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun leon_flush_dcache_all();
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
poke_leonsparc(void)327*4882a593Smuzhiyun void __init poke_leonsparc(void)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun static const struct sparc32_cachetlb_ops leon_ops = {
332*4882a593Smuzhiyun .cache_all = leon_flush_cache_all,
333*4882a593Smuzhiyun .cache_mm = leon_flush_cache_mm,
334*4882a593Smuzhiyun .cache_page = leon_flush_cache_page,
335*4882a593Smuzhiyun .cache_range = leon_flush_cache_range,
336*4882a593Smuzhiyun .tlb_all = leon_flush_tlb_all,
337*4882a593Smuzhiyun .tlb_mm = leon_flush_tlb_mm,
338*4882a593Smuzhiyun .tlb_page = leon_flush_tlb_page,
339*4882a593Smuzhiyun .tlb_range = leon_flush_tlb_range,
340*4882a593Smuzhiyun .page_to_ram = leon_flush_page_to_ram,
341*4882a593Smuzhiyun .sig_insns = leon_flush_sig_insns,
342*4882a593Smuzhiyun .page_for_dma = leon_flush_page_for_dma,
343*4882a593Smuzhiyun };
344*4882a593Smuzhiyun
init_leon(void)345*4882a593Smuzhiyun void __init init_leon(void)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun srmmu_name = "LEON";
348*4882a593Smuzhiyun sparc32_cachetlb_ops = &leon_ops;
349*4882a593Smuzhiyun poke_srmmu = poke_leonsparc;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun leon_flush_during_switch = leon_flush_needed();
352*4882a593Smuzhiyun }
353