diff options
Diffstat (limited to 'arch/sparc/mm')
-rw-r--r-- | arch/sparc/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/sparc/mm/init_32.c | 4 | ||||
-rw-r--r-- | arch/sparc/mm/leon_mm.c | 260 | ||||
-rw-r--r-- | arch/sparc/mm/loadmmu.c | 1 | ||||
-rw-r--r-- | arch/sparc/mm/srmmu.c | 53 |
5 files changed, 318 insertions, 1 deletions
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index 681abe0a4594..79836a7dd00c 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile | |||
@@ -11,6 +11,7 @@ obj-$(CONFIG_SPARC32) += loadmmu.o | |||
11 | obj-y += generic_$(BITS).o | 11 | obj-y += generic_$(BITS).o |
12 | obj-$(CONFIG_SPARC32) += extable.o btfixup.o srmmu.o iommu.o io-unit.o | 12 | obj-$(CONFIG_SPARC32) += extable.o btfixup.o srmmu.o iommu.o io-unit.o |
13 | obj-$(CONFIG_SPARC32) += hypersparc.o viking.o tsunami.o swift.o | 13 | obj-$(CONFIG_SPARC32) += hypersparc.o viking.o tsunami.o swift.o |
14 | obj-$(CONFIG_SPARC_LEON)+= leon_mm.o | ||
14 | 15 | ||
15 | # Only used by sparc64 | 16 | # Only used by sparc64 |
16 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | 17 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o |
diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c index 26bb3919ff1f..54114ad0bdee 100644 --- a/arch/sparc/mm/init_32.c +++ b/arch/sparc/mm/init_32.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <asm/pgalloc.h> /* bug in asm-generic/tlb.h: check_pgt_cache */ | 34 | #include <asm/pgalloc.h> /* bug in asm-generic/tlb.h: check_pgt_cache */ |
35 | #include <asm/tlb.h> | 35 | #include <asm/tlb.h> |
36 | #include <asm/prom.h> | 36 | #include <asm/prom.h> |
37 | #include <asm/leon.h> | ||
37 | 38 | ||
38 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | 39 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); |
39 | 40 | ||
@@ -326,6 +327,9 @@ void __init paging_init(void) | |||
326 | sparc_unmapped_base = 0xe0000000; | 327 | sparc_unmapped_base = 0xe0000000; |
327 | BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000); | 328 | BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000); |
328 | break; | 329 | break; |
330 | case sparc_leon: | ||
331 | leon_init(); | ||
332 | /* fall through */ | ||
329 | case sun4m: | 333 | case sun4m: |
330 | case sun4d: | 334 | case sun4d: |
331 | srmmu_paging_init(); | 335 | srmmu_paging_init(); |
diff --git a/arch/sparc/mm/leon_mm.c b/arch/sparc/mm/leon_mm.c new file mode 100644 index 000000000000..c0e01297e64e --- /dev/null +++ b/arch/sparc/mm/leon_mm.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * linux/arch/sparc/mm/leon_m.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Konrad Eisele (eiselekd@web.de, konrad@gaisler.com) Gaisler Research | ||
5 | * Copyright (C) 2009 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB | ||
6 | * Copyright (C) 2009 Konrad Eisele (konrad@gaisler.com) Aeroflex Gaisler AB | ||
7 | * | ||
8 | * do srmmu probe in software | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <asm/asi.h> | ||
15 | #include <asm/leon.h> | ||
16 | #include <asm/tlbflush.h> | ||
17 | |||
18 | int leon_flush_during_switch = 1; | ||
19 | int srmmu_swprobe_trace; | ||
20 | |||
21 | unsigned long srmmu_swprobe(unsigned long vaddr, unsigned long *paddr) | ||
22 | { | ||
23 | |||
24 | unsigned int ctxtbl; | ||
25 | unsigned int pgd, pmd, ped; | ||
26 | unsigned int ptr; | ||
27 | unsigned int lvl, pte, paddrbase; | ||
28 | unsigned int ctx; | ||
29 | unsigned int paddr_calc; | ||
30 | |||
31 | paddrbase = 0; | ||
32 | |||
33 | if (srmmu_swprobe_trace) | ||
34 | printk(KERN_INFO "swprobe: trace on\n"); | ||
35 | |||
36 | ctxtbl = srmmu_get_ctable_ptr(); | ||
37 | if (!(ctxtbl)) { | ||
38 | if (srmmu_swprobe_trace) | ||
39 | printk(KERN_INFO "swprobe: srmmu_get_ctable_ptr returned 0=>0\n"); | ||
40 | return 0; | ||
41 | } | ||
42 | if (!_pfn_valid(PFN(ctxtbl))) { | ||
43 | if (srmmu_swprobe_trace) | ||
44 | printk(KERN_INFO | ||
45 | "swprobe: !_pfn_valid(%x)=>0\n", | ||
46 | PFN(ctxtbl)); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | ctx = srmmu_get_context(); | ||
51 | if (srmmu_swprobe_trace) | ||
52 | printk(KERN_INFO "swprobe: --- ctx (%x) ---\n", ctx); | ||
53 | |||
54 | pgd = LEON_BYPASS_LOAD_PA(ctxtbl + (ctx * 4)); | ||
55 | |||
56 | if (((pgd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { | ||
57 | if (srmmu_swprobe_trace) | ||
58 | printk(KERN_INFO "swprobe: pgd is entry level 3\n"); | ||
59 | lvl = 3; | ||
60 | pte = pgd; | ||
61 | paddrbase = pgd & _SRMMU_PTE_PMASK_LEON; | ||
62 | goto ready; | ||
63 | } | ||
64 | if (((pgd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { | ||
65 | if (srmmu_swprobe_trace) | ||
66 | printk(KERN_INFO "swprobe: pgd is invalid => 0\n"); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | if (srmmu_swprobe_trace) | ||
71 | printk(KERN_INFO "swprobe: --- pgd (%x) ---\n", pgd); | ||
72 | |||
73 | ptr = (pgd & SRMMU_PTD_PMASK) << 4; | ||
74 | ptr += ((((vaddr) >> LEON_PGD_SH) & LEON_PGD_M) * 4); | ||
75 | if (!_pfn_valid(PFN(ptr))) | ||
76 | return 0; | ||
77 | |||
78 | pmd = LEON_BYPASS_LOAD_PA(ptr); | ||
79 | if (((pmd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { | ||
80 | if (srmmu_swprobe_trace) | ||
81 | printk(KERN_INFO "swprobe: pmd is entry level 2\n"); | ||
82 | lvl = 2; | ||
83 | pte = pmd; | ||
84 | paddrbase = pmd & _SRMMU_PTE_PMASK_LEON; | ||
85 | goto ready; | ||
86 | } | ||
87 | if (((pmd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { | ||
88 | if (srmmu_swprobe_trace) | ||
89 | printk(KERN_INFO "swprobe: pmd is invalid => 0\n"); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | if (srmmu_swprobe_trace) | ||
94 | printk(KERN_INFO "swprobe: --- pmd (%x) ---\n", pmd); | ||
95 | |||
96 | ptr = (pmd & SRMMU_PTD_PMASK) << 4; | ||
97 | ptr += (((vaddr >> LEON_PMD_SH) & LEON_PMD_M) * 4); | ||
98 | if (!_pfn_valid(PFN(ptr))) { | ||
99 | if (srmmu_swprobe_trace) | ||
100 | printk(KERN_INFO "swprobe: !_pfn_valid(%x)=>0\n", | ||
101 | PFN(ptr)); | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | ped = LEON_BYPASS_LOAD_PA(ptr); | ||
106 | |||
107 | if (((ped & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { | ||
108 | if (srmmu_swprobe_trace) | ||
109 | printk(KERN_INFO "swprobe: ped is entry level 1\n"); | ||
110 | lvl = 1; | ||
111 | pte = ped; | ||
112 | paddrbase = ped & _SRMMU_PTE_PMASK_LEON; | ||
113 | goto ready; | ||
114 | } | ||
115 | if (((ped & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { | ||
116 | if (srmmu_swprobe_trace) | ||
117 | printk(KERN_INFO "swprobe: ped is invalid => 0\n"); | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | if (srmmu_swprobe_trace) | ||
122 | printk(KERN_INFO "swprobe: --- ped (%x) ---\n", ped); | ||
123 | |||
124 | ptr = (ped & SRMMU_PTD_PMASK) << 4; | ||
125 | ptr += (((vaddr >> LEON_PTE_SH) & LEON_PTE_M) * 4); | ||
126 | if (!_pfn_valid(PFN(ptr))) | ||
127 | return 0; | ||
128 | |||
129 | ptr = LEON_BYPASS_LOAD_PA(ptr); | ||
130 | if (((ptr & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { | ||
131 | if (srmmu_swprobe_trace) | ||
132 | printk(KERN_INFO "swprobe: ptr is entry level 0\n"); | ||
133 | lvl = 0; | ||
134 | pte = ptr; | ||
135 | paddrbase = ptr & _SRMMU_PTE_PMASK_LEON; | ||
136 | goto ready; | ||
137 | } | ||
138 | if (srmmu_swprobe_trace) | ||
139 | printk(KERN_INFO "swprobe: ptr is invalid => 0\n"); | ||
140 | return 0; | ||
141 | |||
142 | ready: | ||
143 | switch (lvl) { | ||
144 | case 0: | ||
145 | paddr_calc = | ||
146 | (vaddr & ~(-1 << LEON_PTE_SH)) | ((pte & ~0xff) << 4); | ||
147 | break; | ||
148 | case 1: | ||
149 | paddr_calc = | ||
150 | (vaddr & ~(-1 << LEON_PMD_SH)) | ((pte & ~0xff) << 4); | ||
151 | break; | ||
152 | case 2: | ||
153 | paddr_calc = | ||
154 | (vaddr & ~(-1 << LEON_PGD_SH)) | ((pte & ~0xff) << 4); | ||
155 | break; | ||
156 | default: | ||
157 | case 3: | ||
158 | paddr_calc = vaddr; | ||
159 | break; | ||
160 | } | ||
161 | if (srmmu_swprobe_trace) | ||
162 | printk(KERN_INFO "swprobe: padde %x\n", paddr_calc); | ||
163 | if (paddr) | ||
164 | *paddr = paddr_calc; | ||
165 | return paddrbase; | ||
166 | } | ||
167 | |||
168 | void leon_flush_icache_all(void) | ||
169 | { | ||
170 | __asm__ __volatile__(" flush "); /*iflush*/ | ||
171 | } | ||
172 | |||
173 | void leon_flush_dcache_all(void) | ||
174 | { | ||
175 | __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : | ||
176 | "i"(ASI_LEON_DFLUSH) : "memory"); | ||
177 | } | ||
178 | |||
179 | void leon_flush_pcache_all(struct vm_area_struct *vma, unsigned long page) | ||
180 | { | ||
181 | if (vma->vm_flags & VM_EXEC) | ||
182 | leon_flush_icache_all(); | ||
183 | leon_flush_dcache_all(); | ||
184 | } | ||
185 | |||
186 | void leon_flush_cache_all(void) | ||
187 | { | ||
188 | __asm__ __volatile__(" flush "); /*iflush*/ | ||
189 | __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : | ||
190 | "i"(ASI_LEON_DFLUSH) : "memory"); | ||
191 | } | ||
192 | |||
193 | void leon_flush_tlb_all(void) | ||
194 | { | ||
195 | leon_flush_cache_all(); | ||
196 | __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r"(0x400), | ||
197 | "i"(ASI_LEON_MMUFLUSH) : "memory"); | ||
198 | } | ||
199 | |||
200 | /* get all cache regs */ | ||
201 | void leon3_getCacheRegs(struct leon3_cacheregs *regs) | ||
202 | { | ||
203 | unsigned long ccr, iccr, dccr; | ||
204 | |||
205 | if (!regs) | ||
206 | return; | ||
207 | /* Get Cache regs from "Cache ASI" address 0x0, 0x8 and 0xC */ | ||
208 | __asm__ __volatile__("lda [%%g0] %3, %0\n\t" | ||
209 | "mov 0x08, %%g1\n\t" | ||
210 | "lda [%%g1] %3, %1\n\t" | ||
211 | "mov 0x0c, %%g1\n\t" | ||
212 | "lda [%%g1] %3, %2\n\t" | ||
213 | : "=r"(ccr), "=r"(iccr), "=r"(dccr) | ||
214 | /* output */ | ||
215 | : "i"(ASI_LEON_CACHEREGS) /* input */ | ||
216 | : "g1" /* clobber list */ | ||
217 | ); | ||
218 | regs->ccr = ccr; | ||
219 | regs->iccr = iccr; | ||
220 | regs->dccr = dccr; | ||
221 | } | ||
222 | |||
223 | /* Due to virtual cache we need to check cache configuration if | ||
224 | * it is possible to skip flushing in some cases. | ||
225 | * | ||
226 | * Leon2 and Leon3 differ in their way of telling cache information | ||
227 | * | ||
228 | */ | ||
229 | int leon_flush_needed(void) | ||
230 | { | ||
231 | int flush_needed = -1; | ||
232 | unsigned int ssize, sets; | ||
233 | char *setStr[4] = | ||
234 | { "direct mapped", "2-way associative", "3-way associative", | ||
235 | "4-way associative" | ||
236 | }; | ||
237 | /* leon 3 */ | ||
238 | struct leon3_cacheregs cregs; | ||
239 | leon3_getCacheRegs(&cregs); | ||
240 | sets = (cregs.dccr & LEON3_XCCR_SETS_MASK) >> 24; | ||
241 | /* (ssize=>realsize) 0=>1k, 1=>2k, 2=>4k, 3=>8k ... */ | ||
242 | ssize = 1 << ((cregs.dccr & LEON3_XCCR_SSIZE_MASK) >> 20); | ||
243 | |||
244 | printk(KERN_INFO "CACHE: %s cache, set size %dk\n", | ||
245 | sets > 3 ? "unknown" : setStr[sets], ssize); | ||
246 | if ((ssize <= (PAGE_SIZE / 1024)) && (sets == 0)) { | ||
247 | /* Set Size <= Page size ==> | ||
248 | flush on every context switch not needed. */ | ||
249 | flush_needed = 0; | ||
250 | printk(KERN_INFO "CACHE: not flushing on every context switch\n"); | ||
251 | } | ||
252 | return flush_needed; | ||
253 | } | ||
254 | |||
255 | void leon_switch_mm(void) | ||
256 | { | ||
257 | flush_tlb_mm((void *)0); | ||
258 | if (leon_flush_during_switch) | ||
259 | leon_flush_cache_all(); | ||
260 | } | ||
diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c index 652be05acbea..82ec8f666036 100644 --- a/arch/sparc/mm/loadmmu.c +++ b/arch/sparc/mm/loadmmu.c | |||
@@ -33,6 +33,7 @@ void __init load_mmu(void) | |||
33 | break; | 33 | break; |
34 | case sun4m: | 34 | case sun4m: |
35 | case sun4d: | 35 | case sun4d: |
36 | case sparc_leon: | ||
36 | ld_mmu_srmmu(); | 37 | ld_mmu_srmmu(); |
37 | break; | 38 | break; |
38 | default: | 39 | default: |
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index ade4eb373bdd..509b1ffeba66 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <asm/tsunami.h> | 46 | #include <asm/tsunami.h> |
47 | #include <asm/swift.h> | 47 | #include <asm/swift.h> |
48 | #include <asm/turbosparc.h> | 48 | #include <asm/turbosparc.h> |
49 | #include <asm/leon.h> | ||
49 | 50 | ||
50 | #include <asm/btfixup.h> | 51 | #include <asm/btfixup.h> |
51 | 52 | ||
@@ -569,6 +570,9 @@ static void srmmu_switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, | |||
569 | srmmu_ctxd_set(&srmmu_context_table[mm->context], mm->pgd); | 570 | srmmu_ctxd_set(&srmmu_context_table[mm->context], mm->pgd); |
570 | } | 571 | } |
571 | 572 | ||
573 | if (sparc_cpu_model == sparc_leon) | ||
574 | leon_switch_mm(); | ||
575 | |||
572 | if (is_hypersparc) | 576 | if (is_hypersparc) |
573 | hyper_flush_whole_icache(); | 577 | hyper_flush_whole_icache(); |
574 | 578 | ||
@@ -1977,6 +1981,45 @@ static void __init init_viking(void) | |||
1977 | poke_srmmu = poke_viking; | 1981 | poke_srmmu = poke_viking; |
1978 | } | 1982 | } |
1979 | 1983 | ||
1984 | #ifdef CONFIG_SPARC_LEON | ||
1985 | |||
1986 | void __init poke_leonsparc(void) | ||
1987 | { | ||
1988 | } | ||
1989 | |||
1990 | void __init init_leon(void) | ||
1991 | { | ||
1992 | |||
1993 | srmmu_name = "Leon"; | ||
1994 | |||
1995 | BTFIXUPSET_CALL(flush_cache_all, leon_flush_cache_all, | ||
1996 | BTFIXUPCALL_NORM); | ||
1997 | BTFIXUPSET_CALL(flush_cache_mm, leon_flush_cache_all, | ||
1998 | BTFIXUPCALL_NORM); | ||
1999 | BTFIXUPSET_CALL(flush_cache_page, leon_flush_pcache_all, | ||
2000 | BTFIXUPCALL_NORM); | ||
2001 | BTFIXUPSET_CALL(flush_cache_range, leon_flush_cache_all, | ||
2002 | BTFIXUPCALL_NORM); | ||
2003 | BTFIXUPSET_CALL(flush_page_for_dma, leon_flush_dcache_all, | ||
2004 | BTFIXUPCALL_NORM); | ||
2005 | |||
2006 | BTFIXUPSET_CALL(flush_tlb_all, leon_flush_tlb_all, BTFIXUPCALL_NORM); | ||
2007 | BTFIXUPSET_CALL(flush_tlb_mm, leon_flush_tlb_all, BTFIXUPCALL_NORM); | ||
2008 | BTFIXUPSET_CALL(flush_tlb_page, leon_flush_tlb_all, BTFIXUPCALL_NORM); | ||
2009 | BTFIXUPSET_CALL(flush_tlb_range, leon_flush_tlb_all, BTFIXUPCALL_NORM); | ||
2010 | |||
2011 | BTFIXUPSET_CALL(__flush_page_to_ram, leon_flush_cache_all, | ||
2012 | BTFIXUPCALL_NOP); | ||
2013 | BTFIXUPSET_CALL(flush_sig_insns, leon_flush_cache_all, BTFIXUPCALL_NOP); | ||
2014 | |||
2015 | poke_srmmu = poke_leonsparc; | ||
2016 | |||
2017 | srmmu_cache_pagetables = 0; | ||
2018 | |||
2019 | leon_flush_during_switch = leon_flush_needed(); | ||
2020 | } | ||
2021 | #endif | ||
2022 | |||
1980 | /* Probe for the srmmu chip version. */ | 2023 | /* Probe for the srmmu chip version. */ |
1981 | static void __init get_srmmu_type(void) | 2024 | static void __init get_srmmu_type(void) |
1982 | { | 2025 | { |
@@ -1992,7 +2035,15 @@ static void __init get_srmmu_type(void) | |||
1992 | psr_typ = (psr >> 28) & 0xf; | 2035 | psr_typ = (psr >> 28) & 0xf; |
1993 | psr_vers = (psr >> 24) & 0xf; | 2036 | psr_vers = (psr >> 24) & 0xf; |
1994 | 2037 | ||
1995 | /* First, check for HyperSparc or Cypress. */ | 2038 | /* First, check for sparc-leon. */ |
2039 | if (sparc_cpu_model == sparc_leon) { | ||
2040 | psr_typ = 0xf; /* hardcoded ids for older models/simulators */ | ||
2041 | psr_vers = 2; | ||
2042 | init_leon(); | ||
2043 | return; | ||
2044 | } | ||
2045 | |||
2046 | /* Second, check for HyperSparc or Cypress. */ | ||
1996 | if(mod_typ == 1) { | 2047 | if(mod_typ == 1) { |
1997 | switch(mod_rev) { | 2048 | switch(mod_rev) { |
1998 | case 7: | 2049 | case 7: |