1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25213a780SKonrad Eisele /*
35213a780SKonrad Eisele * linux/arch/sparc/mm/leon_m.c
45213a780SKonrad Eisele *
55213a780SKonrad Eisele * Copyright (C) 2004 Konrad Eisele (eiselekd@web.de, konrad@gaisler.com) Gaisler Research
65213a780SKonrad Eisele * Copyright (C) 2009 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB
75213a780SKonrad Eisele * Copyright (C) 2009 Konrad Eisele (konrad@gaisler.com) Aeroflex Gaisler AB
85213a780SKonrad Eisele *
95213a780SKonrad Eisele * do srmmu probe in software
105213a780SKonrad Eisele *
115213a780SKonrad Eisele */
125213a780SKonrad Eisele
135213a780SKonrad Eisele #include <linux/kernel.h>
145213a780SKonrad Eisele #include <linux/mm.h>
155213a780SKonrad Eisele #include <asm/asi.h>
165213a780SKonrad Eisele #include <asm/leon.h>
175213a780SKonrad Eisele #include <asm/tlbflush.h>
185213a780SKonrad Eisele
19ddb7417eSSam Ravnborg #include "mm_32.h"
20accf032cSSam Ravnborg
215213a780SKonrad Eisele int leon_flush_during_switch = 1;
22a2b0aa94SSam Ravnborg static int srmmu_swprobe_trace;
235213a780SKonrad Eisele
leon_get_ctable_ptr(void)243d5f7d37SSam Ravnborg static inline unsigned long leon_get_ctable_ptr(void)
253d5f7d37SSam Ravnborg {
263d5f7d37SSam Ravnborg unsigned int retval;
273d5f7d37SSam Ravnborg
283d5f7d37SSam Ravnborg __asm__ __volatile__("lda [%1] %2, %0\n\t" :
293d5f7d37SSam Ravnborg "=r" (retval) :
303d5f7d37SSam Ravnborg "r" (SRMMU_CTXTBL_PTR),
313d5f7d37SSam Ravnborg "i" (ASI_LEON_MMUREGS));
323d5f7d37SSam Ravnborg return (retval & SRMMU_CTX_PMASK) << 4;
333d5f7d37SSam Ravnborg }
343d5f7d37SSam Ravnborg
353d5f7d37SSam Ravnborg
leon_swprobe(unsigned long vaddr,unsigned long * paddr)36805918f8SSam Ravnborg unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr)
375213a780SKonrad Eisele {
385213a780SKonrad Eisele
395213a780SKonrad Eisele unsigned int ctxtbl;
405213a780SKonrad Eisele unsigned int pgd, pmd, ped;
415213a780SKonrad Eisele unsigned int ptr;
425213a780SKonrad Eisele unsigned int lvl, pte, paddrbase;
435213a780SKonrad Eisele unsigned int ctx;
445213a780SKonrad Eisele unsigned int paddr_calc;
455213a780SKonrad Eisele
465213a780SKonrad Eisele paddrbase = 0;
475213a780SKonrad Eisele
485213a780SKonrad Eisele if (srmmu_swprobe_trace)
495213a780SKonrad Eisele printk(KERN_INFO "swprobe: trace on\n");
505213a780SKonrad Eisele
513d5f7d37SSam Ravnborg ctxtbl = leon_get_ctable_ptr();
525213a780SKonrad Eisele if (!(ctxtbl)) {
535213a780SKonrad Eisele if (srmmu_swprobe_trace)
543d5f7d37SSam Ravnborg printk(KERN_INFO "swprobe: leon_get_ctable_ptr returned 0=>0\n");
555213a780SKonrad Eisele return 0;
565213a780SKonrad Eisele }
575213a780SKonrad Eisele if (!_pfn_valid(PFN(ctxtbl))) {
585213a780SKonrad Eisele if (srmmu_swprobe_trace)
595213a780SKonrad Eisele printk(KERN_INFO
605213a780SKonrad Eisele "swprobe: !_pfn_valid(%x)=>0\n",
615213a780SKonrad Eisele PFN(ctxtbl));
625213a780SKonrad Eisele return 0;
635213a780SKonrad Eisele }
645213a780SKonrad Eisele
655213a780SKonrad Eisele ctx = srmmu_get_context();
665213a780SKonrad Eisele if (srmmu_swprobe_trace)
675213a780SKonrad Eisele printk(KERN_INFO "swprobe: --- ctx (%x) ---\n", ctx);
685213a780SKonrad Eisele
695213a780SKonrad Eisele pgd = LEON_BYPASS_LOAD_PA(ctxtbl + (ctx * 4));
705213a780SKonrad Eisele
715213a780SKonrad Eisele if (((pgd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
725213a780SKonrad Eisele if (srmmu_swprobe_trace)
735213a780SKonrad Eisele printk(KERN_INFO "swprobe: pgd is entry level 3\n");
745213a780SKonrad Eisele lvl = 3;
755213a780SKonrad Eisele pte = pgd;
765213a780SKonrad Eisele paddrbase = pgd & _SRMMU_PTE_PMASK_LEON;
775213a780SKonrad Eisele goto ready;
785213a780SKonrad Eisele }
795213a780SKonrad Eisele if (((pgd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
805213a780SKonrad Eisele if (srmmu_swprobe_trace)
815213a780SKonrad Eisele printk(KERN_INFO "swprobe: pgd is invalid => 0\n");
825213a780SKonrad Eisele return 0;
835213a780SKonrad Eisele }
845213a780SKonrad Eisele
855213a780SKonrad Eisele if (srmmu_swprobe_trace)
865213a780SKonrad Eisele printk(KERN_INFO "swprobe: --- pgd (%x) ---\n", pgd);
875213a780SKonrad Eisele
885213a780SKonrad Eisele ptr = (pgd & SRMMU_PTD_PMASK) << 4;
895213a780SKonrad Eisele ptr += ((((vaddr) >> LEON_PGD_SH) & LEON_PGD_M) * 4);
905213a780SKonrad Eisele if (!_pfn_valid(PFN(ptr)))
915213a780SKonrad Eisele return 0;
925213a780SKonrad Eisele
935213a780SKonrad Eisele pmd = LEON_BYPASS_LOAD_PA(ptr);
945213a780SKonrad Eisele if (((pmd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
955213a780SKonrad Eisele if (srmmu_swprobe_trace)
965213a780SKonrad Eisele printk(KERN_INFO "swprobe: pmd is entry level 2\n");
975213a780SKonrad Eisele lvl = 2;
985213a780SKonrad Eisele pte = pmd;
995213a780SKonrad Eisele paddrbase = pmd & _SRMMU_PTE_PMASK_LEON;
1005213a780SKonrad Eisele goto ready;
1015213a780SKonrad Eisele }
1025213a780SKonrad Eisele if (((pmd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
1035213a780SKonrad Eisele if (srmmu_swprobe_trace)
1045213a780SKonrad Eisele printk(KERN_INFO "swprobe: pmd is invalid => 0\n");
1055213a780SKonrad Eisele return 0;
1065213a780SKonrad Eisele }
1075213a780SKonrad Eisele
1085213a780SKonrad Eisele if (srmmu_swprobe_trace)
1095213a780SKonrad Eisele printk(KERN_INFO "swprobe: --- pmd (%x) ---\n", pmd);
1105213a780SKonrad Eisele
1115213a780SKonrad Eisele ptr = (pmd & SRMMU_PTD_PMASK) << 4;
1125213a780SKonrad Eisele ptr += (((vaddr >> LEON_PMD_SH) & LEON_PMD_M) * 4);
1135213a780SKonrad Eisele if (!_pfn_valid(PFN(ptr))) {
1145213a780SKonrad Eisele if (srmmu_swprobe_trace)
1155213a780SKonrad Eisele printk(KERN_INFO "swprobe: !_pfn_valid(%x)=>0\n",
1165213a780SKonrad Eisele PFN(ptr));
1175213a780SKonrad Eisele return 0;
1185213a780SKonrad Eisele }
1195213a780SKonrad Eisele
1205213a780SKonrad Eisele ped = LEON_BYPASS_LOAD_PA(ptr);
1215213a780SKonrad Eisele
1225213a780SKonrad Eisele if (((ped & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
1235213a780SKonrad Eisele if (srmmu_swprobe_trace)
1245213a780SKonrad Eisele printk(KERN_INFO "swprobe: ped is entry level 1\n");
1255213a780SKonrad Eisele lvl = 1;
1265213a780SKonrad Eisele pte = ped;
1275213a780SKonrad Eisele paddrbase = ped & _SRMMU_PTE_PMASK_LEON;
1285213a780SKonrad Eisele goto ready;
1295213a780SKonrad Eisele }
1305213a780SKonrad Eisele if (((ped & SRMMU_ET_MASK) != SRMMU_ET_PTD)) {
1315213a780SKonrad Eisele if (srmmu_swprobe_trace)
1325213a780SKonrad Eisele printk(KERN_INFO "swprobe: ped is invalid => 0\n");
1335213a780SKonrad Eisele return 0;
1345213a780SKonrad Eisele }
1355213a780SKonrad Eisele
1365213a780SKonrad Eisele if (srmmu_swprobe_trace)
1375213a780SKonrad Eisele printk(KERN_INFO "swprobe: --- ped (%x) ---\n", ped);
1385213a780SKonrad Eisele
1395213a780SKonrad Eisele ptr = (ped & SRMMU_PTD_PMASK) << 4;
1405213a780SKonrad Eisele ptr += (((vaddr >> LEON_PTE_SH) & LEON_PTE_M) * 4);
1415213a780SKonrad Eisele if (!_pfn_valid(PFN(ptr)))
1425213a780SKonrad Eisele return 0;
1435213a780SKonrad Eisele
1445213a780SKonrad Eisele ptr = LEON_BYPASS_LOAD_PA(ptr);
1455213a780SKonrad Eisele if (((ptr & SRMMU_ET_MASK) == SRMMU_ET_PTE)) {
1465213a780SKonrad Eisele if (srmmu_swprobe_trace)
1475213a780SKonrad Eisele printk(KERN_INFO "swprobe: ptr is entry level 0\n");
1485213a780SKonrad Eisele lvl = 0;
1495213a780SKonrad Eisele pte = ptr;
1505213a780SKonrad Eisele paddrbase = ptr & _SRMMU_PTE_PMASK_LEON;
1515213a780SKonrad Eisele goto ready;
1525213a780SKonrad Eisele }
1535213a780SKonrad Eisele if (srmmu_swprobe_trace)
1545213a780SKonrad Eisele printk(KERN_INFO "swprobe: ptr is invalid => 0\n");
1555213a780SKonrad Eisele return 0;
1565213a780SKonrad Eisele
1575213a780SKonrad Eisele ready:
1585213a780SKonrad Eisele switch (lvl) {
1595213a780SKonrad Eisele case 0:
1605213a780SKonrad Eisele paddr_calc =
1615213a780SKonrad Eisele (vaddr & ~(-1 << LEON_PTE_SH)) | ((pte & ~0xff) << 4);
1625213a780SKonrad Eisele break;
1635213a780SKonrad Eisele case 1:
1645213a780SKonrad Eisele paddr_calc =
1655213a780SKonrad Eisele (vaddr & ~(-1 << LEON_PMD_SH)) | ((pte & ~0xff) << 4);
1665213a780SKonrad Eisele break;
1675213a780SKonrad Eisele case 2:
1685213a780SKonrad Eisele paddr_calc =
1695213a780SKonrad Eisele (vaddr & ~(-1 << LEON_PGD_SH)) | ((pte & ~0xff) << 4);
1705213a780SKonrad Eisele break;
1715213a780SKonrad Eisele default:
1725213a780SKonrad Eisele case 3:
1735213a780SKonrad Eisele paddr_calc = vaddr;
1745213a780SKonrad Eisele break;
1755213a780SKonrad Eisele }
1765213a780SKonrad Eisele if (srmmu_swprobe_trace)
1775213a780SKonrad Eisele printk(KERN_INFO "swprobe: padde %x\n", paddr_calc);
1785213a780SKonrad Eisele if (paddr)
1795213a780SKonrad Eisele *paddr = paddr_calc;
180f22ed71cSDaniel Hellstrom return pte;
1815213a780SKonrad Eisele }
1825213a780SKonrad Eisele
leon_flush_icache_all(void)1835213a780SKonrad Eisele void leon_flush_icache_all(void)
1845213a780SKonrad Eisele {
1855213a780SKonrad Eisele __asm__ __volatile__(" flush "); /*iflush*/
1865213a780SKonrad Eisele }
1875213a780SKonrad Eisele
leon_flush_dcache_all(void)1885213a780SKonrad Eisele void leon_flush_dcache_all(void)
1895213a780SKonrad Eisele {
1905213a780SKonrad Eisele __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
1915213a780SKonrad Eisele "i"(ASI_LEON_DFLUSH) : "memory");
1925213a780SKonrad Eisele }
1935213a780SKonrad Eisele
leon_flush_pcache_all(struct vm_area_struct * vma,unsigned long page)1945213a780SKonrad Eisele void leon_flush_pcache_all(struct vm_area_struct *vma, unsigned long page)
1955213a780SKonrad Eisele {
1965213a780SKonrad Eisele if (vma->vm_flags & VM_EXEC)
1975213a780SKonrad Eisele leon_flush_icache_all();
1985213a780SKonrad Eisele leon_flush_dcache_all();
1995213a780SKonrad Eisele }
2005213a780SKonrad Eisele
leon_flush_cache_all(void)2015213a780SKonrad Eisele void leon_flush_cache_all(void)
2025213a780SKonrad Eisele {
2035213a780SKonrad Eisele __asm__ __volatile__(" flush "); /*iflush*/
2045213a780SKonrad Eisele __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
2055213a780SKonrad Eisele "i"(ASI_LEON_DFLUSH) : "memory");
2065213a780SKonrad Eisele }
2075213a780SKonrad Eisele
leon_flush_tlb_all(void)2085213a780SKonrad Eisele void leon_flush_tlb_all(void)
2095213a780SKonrad Eisele {
2105213a780SKonrad Eisele leon_flush_cache_all();
2115213a780SKonrad Eisele __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r"(0x400),
2125213a780SKonrad Eisele "i"(ASI_LEON_MMUFLUSH) : "memory");
2135213a780SKonrad Eisele }
2145213a780SKonrad Eisele
2155213a780SKonrad Eisele /* get all cache regs */
leon3_getCacheRegs(struct leon3_cacheregs * regs)2165213a780SKonrad Eisele void leon3_getCacheRegs(struct leon3_cacheregs *regs)
2175213a780SKonrad Eisele {
2185213a780SKonrad Eisele unsigned long ccr, iccr, dccr;
2195213a780SKonrad Eisele
2205213a780SKonrad Eisele if (!regs)
2215213a780SKonrad Eisele return;
2225213a780SKonrad Eisele /* Get Cache regs from "Cache ASI" address 0x0, 0x8 and 0xC */
2235213a780SKonrad Eisele __asm__ __volatile__("lda [%%g0] %3, %0\n\t"
2245213a780SKonrad Eisele "mov 0x08, %%g1\n\t"
2255213a780SKonrad Eisele "lda [%%g1] %3, %1\n\t"
2265213a780SKonrad Eisele "mov 0x0c, %%g1\n\t"
2275213a780SKonrad Eisele "lda [%%g1] %3, %2\n\t"
2285213a780SKonrad Eisele : "=r"(ccr), "=r"(iccr), "=r"(dccr)
2295213a780SKonrad Eisele /* output */
2305213a780SKonrad Eisele : "i"(ASI_LEON_CACHEREGS) /* input */
2315213a780SKonrad Eisele : "g1" /* clobber list */
2325213a780SKonrad Eisele );
2335213a780SKonrad Eisele regs->ccr = ccr;
2345213a780SKonrad Eisele regs->iccr = iccr;
2355213a780SKonrad Eisele regs->dccr = dccr;
2365213a780SKonrad Eisele }
2375213a780SKonrad Eisele
2385213a780SKonrad Eisele /* Due to virtual cache we need to check cache configuration if
2395213a780SKonrad Eisele * it is possible to skip flushing in some cases.
2405213a780SKonrad Eisele *
2415213a780SKonrad Eisele * Leon2 and Leon3 differ in their way of telling cache information
2425213a780SKonrad Eisele *
2435213a780SKonrad Eisele */
leon_flush_needed(void)2446d999da4SMatthias Rosenfelder int __init leon_flush_needed(void)
2455213a780SKonrad Eisele {
2465213a780SKonrad Eisele int flush_needed = -1;
2475213a780SKonrad Eisele unsigned int ssize, sets;
2485213a780SKonrad Eisele char *setStr[4] =
2495213a780SKonrad Eisele { "direct mapped", "2-way associative", "3-way associative",
2505213a780SKonrad Eisele "4-way associative"
2515213a780SKonrad Eisele };
2525213a780SKonrad Eisele /* leon 3 */
2535213a780SKonrad Eisele struct leon3_cacheregs cregs;
2545213a780SKonrad Eisele leon3_getCacheRegs(&cregs);
2555213a780SKonrad Eisele sets = (cregs.dccr & LEON3_XCCR_SETS_MASK) >> 24;
2565213a780SKonrad Eisele /* (ssize=>realsize) 0=>1k, 1=>2k, 2=>4k, 3=>8k ... */
2575213a780SKonrad Eisele ssize = 1 << ((cregs.dccr & LEON3_XCCR_SSIZE_MASK) >> 20);
2585213a780SKonrad Eisele
2595213a780SKonrad Eisele printk(KERN_INFO "CACHE: %s cache, set size %dk\n",
2605213a780SKonrad Eisele sets > 3 ? "unknown" : setStr[sets], ssize);
2615213a780SKonrad Eisele if ((ssize <= (PAGE_SIZE / 1024)) && (sets == 0)) {
2625213a780SKonrad Eisele /* Set Size <= Page size ==>
2635213a780SKonrad Eisele flush on every context switch not needed. */
2645213a780SKonrad Eisele flush_needed = 0;
2655213a780SKonrad Eisele printk(KERN_INFO "CACHE: not flushing on every context switch\n");
2665213a780SKonrad Eisele }
2675213a780SKonrad Eisele return flush_needed;
2685213a780SKonrad Eisele }
2695213a780SKonrad Eisele
leon_switch_mm(void)2705213a780SKonrad Eisele void leon_switch_mm(void)
2715213a780SKonrad Eisele {
2725213a780SKonrad Eisele flush_tlb_mm((void *)0);
2735213a780SKonrad Eisele if (leon_flush_during_switch)
2745213a780SKonrad Eisele leon_flush_cache_all();
2755213a780SKonrad Eisele }
276accf032cSSam Ravnborg
leon_flush_cache_mm(struct mm_struct * mm)277accf032cSSam Ravnborg static void leon_flush_cache_mm(struct mm_struct *mm)
278accf032cSSam Ravnborg {
279accf032cSSam Ravnborg leon_flush_cache_all();
280accf032cSSam Ravnborg }
281accf032cSSam Ravnborg
leon_flush_cache_page(struct vm_area_struct * vma,unsigned long page)282accf032cSSam Ravnborg static void leon_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
283accf032cSSam Ravnborg {
284accf032cSSam Ravnborg leon_flush_pcache_all(vma, page);
285accf032cSSam Ravnborg }
286accf032cSSam Ravnborg
leon_flush_cache_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)287accf032cSSam Ravnborg static void leon_flush_cache_range(struct vm_area_struct *vma,
288accf032cSSam Ravnborg unsigned long start,
289accf032cSSam Ravnborg unsigned long end)
290accf032cSSam Ravnborg {
291accf032cSSam Ravnborg leon_flush_cache_all();
292accf032cSSam Ravnborg }
293accf032cSSam Ravnborg
leon_flush_tlb_mm(struct mm_struct * mm)294accf032cSSam Ravnborg static void leon_flush_tlb_mm(struct mm_struct *mm)
295accf032cSSam Ravnborg {
296accf032cSSam Ravnborg leon_flush_tlb_all();
297accf032cSSam Ravnborg }
298accf032cSSam Ravnborg
leon_flush_tlb_page(struct vm_area_struct * vma,unsigned long page)299accf032cSSam Ravnborg static void leon_flush_tlb_page(struct vm_area_struct *vma,
300accf032cSSam Ravnborg unsigned long page)
301accf032cSSam Ravnborg {
302accf032cSSam Ravnborg leon_flush_tlb_all();
303accf032cSSam Ravnborg }
304accf032cSSam Ravnborg
leon_flush_tlb_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)305accf032cSSam Ravnborg static void leon_flush_tlb_range(struct vm_area_struct *vma,
306accf032cSSam Ravnborg unsigned long start,
307accf032cSSam Ravnborg unsigned long end)
308accf032cSSam Ravnborg {
309accf032cSSam Ravnborg leon_flush_tlb_all();
310accf032cSSam Ravnborg }
311accf032cSSam Ravnborg
leon_flush_page_to_ram(unsigned long page)312accf032cSSam Ravnborg static void leon_flush_page_to_ram(unsigned long page)
313accf032cSSam Ravnborg {
314accf032cSSam Ravnborg leon_flush_cache_all();
315accf032cSSam Ravnborg }
316accf032cSSam Ravnborg
leon_flush_sig_insns(struct mm_struct * mm,unsigned long page)317accf032cSSam Ravnborg static void leon_flush_sig_insns(struct mm_struct *mm, unsigned long page)
318accf032cSSam Ravnborg {
319accf032cSSam Ravnborg leon_flush_cache_all();
320accf032cSSam Ravnborg }
321accf032cSSam Ravnborg
leon_flush_page_for_dma(unsigned long page)322accf032cSSam Ravnborg static void leon_flush_page_for_dma(unsigned long page)
323accf032cSSam Ravnborg {
324accf032cSSam Ravnborg leon_flush_dcache_all();
325accf032cSSam Ravnborg }
326accf032cSSam Ravnborg
poke_leonsparc(void)327accf032cSSam Ravnborg void __init poke_leonsparc(void)
328accf032cSSam Ravnborg {
329accf032cSSam Ravnborg }
330accf032cSSam Ravnborg
331accf032cSSam Ravnborg static const struct sparc32_cachetlb_ops leon_ops = {
332accf032cSSam Ravnborg .cache_all = leon_flush_cache_all,
333accf032cSSam Ravnborg .cache_mm = leon_flush_cache_mm,
334accf032cSSam Ravnborg .cache_page = leon_flush_cache_page,
335accf032cSSam Ravnborg .cache_range = leon_flush_cache_range,
336accf032cSSam Ravnborg .tlb_all = leon_flush_tlb_all,
337accf032cSSam Ravnborg .tlb_mm = leon_flush_tlb_mm,
338accf032cSSam Ravnborg .tlb_page = leon_flush_tlb_page,
339accf032cSSam Ravnborg .tlb_range = leon_flush_tlb_range,
340accf032cSSam Ravnborg .page_to_ram = leon_flush_page_to_ram,
341accf032cSSam Ravnborg .sig_insns = leon_flush_sig_insns,
342accf032cSSam Ravnborg .page_for_dma = leon_flush_page_for_dma,
343accf032cSSam Ravnborg };
344accf032cSSam Ravnborg
init_leon(void)345accf032cSSam Ravnborg void __init init_leon(void)
346accf032cSSam Ravnborg {
347accf032cSSam Ravnborg srmmu_name = "LEON";
348accf032cSSam Ravnborg sparc32_cachetlb_ops = &leon_ops;
349accf032cSSam Ravnborg poke_srmmu = poke_leonsparc;
350accf032cSSam Ravnborg
351accf032cSSam Ravnborg leon_flush_during_switch = leon_flush_needed();
352accf032cSSam Ravnborg }
353