diff options
Diffstat (limited to 'arch/sh/mm')
-rw-r--r-- | arch/sh/mm/Kconfig | 78 | ||||
-rw-r--r-- | arch/sh/mm/Makefile | 16 | ||||
-rw-r--r-- | arch/sh/mm/cache-debugfs.c | 147 | ||||
-rw-r--r-- | arch/sh/mm/cache-sh4.c | 685 | ||||
-rw-r--r-- | arch/sh/mm/cache-sh7705.c | 19 | ||||
-rw-r--r-- | arch/sh/mm/clear_page.S | 99 | ||||
-rw-r--r-- | arch/sh/mm/consistent.c | 2 | ||||
-rw-r--r-- | arch/sh/mm/fault.c | 207 | ||||
-rw-r--r-- | arch/sh/mm/hugetlbpage.c | 52 | ||||
-rw-r--r-- | arch/sh/mm/init.c | 32 | ||||
-rw-r--r-- | arch/sh/mm/ioremap.c | 17 | ||||
-rw-r--r-- | arch/sh/mm/pg-nommu.c | 17 | ||||
-rw-r--r-- | arch/sh/mm/pg-sh4.c | 24 | ||||
-rw-r--r-- | arch/sh/mm/pmb.c | 400 | ||||
-rw-r--r-- | arch/sh/mm/tlb-flush.c | 134 | ||||
-rw-r--r-- | arch/sh/mm/tlb-sh4.c | 8 |
16 files changed, 1454 insertions, 483 deletions
diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index fb586b1cf8bb..9dd606464d23 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig | |||
@@ -20,7 +20,10 @@ config CPU_SH4 | |||
20 | config CPU_SH4A | 20 | config CPU_SH4A |
21 | bool | 21 | bool |
22 | select CPU_SH4 | 22 | select CPU_SH4 |
23 | select CPU_HAS_INTC2_IRQ | 23 | |
24 | config CPU_SH4AL_DSP | ||
25 | bool | ||
26 | select CPU_SH4A | ||
24 | 27 | ||
25 | config CPU_SUBTYPE_ST40 | 28 | config CPU_SUBTYPE_ST40 |
26 | bool | 29 | bool |
@@ -48,6 +51,12 @@ config CPU_SUBTYPE_SH7705 | |||
48 | select CPU_SH3 | 51 | select CPU_SH3 |
49 | select CPU_HAS_PINT_IRQ | 52 | select CPU_HAS_PINT_IRQ |
50 | 53 | ||
54 | config CPU_SUBTYPE_SH7706 | ||
55 | bool "Support SH7706 processor" | ||
56 | select CPU_SH3 | ||
57 | help | ||
58 | Select SH7706 if you have a 133 Mhz SH-3 HD6417706 CPU. | ||
59 | |||
51 | config CPU_SUBTYPE_SH7707 | 60 | config CPU_SUBTYPE_SH7707 |
52 | bool "Support SH7707 processor" | 61 | bool "Support SH7707 processor" |
53 | select CPU_SH3 | 62 | select CPU_SH3 |
@@ -69,6 +78,12 @@ config CPU_SUBTYPE_SH7709 | |||
69 | help | 78 | help |
70 | Select SH7709 if you have a 80 Mhz SH-3 HD6417709 CPU. | 79 | Select SH7709 if you have a 80 Mhz SH-3 HD6417709 CPU. |
71 | 80 | ||
81 | config CPU_SUBTYPE_SH7710 | ||
82 | bool "Support SH7710 processor" | ||
83 | select CPU_SH3 | ||
84 | help | ||
85 | Select SH7710 if you have a SH3-DSP SH7710 CPU. | ||
86 | |||
72 | comment "SH-4 Processor Support" | 87 | comment "SH-4 Processor Support" |
73 | 88 | ||
74 | config CPU_SUBTYPE_SH7750 | 89 | config CPU_SUBTYPE_SH7750 |
@@ -133,10 +148,6 @@ config CPU_SUBTYPE_ST40GX1 | |||
133 | 148 | ||
134 | comment "SH-4A Processor Support" | 149 | comment "SH-4A Processor Support" |
135 | 150 | ||
136 | config CPU_SUBTYPE_SH73180 | ||
137 | bool "Support SH73180 processor" | ||
138 | select CPU_SH4A | ||
139 | |||
140 | config CPU_SUBTYPE_SH7770 | 151 | config CPU_SUBTYPE_SH7770 |
141 | bool "Support SH7770 processor" | 152 | bool "Support SH7770 processor" |
142 | select CPU_SH4A | 153 | select CPU_SH4A |
@@ -144,6 +155,17 @@ config CPU_SUBTYPE_SH7770 | |||
144 | config CPU_SUBTYPE_SH7780 | 155 | config CPU_SUBTYPE_SH7780 |
145 | bool "Support SH7780 processor" | 156 | bool "Support SH7780 processor" |
146 | select CPU_SH4A | 157 | select CPU_SH4A |
158 | select CPU_HAS_INTC2_IRQ | ||
159 | |||
160 | comment "SH4AL-DSP Processor Support" | ||
161 | |||
162 | config CPU_SUBTYPE_SH73180 | ||
163 | bool "Support SH73180 processor" | ||
164 | select CPU_SH4AL_DSP | ||
165 | |||
166 | config CPU_SUBTYPE_SH7343 | ||
167 | bool "Support SH7343 processor" | ||
168 | select CPU_SH4AL_DSP | ||
147 | 169 | ||
148 | endmenu | 170 | endmenu |
149 | 171 | ||
@@ -161,15 +183,59 @@ config MMU | |||
161 | turning this off will boot the kernel on these machines with the | 183 | turning this off will boot the kernel on these machines with the |
162 | MMU implicitly switched off. | 184 | MMU implicitly switched off. |
163 | 185 | ||
186 | config PAGE_OFFSET | ||
187 | hex | ||
188 | default "0x80000000" if MMU | ||
189 | default "0x00000000" | ||
190 | |||
191 | config MEMORY_START | ||
192 | hex "Physical memory start address" | ||
193 | default "0x08000000" | ||
194 | ---help--- | ||
195 | Computers built with Hitachi SuperH processors always | ||
196 | map the ROM starting at address zero. But the processor | ||
197 | does not specify the range that RAM takes. | ||
198 | |||
199 | The physical memory (RAM) start address will be automatically | ||
200 | set to 08000000. Other platforms, such as the Solution Engine | ||
201 | boards typically map RAM at 0C000000. | ||
202 | |||
203 | Tweak this only when porting to a new machine which does not | ||
204 | already have a defconfig. Changing it from the known correct | ||
205 | value on any of the known systems will only lead to disaster. | ||
206 | |||
207 | config MEMORY_SIZE | ||
208 | hex "Physical memory size" | ||
209 | default "0x00400000" | ||
210 | help | ||
211 | This sets the default memory size assumed by your SH kernel. It can | ||
212 | be overridden as normal by the 'mem=' argument on the kernel command | ||
213 | line. If unsure, consult your board specifications or just leave it | ||
214 | as 0x00400000 which was the default value before this became | ||
215 | configurable. | ||
216 | |||
164 | config 32BIT | 217 | config 32BIT |
165 | bool "Support 32-bit physical addressing through PMB" | 218 | bool "Support 32-bit physical addressing through PMB" |
166 | depends on CPU_SH4A | 219 | depends on CPU_SH4A && MMU |
167 | default y | 220 | default y |
168 | help | 221 | help |
169 | If you say Y here, physical addressing will be extended to | 222 | If you say Y here, physical addressing will be extended to |
170 | 32-bits through the SH-4A PMB. If this is not set, legacy | 223 | 32-bits through the SH-4A PMB. If this is not set, legacy |
171 | 29-bit physical addressing will be used. | 224 | 29-bit physical addressing will be used. |
172 | 225 | ||
226 | config VSYSCALL | ||
227 | bool "Support vsyscall page" | ||
228 | depends on MMU | ||
229 | default y | ||
230 | help | ||
231 | This will enable support for the kernel mapping a vDSO page | ||
232 | in process space, and subsequently handing down the entry point | ||
233 | to the libc through the ELF auxiliary vector. | ||
234 | |||
235 | From the kernel side this is used for the signal trampoline. | ||
236 | For systems with an MMU that can afford to give up a page, | ||
237 | (the default value) say Y. | ||
238 | |||
173 | choice | 239 | choice |
174 | prompt "HugeTLB page size" | 240 | prompt "HugeTLB page size" |
175 | depends on HUGETLB_PAGE && CPU_SH4 && MMU | 241 | depends on HUGETLB_PAGE && CPU_SH4 && MMU |
diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile index 9489a1424644..3ffd7f68c0a2 100644 --- a/arch/sh/mm/Makefile +++ b/arch/sh/mm/Makefile | |||
@@ -6,20 +6,26 @@ obj-y := init.o extable.o consistent.o | |||
6 | 6 | ||
7 | obj-$(CONFIG_CPU_SH2) += cache-sh2.o | 7 | obj-$(CONFIG_CPU_SH2) += cache-sh2.o |
8 | obj-$(CONFIG_CPU_SH3) += cache-sh3.o | 8 | obj-$(CONFIG_CPU_SH3) += cache-sh3.o |
9 | obj-$(CONFIG_CPU_SH4) += cache-sh4.o pg-sh4.o | 9 | obj-$(CONFIG_CPU_SH4) += cache-sh4.o |
10 | 10 | ||
11 | obj-$(CONFIG_DMA_PAGE_OPS) += pg-dma.o | 11 | obj-$(CONFIG_DMA_PAGE_OPS) += pg-dma.o |
12 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | 12 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o |
13 | 13 | ||
14 | mmu-y := fault-nommu.o tlb-nommu.o pg-nommu.o | 14 | mmu-y := fault-nommu.o tlb-nommu.o pg-nommu.o |
15 | mmu-$(CONFIG_MMU) := fault.o clear_page.o copy_page.o | 15 | mmu-$(CONFIG_MMU) := fault.o clear_page.o copy_page.o tlb-flush.o \ |
16 | ioremap.o | ||
16 | 17 | ||
17 | obj-y += $(mmu-y) | 18 | obj-y += $(mmu-y) |
18 | 19 | ||
20 | ifdef CONFIG_DEBUG_FS | ||
21 | obj-$(CONFIG_CPU_SH4) += cache-debugfs.o | ||
22 | endif | ||
23 | |||
19 | ifdef CONFIG_MMU | 24 | ifdef CONFIG_MMU |
20 | obj-$(CONFIG_CPU_SH3) += tlb-sh3.o | 25 | obj-$(CONFIG_CPU_SH3) += tlb-sh3.o |
21 | obj-$(CONFIG_CPU_SH4) += tlb-sh4.o ioremap.o | 26 | obj-$(CONFIG_CPU_SH4) += tlb-sh4.o pg-sh4.o |
22 | obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o | 27 | obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o |
23 | endif | 28 | endif |
24 | 29 | ||
25 | obj-$(CONFIG_SH7705_CACHE_32KB) += cache-sh7705.o | 30 | obj-$(CONFIG_SH7705_CACHE_32KB) += cache-sh7705.o |
31 | obj-$(CONFIG_32BIT) += pmb.o | ||
diff --git a/arch/sh/mm/cache-debugfs.c b/arch/sh/mm/cache-debugfs.c new file mode 100644 index 000000000000..a22d914e4d15 --- /dev/null +++ b/arch/sh/mm/cache-debugfs.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * debugfs ops for the L1 cache | ||
3 | * | ||
4 | * Copyright (C) 2006 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/debugfs.h> | ||
13 | #include <linux/seq_file.h> | ||
14 | #include <asm/processor.h> | ||
15 | #include <asm/uaccess.h> | ||
16 | #include <asm/cache.h> | ||
17 | #include <asm/io.h> | ||
18 | |||
19 | enum cache_type { | ||
20 | CACHE_TYPE_ICACHE, | ||
21 | CACHE_TYPE_DCACHE, | ||
22 | CACHE_TYPE_UNIFIED, | ||
23 | }; | ||
24 | |||
25 | static int cache_seq_show(struct seq_file *file, void *iter) | ||
26 | { | ||
27 | unsigned int cache_type = (unsigned int)file->private; | ||
28 | struct cache_info *cache; | ||
29 | unsigned int waysize, way, cache_size; | ||
30 | unsigned long ccr, base; | ||
31 | static unsigned long addrstart = 0; | ||
32 | |||
33 | /* | ||
34 | * Go uncached immediately so we don't skew the results any | ||
35 | * more than we already are.. | ||
36 | */ | ||
37 | jump_to_P2(); | ||
38 | |||
39 | ccr = ctrl_inl(CCR); | ||
40 | if ((ccr & CCR_CACHE_ENABLE) == 0) { | ||
41 | back_to_P1(); | ||
42 | |||
43 | seq_printf(file, "disabled\n"); | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | if (cache_type == CACHE_TYPE_DCACHE) { | ||
48 | base = CACHE_OC_ADDRESS_ARRAY; | ||
49 | cache = &cpu_data->dcache; | ||
50 | } else { | ||
51 | base = CACHE_IC_ADDRESS_ARRAY; | ||
52 | cache = &cpu_data->icache; | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Due to the amount of data written out (depending on the cache size), | ||
57 | * we may be iterated over multiple times. In this case, keep track of | ||
58 | * the entry position in addrstart, and rewind it when we've hit the | ||
59 | * end of the cache. | ||
60 | * | ||
61 | * Likewise, the same code is used for multiple caches, so care must | ||
62 | * be taken for bouncing addrstart back and forth so the appropriate | ||
63 | * cache is hit. | ||
64 | */ | ||
65 | cache_size = cache->ways * cache->sets * cache->linesz; | ||
66 | if (((addrstart & 0xff000000) != base) || | ||
67 | (addrstart & 0x00ffffff) > cache_size) | ||
68 | addrstart = base; | ||
69 | |||
70 | waysize = cache->sets; | ||
71 | |||
72 | /* | ||
73 | * If the OC is already in RAM mode, we only have | ||
74 | * half of the entries to consider.. | ||
75 | */ | ||
76 | if ((ccr & CCR_CACHE_ORA) && cache_type == CACHE_TYPE_DCACHE) | ||
77 | waysize >>= 1; | ||
78 | |||
79 | waysize <<= cache->entry_shift; | ||
80 | |||
81 | for (way = 0; way < cache->ways; way++) { | ||
82 | unsigned long addr; | ||
83 | unsigned int line; | ||
84 | |||
85 | seq_printf(file, "-----------------------------------------\n"); | ||
86 | seq_printf(file, "Way %d\n", way); | ||
87 | seq_printf(file, "-----------------------------------------\n"); | ||
88 | |||
89 | for (addr = addrstart, line = 0; | ||
90 | addr < addrstart + waysize; | ||
91 | addr += cache->linesz, line++) { | ||
92 | unsigned long data = ctrl_inl(addr); | ||
93 | |||
94 | /* Check the V bit, ignore invalid cachelines */ | ||
95 | if ((data & 1) == 0) | ||
96 | continue; | ||
97 | |||
98 | /* U: Dirty, cache tag is 10 bits up */ | ||
99 | seq_printf(file, "%3d: %c 0x%lx\n", | ||
100 | line, data & 2 ? 'U' : ' ', | ||
101 | data & 0x1ffffc00); | ||
102 | } | ||
103 | |||
104 | addrstart += cache->way_incr; | ||
105 | } | ||
106 | |||
107 | back_to_P1(); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int cache_debugfs_open(struct inode *inode, struct file *file) | ||
113 | { | ||
114 | return single_open(file, cache_seq_show, inode->u.generic_ip); | ||
115 | } | ||
116 | |||
117 | static struct file_operations cache_debugfs_fops = { | ||
118 | .owner = THIS_MODULE, | ||
119 | .open = cache_debugfs_open, | ||
120 | .read = seq_read, | ||
121 | .llseek = seq_lseek, | ||
122 | .release = seq_release, | ||
123 | }; | ||
124 | |||
125 | static int __init cache_debugfs_init(void) | ||
126 | { | ||
127 | struct dentry *dcache_dentry, *icache_dentry; | ||
128 | |||
129 | dcache_dentry = debugfs_create_file("dcache", S_IRUSR, NULL, | ||
130 | (unsigned int *)CACHE_TYPE_DCACHE, | ||
131 | &cache_debugfs_fops); | ||
132 | if (IS_ERR(dcache_dentry)) | ||
133 | return PTR_ERR(dcache_dentry); | ||
134 | |||
135 | icache_dentry = debugfs_create_file("icache", S_IRUSR, NULL, | ||
136 | (unsigned int *)CACHE_TYPE_ICACHE, | ||
137 | &cache_debugfs_fops); | ||
138 | if (IS_ERR(icache_dentry)) { | ||
139 | debugfs_remove(dcache_dentry); | ||
140 | return PTR_ERR(icache_dentry); | ||
141 | } | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | module_init(cache_debugfs_init); | ||
146 | |||
147 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 524cea5b47f9..e48cc22724d9 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c | |||
@@ -2,49 +2,120 @@ | |||
2 | * arch/sh/mm/cache-sh4.c | 2 | * arch/sh/mm/cache-sh4.c |
3 | * | 3 | * |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | 4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka |
5 | * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt | 5 | * Copyright (C) 2001 - 2006 Paul Mundt |
6 | * Copyright (C) 2003 Richard Curnow | 6 | * Copyright (C) 2003 Richard Curnow |
7 | * | 7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file "COPYING" in the main directory of this archive | 9 | * License. See the file "COPYING" in the main directory of this archive |
10 | * for more details. | 10 | * for more details. |
11 | */ | 11 | */ |
12 | |||
13 | #include <linux/init.h> | 12 | #include <linux/init.h> |
14 | #include <linux/mman.h> | ||
15 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
16 | #include <linux/threads.h> | ||
17 | #include <asm/addrspace.h> | 14 | #include <asm/addrspace.h> |
18 | #include <asm/page.h> | ||
19 | #include <asm/pgtable.h> | 15 | #include <asm/pgtable.h> |
20 | #include <asm/processor.h> | 16 | #include <asm/processor.h> |
21 | #include <asm/cache.h> | 17 | #include <asm/cache.h> |
22 | #include <asm/io.h> | 18 | #include <asm/io.h> |
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/pgalloc.h> | 19 | #include <asm/pgalloc.h> |
25 | #include <asm/mmu_context.h> | 20 | #include <asm/mmu_context.h> |
26 | #include <asm/cacheflush.h> | 21 | #include <asm/cacheflush.h> |
27 | 22 | ||
28 | extern void __flush_cache_4096_all(unsigned long start); | 23 | /* |
29 | static void __flush_cache_4096_all_ex(unsigned long start); | 24 | * The maximum number of pages we support up to when doing ranged dcache |
30 | extern void __flush_dcache_all(void); | 25 | * flushing. Anything exceeding this will simply flush the dcache in its |
31 | static void __flush_dcache_all_ex(void); | 26 | * entirety. |
27 | */ | ||
28 | #define MAX_DCACHE_PAGES 64 /* XXX: Tune for ways */ | ||
29 | |||
30 | static void __flush_dcache_segment_1way(unsigned long start, | ||
31 | unsigned long extent); | ||
32 | static void __flush_dcache_segment_2way(unsigned long start, | ||
33 | unsigned long extent); | ||
34 | static void __flush_dcache_segment_4way(unsigned long start, | ||
35 | unsigned long extent); | ||
36 | |||
37 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, | ||
38 | unsigned long exec_offset); | ||
39 | |||
40 | /* | ||
41 | * This is initialised here to ensure that it is not placed in the BSS. If | ||
42 | * that were to happen, note that cache_init gets called before the BSS is | ||
43 | * cleared, so this would get nulled out which would be hopeless. | ||
44 | */ | ||
45 | static void (*__flush_dcache_segment_fn)(unsigned long, unsigned long) = | ||
46 | (void (*)(unsigned long, unsigned long))0xdeadbeef; | ||
47 | |||
48 | static void compute_alias(struct cache_info *c) | ||
49 | { | ||
50 | c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1); | ||
51 | c->n_aliases = (c->alias_mask >> PAGE_SHIFT) + 1; | ||
52 | } | ||
53 | |||
54 | static void __init emit_cache_params(void) | ||
55 | { | ||
56 | printk("PVR=%08x CVR=%08x PRR=%08x\n", | ||
57 | ctrl_inl(CCN_PVR), | ||
58 | ctrl_inl(CCN_CVR), | ||
59 | ctrl_inl(CCN_PRR)); | ||
60 | printk("I-cache : n_ways=%d n_sets=%d way_incr=%d\n", | ||
61 | cpu_data->icache.ways, | ||
62 | cpu_data->icache.sets, | ||
63 | cpu_data->icache.way_incr); | ||
64 | printk("I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | ||
65 | cpu_data->icache.entry_mask, | ||
66 | cpu_data->icache.alias_mask, | ||
67 | cpu_data->icache.n_aliases); | ||
68 | printk("D-cache : n_ways=%d n_sets=%d way_incr=%d\n", | ||
69 | cpu_data->dcache.ways, | ||
70 | cpu_data->dcache.sets, | ||
71 | cpu_data->dcache.way_incr); | ||
72 | printk("D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | ||
73 | cpu_data->dcache.entry_mask, | ||
74 | cpu_data->dcache.alias_mask, | ||
75 | cpu_data->dcache.n_aliases); | ||
76 | |||
77 | if (!__flush_dcache_segment_fn) | ||
78 | panic("unknown number of cache ways\n"); | ||
79 | } | ||
32 | 80 | ||
33 | /* | 81 | /* |
34 | * SH-4 has virtually indexed and physically tagged cache. | 82 | * SH-4 has virtually indexed and physically tagged cache. |
35 | */ | 83 | */ |
36 | 84 | ||
37 | struct semaphore p3map_sem[4]; | 85 | /* Worst case assumed to be 64k cache, direct-mapped i.e. 4 synonym bits. */ |
86 | #define MAX_P3_SEMAPHORES 16 | ||
87 | |||
88 | struct semaphore p3map_sem[MAX_P3_SEMAPHORES]; | ||
38 | 89 | ||
39 | void __init p3_cache_init(void) | 90 | void __init p3_cache_init(void) |
40 | { | 91 | { |
41 | if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE)) | 92 | int i; |
93 | |||
94 | compute_alias(&cpu_data->icache); | ||
95 | compute_alias(&cpu_data->dcache); | ||
96 | |||
97 | switch (cpu_data->dcache.ways) { | ||
98 | case 1: | ||
99 | __flush_dcache_segment_fn = __flush_dcache_segment_1way; | ||
100 | break; | ||
101 | case 2: | ||
102 | __flush_dcache_segment_fn = __flush_dcache_segment_2way; | ||
103 | break; | ||
104 | case 4: | ||
105 | __flush_dcache_segment_fn = __flush_dcache_segment_4way; | ||
106 | break; | ||
107 | default: | ||
108 | __flush_dcache_segment_fn = NULL; | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | emit_cache_params(); | ||
113 | |||
114 | if (remap_area_pages(P3SEG, 0, PAGE_SIZE * 4, _PAGE_CACHABLE)) | ||
42 | panic("%s failed.", __FUNCTION__); | 115 | panic("%s failed.", __FUNCTION__); |
43 | 116 | ||
44 | sema_init (&p3map_sem[0], 1); | 117 | for (i = 0; i < cpu_data->dcache.n_aliases; i++) |
45 | sema_init (&p3map_sem[1], 1); | 118 | sema_init(&p3map_sem[i], 1); |
46 | sema_init (&p3map_sem[2], 1); | ||
47 | sema_init (&p3map_sem[3], 1); | ||
48 | } | 119 | } |
49 | 120 | ||
50 | /* | 121 | /* |
@@ -89,7 +160,6 @@ void __flush_purge_region(void *start, int size) | |||
89 | } | 160 | } |
90 | } | 161 | } |
91 | 162 | ||
92 | |||
93 | /* | 163 | /* |
94 | * No write back please | 164 | * No write back please |
95 | */ | 165 | */ |
@@ -108,40 +178,6 @@ void __flush_invalidate_region(void *start, int size) | |||
108 | } | 178 | } |
109 | } | 179 | } |
110 | 180 | ||
111 | static void __flush_dcache_all_ex(void) | ||
112 | { | ||
113 | unsigned long addr, end_addr, entry_offset; | ||
114 | |||
115 | end_addr = CACHE_OC_ADDRESS_ARRAY + (cpu_data->dcache.sets << cpu_data->dcache.entry_shift) * cpu_data->dcache.ways; | ||
116 | entry_offset = 1 << cpu_data->dcache.entry_shift; | ||
117 | for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; addr += entry_offset) { | ||
118 | ctrl_outl(0, addr); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static void __flush_cache_4096_all_ex(unsigned long start) | ||
123 | { | ||
124 | unsigned long addr, entry_offset; | ||
125 | int i; | ||
126 | |||
127 | entry_offset = 1 << cpu_data->dcache.entry_shift; | ||
128 | for (i = 0; i < cpu_data->dcache.ways; i++, start += cpu_data->dcache.way_incr) { | ||
129 | for (addr = CACHE_OC_ADDRESS_ARRAY + start; | ||
130 | addr < CACHE_OC_ADDRESS_ARRAY + 4096 + start; | ||
131 | addr += entry_offset) { | ||
132 | ctrl_outl(0, addr); | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | void flush_cache_4096_all(unsigned long start) | ||
138 | { | ||
139 | if (cpu_data->dcache.ways == 1) | ||
140 | __flush_cache_4096_all(start); | ||
141 | else | ||
142 | __flush_cache_4096_all_ex(start); | ||
143 | } | ||
144 | |||
145 | /* | 181 | /* |
146 | * Write back the range of D-cache, and purge the I-cache. | 182 | * Write back the range of D-cache, and purge the I-cache. |
147 | * | 183 | * |
@@ -153,14 +189,14 @@ void flush_icache_range(unsigned long start, unsigned long end) | |||
153 | } | 189 | } |
154 | 190 | ||
155 | /* | 191 | /* |
156 | * Write back the D-cache and purge the I-cache for signal trampoline. | 192 | * Write back the D-cache and purge the I-cache for signal trampoline. |
157 | * .. which happens to be the same behavior as flush_icache_range(). | 193 | * .. which happens to be the same behavior as flush_icache_range(). |
158 | * So, we simply flush out a line. | 194 | * So, we simply flush out a line. |
159 | */ | 195 | */ |
160 | void flush_cache_sigtramp(unsigned long addr) | 196 | void flush_cache_sigtramp(unsigned long addr) |
161 | { | 197 | { |
162 | unsigned long v, index; | 198 | unsigned long v, index; |
163 | unsigned long flags; | 199 | unsigned long flags; |
164 | int i; | 200 | int i; |
165 | 201 | ||
166 | v = addr & ~(L1_CACHE_BYTES-1); | 202 | v = addr & ~(L1_CACHE_BYTES-1); |
@@ -172,30 +208,33 @@ void flush_cache_sigtramp(unsigned long addr) | |||
172 | 208 | ||
173 | local_irq_save(flags); | 209 | local_irq_save(flags); |
174 | jump_to_P2(); | 210 | jump_to_P2(); |
175 | for(i = 0; i < cpu_data->icache.ways; i++, index += cpu_data->icache.way_incr) | 211 | |
212 | for (i = 0; i < cpu_data->icache.ways; | ||
213 | i++, index += cpu_data->icache.way_incr) | ||
176 | ctrl_outl(0, index); /* Clear out Valid-bit */ | 214 | ctrl_outl(0, index); /* Clear out Valid-bit */ |
215 | |||
177 | back_to_P1(); | 216 | back_to_P1(); |
217 | wmb(); | ||
178 | local_irq_restore(flags); | 218 | local_irq_restore(flags); |
179 | } | 219 | } |
180 | 220 | ||
181 | static inline void flush_cache_4096(unsigned long start, | 221 | static inline void flush_cache_4096(unsigned long start, |
182 | unsigned long phys) | 222 | unsigned long phys) |
183 | { | 223 | { |
184 | unsigned long flags; | 224 | unsigned long flags, exec_offset = 0; |
185 | extern void __flush_cache_4096(unsigned long addr, unsigned long phys, unsigned long exec_offset); | ||
186 | 225 | ||
187 | /* | 226 | /* |
188 | * SH7751, SH7751R, and ST40 have no restriction to handle cache. | 227 | * All types of SH-4 require PC to be in P2 to operate on the I-cache. |
189 | * (While SH7750 must do that at P2 area.) | 228 | * Some types of SH-4 require PC to be in P2 to operate on the D-cache. |
190 | */ | 229 | */ |
191 | if ((cpu_data->flags & CPU_HAS_P2_FLUSH_BUG) | 230 | if ((cpu_data->flags & CPU_HAS_P2_FLUSH_BUG) || |
192 | || start < CACHE_OC_ADDRESS_ARRAY) { | 231 | (start < CACHE_OC_ADDRESS_ARRAY)) |
193 | local_irq_save(flags); | 232 | exec_offset = 0x20000000; |
194 | __flush_cache_4096(start | SH_CACHE_ASSOC, P1SEGADDR(phys), 0x20000000); | 233 | |
195 | local_irq_restore(flags); | 234 | local_irq_save(flags); |
196 | } else { | 235 | __flush_cache_4096(start | SH_CACHE_ASSOC, |
197 | __flush_cache_4096(start | SH_CACHE_ASSOC, P1SEGADDR(phys), 0); | 236 | P1SEGADDR(phys), exec_offset); |
198 | } | 237 | local_irq_restore(flags); |
199 | } | 238 | } |
200 | 239 | ||
201 | /* | 240 | /* |
@@ -206,15 +245,19 @@ void flush_dcache_page(struct page *page) | |||
206 | { | 245 | { |
207 | if (test_bit(PG_mapped, &page->flags)) { | 246 | if (test_bit(PG_mapped, &page->flags)) { |
208 | unsigned long phys = PHYSADDR(page_address(page)); | 247 | unsigned long phys = PHYSADDR(page_address(page)); |
248 | unsigned long addr = CACHE_OC_ADDRESS_ARRAY; | ||
249 | int i, n; | ||
209 | 250 | ||
210 | /* Loop all the D-cache */ | 251 | /* Loop all the D-cache */ |
211 | flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys); | 252 | n = cpu_data->dcache.n_aliases; |
212 | flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x1000, phys); | 253 | for (i = 0; i < n; i++, addr += PAGE_SIZE) |
213 | flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x2000, phys); | 254 | flush_cache_4096(addr, phys); |
214 | flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x3000, phys); | ||
215 | } | 255 | } |
256 | |||
257 | wmb(); | ||
216 | } | 258 | } |
217 | 259 | ||
260 | /* TODO: Selective icache invalidation through IC address array.. */ | ||
218 | static inline void flush_icache_all(void) | 261 | static inline void flush_icache_all(void) |
219 | { | 262 | { |
220 | unsigned long flags, ccr; | 263 | unsigned long flags, ccr; |
@@ -227,34 +270,142 @@ static inline void flush_icache_all(void) | |||
227 | ccr |= CCR_CACHE_ICI; | 270 | ccr |= CCR_CACHE_ICI; |
228 | ctrl_outl(ccr, CCR); | 271 | ctrl_outl(ccr, CCR); |
229 | 272 | ||
273 | /* | ||
274 | * back_to_P1() will take care of the barrier for us, don't add | ||
275 | * another one! | ||
276 | */ | ||
277 | |||
230 | back_to_P1(); | 278 | back_to_P1(); |
231 | local_irq_restore(flags); | 279 | local_irq_restore(flags); |
232 | } | 280 | } |
233 | 281 | ||
282 | void flush_dcache_all(void) | ||
283 | { | ||
284 | (*__flush_dcache_segment_fn)(0UL, cpu_data->dcache.way_size); | ||
285 | wmb(); | ||
286 | } | ||
287 | |||
234 | void flush_cache_all(void) | 288 | void flush_cache_all(void) |
235 | { | 289 | { |
236 | if (cpu_data->dcache.ways == 1) | 290 | flush_dcache_all(); |
237 | __flush_dcache_all(); | ||
238 | else | ||
239 | __flush_dcache_all_ex(); | ||
240 | flush_icache_all(); | 291 | flush_icache_all(); |
241 | } | 292 | } |
242 | 293 | ||
294 | static void __flush_cache_mm(struct mm_struct *mm, unsigned long start, | ||
295 | unsigned long end) | ||
296 | { | ||
297 | unsigned long d = 0, p = start & PAGE_MASK; | ||
298 | unsigned long alias_mask = cpu_data->dcache.alias_mask; | ||
299 | unsigned long n_aliases = cpu_data->dcache.n_aliases; | ||
300 | unsigned long select_bit; | ||
301 | unsigned long all_aliases_mask; | ||
302 | unsigned long addr_offset; | ||
303 | pgd_t *dir; | ||
304 | pmd_t *pmd; | ||
305 | pud_t *pud; | ||
306 | pte_t *pte; | ||
307 | int i; | ||
308 | |||
309 | dir = pgd_offset(mm, p); | ||
310 | pud = pud_offset(dir, p); | ||
311 | pmd = pmd_offset(pud, p); | ||
312 | end = PAGE_ALIGN(end); | ||
313 | |||
314 | all_aliases_mask = (1 << n_aliases) - 1; | ||
315 | |||
316 | do { | ||
317 | if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) { | ||
318 | p &= PMD_MASK; | ||
319 | p += PMD_SIZE; | ||
320 | pmd++; | ||
321 | |||
322 | continue; | ||
323 | } | ||
324 | |||
325 | pte = pte_offset_kernel(pmd, p); | ||
326 | |||
327 | do { | ||
328 | unsigned long phys; | ||
329 | pte_t entry = *pte; | ||
330 | |||
331 | if (!(pte_val(entry) & _PAGE_PRESENT)) { | ||
332 | pte++; | ||
333 | p += PAGE_SIZE; | ||
334 | continue; | ||
335 | } | ||
336 | |||
337 | phys = pte_val(entry) & PTE_PHYS_MASK; | ||
338 | |||
339 | if ((p ^ phys) & alias_mask) { | ||
340 | d |= 1 << ((p & alias_mask) >> PAGE_SHIFT); | ||
341 | d |= 1 << ((phys & alias_mask) >> PAGE_SHIFT); | ||
342 | |||
343 | if (d == all_aliases_mask) | ||
344 | goto loop_exit; | ||
345 | } | ||
346 | |||
347 | pte++; | ||
348 | p += PAGE_SIZE; | ||
349 | } while (p < end && ((unsigned long)pte & ~PAGE_MASK)); | ||
350 | pmd++; | ||
351 | } while (p < end); | ||
352 | |||
353 | loop_exit: | ||
354 | addr_offset = 0; | ||
355 | select_bit = 1; | ||
356 | |||
357 | for (i = 0; i < n_aliases; i++) { | ||
358 | if (d & select_bit) { | ||
359 | (*__flush_dcache_segment_fn)(addr_offset, PAGE_SIZE); | ||
360 | wmb(); | ||
361 | } | ||
362 | |||
363 | select_bit <<= 1; | ||
364 | addr_offset += PAGE_SIZE; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Note : (RPC) since the caches are physically tagged, the only point | ||
370 | * of flush_cache_mm for SH-4 is to get rid of aliases from the | ||
371 | * D-cache. The assumption elsewhere, e.g. flush_cache_range, is that | ||
372 | * lines can stay resident so long as the virtual address they were | ||
373 | * accessed with (hence cache set) is in accord with the physical | ||
374 | * address (i.e. tag). It's no different here. So I reckon we don't | ||
375 | * need to flush the I-cache, since aliases don't matter for that. We | ||
376 | * should try that. | ||
377 | * | ||
378 | * Caller takes mm->mmap_sem. | ||
379 | */ | ||
243 | void flush_cache_mm(struct mm_struct *mm) | 380 | void flush_cache_mm(struct mm_struct *mm) |
244 | { | 381 | { |
245 | /* Is there any good way? */ | 382 | /* |
246 | /* XXX: possibly call flush_cache_range for each vm area */ | 383 | * If cache is only 4k-per-way, there are never any 'aliases'. Since |
247 | /* | 384 | * the cache is physically tagged, the data can just be left in there. |
248 | * FIXME: Really, the optimal solution here would be able to flush out | ||
249 | * individual lines created by the specified context, but this isn't | ||
250 | * feasible for a number of architectures (such as MIPS, and some | ||
251 | * SPARC) .. is this possible for SuperH? | ||
252 | * | ||
253 | * In the meantime, we'll just flush all of the caches.. this | ||
254 | * seems to be the simplest way to avoid at least a few wasted | ||
255 | * cache flushes. -Lethal | ||
256 | */ | 385 | */ |
257 | flush_cache_all(); | 386 | if (cpu_data->dcache.n_aliases == 0) |
387 | return; | ||
388 | |||
389 | /* | ||
390 | * Don't bother groveling around the dcache for the VMA ranges | ||
391 | * if there are too many PTEs to make it worthwhile. | ||
392 | */ | ||
393 | if (mm->nr_ptes >= MAX_DCACHE_PAGES) | ||
394 | flush_dcache_all(); | ||
395 | else { | ||
396 | struct vm_area_struct *vma; | ||
397 | |||
398 | /* | ||
399 | * In this case there are reasonably sized ranges to flush, | ||
400 | * iterate through the VMA list and take care of any aliases. | ||
401 | */ | ||
402 | for (vma = mm->mmap; vma; vma = vma->vm_next) | ||
403 | __flush_cache_mm(mm, vma->vm_start, vma->vm_end); | ||
404 | } | ||
405 | |||
406 | /* Only touch the icache if one of the VMAs has VM_EXEC set. */ | ||
407 | if (mm->exec_vm) | ||
408 | flush_icache_all(); | ||
258 | } | 409 | } |
259 | 410 | ||
260 | /* | 411 | /* |
@@ -263,27 +414,40 @@ void flush_cache_mm(struct mm_struct *mm) | |||
263 | * ADDR: Virtual Address (U0 address) | 414 | * ADDR: Virtual Address (U0 address) |
264 | * PFN: Physical page number | 415 | * PFN: Physical page number |
265 | */ | 416 | */ |
266 | void flush_cache_page(struct vm_area_struct *vma, unsigned long address, unsigned long pfn) | 417 | void flush_cache_page(struct vm_area_struct *vma, unsigned long address, |
418 | unsigned long pfn) | ||
267 | { | 419 | { |
268 | unsigned long phys = pfn << PAGE_SHIFT; | 420 | unsigned long phys = pfn << PAGE_SHIFT; |
421 | unsigned int alias_mask; | ||
422 | |||
423 | alias_mask = cpu_data->dcache.alias_mask; | ||
269 | 424 | ||
270 | /* We only need to flush D-cache when we have alias */ | 425 | /* We only need to flush D-cache when we have alias */ |
271 | if ((address^phys) & CACHE_ALIAS) { | 426 | if ((address^phys) & alias_mask) { |
272 | /* Loop 4K of the D-cache */ | 427 | /* Loop 4K of the D-cache */ |
273 | flush_cache_4096( | 428 | flush_cache_4096( |
274 | CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS), | 429 | CACHE_OC_ADDRESS_ARRAY | (address & alias_mask), |
275 | phys); | 430 | phys); |
276 | /* Loop another 4K of the D-cache */ | 431 | /* Loop another 4K of the D-cache */ |
277 | flush_cache_4096( | 432 | flush_cache_4096( |
278 | CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS), | 433 | CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask), |
279 | phys); | 434 | phys); |
280 | } | 435 | } |
281 | 436 | ||
282 | if (vma->vm_flags & VM_EXEC) | 437 | alias_mask = cpu_data->icache.alias_mask; |
283 | /* Loop 4K (half) of the I-cache */ | 438 | if (vma->vm_flags & VM_EXEC) { |
439 | /* | ||
440 | * Evict entries from the portion of the cache from which code | ||
441 | * may have been executed at this address (virtual). There's | ||
442 | * no need to evict from the portion corresponding to the | ||
443 | * physical address as for the D-cache, because we know the | ||
444 | * kernel has never executed the code through its identity | ||
445 | * translation. | ||
446 | */ | ||
284 | flush_cache_4096( | 447 | flush_cache_4096( |
285 | CACHE_IC_ADDRESS_ARRAY | (address & 0x1000), | 448 | CACHE_IC_ADDRESS_ARRAY | (address & alias_mask), |
286 | phys); | 449 | phys); |
450 | } | ||
287 | } | 451 | } |
288 | 452 | ||
289 | /* | 453 | /* |
@@ -298,52 +462,31 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long address, unsigne | |||
298 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | 462 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, |
299 | unsigned long end) | 463 | unsigned long end) |
300 | { | 464 | { |
301 | unsigned long p = start & PAGE_MASK; | 465 | /* |
302 | pgd_t *dir; | 466 | * If cache is only 4k-per-way, there are never any 'aliases'. Since |
303 | pmd_t *pmd; | 467 | * the cache is physically tagged, the data can just be left in there. |
304 | pte_t *pte; | 468 | */ |
305 | pte_t entry; | 469 | if (cpu_data->dcache.n_aliases == 0) |
306 | unsigned long phys; | 470 | return; |
307 | unsigned long d = 0; | ||
308 | |||
309 | dir = pgd_offset(vma->vm_mm, p); | ||
310 | pmd = pmd_offset(dir, p); | ||
311 | 471 | ||
312 | do { | 472 | /* |
313 | if (pmd_none(*pmd) || pmd_bad(*pmd)) { | 473 | * Don't bother with the lookup and alias check if we have a |
314 | p &= ~((1 << PMD_SHIFT) -1); | 474 | * wide range to cover, just blow away the dcache in its |
315 | p += (1 << PMD_SHIFT); | 475 | * entirety instead. -- PFM. |
316 | pmd++; | 476 | */ |
317 | continue; | 477 | if (((end - start) >> PAGE_SHIFT) >= MAX_DCACHE_PAGES) |
318 | } | 478 | flush_dcache_all(); |
319 | pte = pte_offset_kernel(pmd, p); | 479 | else |
320 | do { | 480 | __flush_cache_mm(vma->vm_mm, start, end); |
321 | entry = *pte; | 481 | |
322 | if ((pte_val(entry) & _PAGE_PRESENT)) { | 482 | if (vma->vm_flags & VM_EXEC) { |
323 | phys = pte_val(entry)&PTE_PHYS_MASK; | 483 | /* |
324 | if ((p^phys) & CACHE_ALIAS) { | 484 | * TODO: Is this required??? Need to look at how I-cache |
325 | d |= 1 << ((p & CACHE_ALIAS)>>12); | 485 | * coherency is assured when new programs are loaded to see if |
326 | d |= 1 << ((phys & CACHE_ALIAS)>>12); | 486 | * this matters. |
327 | if (d == 0x0f) | 487 | */ |
328 | goto loop_exit; | ||
329 | } | ||
330 | } | ||
331 | pte++; | ||
332 | p += PAGE_SIZE; | ||
333 | } while (p < end && ((unsigned long)pte & ~PAGE_MASK)); | ||
334 | pmd++; | ||
335 | } while (p < end); | ||
336 | loop_exit: | ||
337 | if (d & 1) | ||
338 | flush_cache_4096_all(0); | ||
339 | if (d & 2) | ||
340 | flush_cache_4096_all(0x1000); | ||
341 | if (d & 4) | ||
342 | flush_cache_4096_all(0x2000); | ||
343 | if (d & 8) | ||
344 | flush_cache_4096_all(0x3000); | ||
345 | if (vma->vm_flags & VM_EXEC) | ||
346 | flush_icache_all(); | 488 | flush_icache_all(); |
489 | } | ||
347 | } | 490 | } |
348 | 491 | ||
349 | /* | 492 | /* |
@@ -357,5 +500,273 @@ void flush_icache_user_range(struct vm_area_struct *vma, | |||
357 | struct page *page, unsigned long addr, int len) | 500 | struct page *page, unsigned long addr, int len) |
358 | { | 501 | { |
359 | flush_cache_page(vma, addr, page_to_pfn(page)); | 502 | flush_cache_page(vma, addr, page_to_pfn(page)); |
503 | mb(); | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * __flush_cache_4096 | ||
508 | * | ||
509 | * @addr: address in memory mapped cache array | ||
510 | * @phys: P1 address to flush (has to match tags if addr has 'A' bit | ||
511 | * set i.e. associative write) | ||
512 | * @exec_offset: set to 0x20000000 if flush has to be executed from P2 | ||
513 | * region else 0x0 | ||
514 | * | ||
515 | * The offset into the cache array implied by 'addr' selects the | ||
516 | * 'colour' of the virtual address range that will be flushed. The | ||
517 | * operation (purge/write-back) is selected by the lower 2 bits of | ||
518 | * 'phys'. | ||
519 | */ | ||
520 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, | ||
521 | unsigned long exec_offset) | ||
522 | { | ||
523 | int way_count; | ||
524 | unsigned long base_addr = addr; | ||
525 | struct cache_info *dcache; | ||
526 | unsigned long way_incr; | ||
527 | unsigned long a, ea, p; | ||
528 | unsigned long temp_pc; | ||
529 | |||
530 | dcache = &cpu_data->dcache; | ||
531 | /* Write this way for better assembly. */ | ||
532 | way_count = dcache->ways; | ||
533 | way_incr = dcache->way_incr; | ||
534 | |||
535 | /* | ||
536 | * Apply exec_offset (i.e. branch to P2 if required.). | ||
537 | * | ||
538 | * FIXME: | ||
539 | * | ||
540 | * If I write "=r" for the (temp_pc), it puts this in r6 hence | ||
541 | * trashing exec_offset before it's been added on - why? Hence | ||
542 | * "=&r" as a 'workaround' | ||
543 | */ | ||
544 | asm volatile("mov.l 1f, %0\n\t" | ||
545 | "add %1, %0\n\t" | ||
546 | "jmp @%0\n\t" | ||
547 | "nop\n\t" | ||
548 | ".balign 4\n\t" | ||
549 | "1: .long 2f\n\t" | ||
550 | "2:\n" : "=&r" (temp_pc) : "r" (exec_offset)); | ||
551 | |||
552 | /* | ||
553 | * We know there will be >=1 iteration, so write as do-while to avoid | ||
554 | * pointless nead-of-loop check for 0 iterations. | ||
555 | */ | ||
556 | do { | ||
557 | ea = base_addr + PAGE_SIZE; | ||
558 | a = base_addr; | ||
559 | p = phys; | ||
560 | |||
561 | do { | ||
562 | *(volatile unsigned long *)a = p; | ||
563 | /* | ||
564 | * Next line: intentionally not p+32, saves an add, p | ||
565 | * will do since only the cache tag bits need to | ||
566 | * match. | ||
567 | */ | ||
568 | *(volatile unsigned long *)(a+32) = p; | ||
569 | a += 64; | ||
570 | p += 64; | ||
571 | } while (a < ea); | ||
572 | |||
573 | base_addr += way_incr; | ||
574 | } while (--way_count != 0); | ||
360 | } | 575 | } |
361 | 576 | ||
577 | /* | ||
578 | * Break the 1, 2 and 4 way variants of this out into separate functions to | ||
579 | * avoid nearly all the overhead of having the conditional stuff in the function | ||
580 | * bodies (+ the 1 and 2 way cases avoid saving any registers too). | ||
581 | */ | ||
582 | static void __flush_dcache_segment_1way(unsigned long start, | ||
583 | unsigned long extent_per_way) | ||
584 | { | ||
585 | unsigned long orig_sr, sr_with_bl; | ||
586 | unsigned long base_addr; | ||
587 | unsigned long way_incr, linesz, way_size; | ||
588 | struct cache_info *dcache; | ||
589 | register unsigned long a0, a0e; | ||
590 | |||
591 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | ||
592 | sr_with_bl = orig_sr | (1<<28); | ||
593 | base_addr = ((unsigned long)&empty_zero_page[0]); | ||
594 | |||
595 | /* | ||
596 | * The previous code aligned base_addr to 16k, i.e. the way_size of all | ||
597 | * existing SH-4 D-caches. Whilst I don't see a need to have this | ||
598 | * aligned to any better than the cache line size (which it will be | ||
599 | * anyway by construction), let's align it to at least the way_size of | ||
600 | * any existing or conceivable SH-4 D-cache. -- RPC | ||
601 | */ | ||
602 | base_addr = ((base_addr >> 16) << 16); | ||
603 | base_addr |= start; | ||
604 | |||
605 | dcache = &cpu_data->dcache; | ||
606 | linesz = dcache->linesz; | ||
607 | way_incr = dcache->way_incr; | ||
608 | way_size = dcache->way_size; | ||
609 | |||
610 | a0 = base_addr; | ||
611 | a0e = base_addr + extent_per_way; | ||
612 | do { | ||
613 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
614 | asm volatile("movca.l r0, @%0\n\t" | ||
615 | "ocbi @%0" : : "r" (a0)); | ||
616 | a0 += linesz; | ||
617 | asm volatile("movca.l r0, @%0\n\t" | ||
618 | "ocbi @%0" : : "r" (a0)); | ||
619 | a0 += linesz; | ||
620 | asm volatile("movca.l r0, @%0\n\t" | ||
621 | "ocbi @%0" : : "r" (a0)); | ||
622 | a0 += linesz; | ||
623 | asm volatile("movca.l r0, @%0\n\t" | ||
624 | "ocbi @%0" : : "r" (a0)); | ||
625 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
626 | a0 += linesz; | ||
627 | } while (a0 < a0e); | ||
628 | } | ||
629 | |||
630 | static void __flush_dcache_segment_2way(unsigned long start, | ||
631 | unsigned long extent_per_way) | ||
632 | { | ||
633 | unsigned long orig_sr, sr_with_bl; | ||
634 | unsigned long base_addr; | ||
635 | unsigned long way_incr, linesz, way_size; | ||
636 | struct cache_info *dcache; | ||
637 | register unsigned long a0, a1, a0e; | ||
638 | |||
639 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | ||
640 | sr_with_bl = orig_sr | (1<<28); | ||
641 | base_addr = ((unsigned long)&empty_zero_page[0]); | ||
642 | |||
643 | /* See comment under 1-way above */ | ||
644 | base_addr = ((base_addr >> 16) << 16); | ||
645 | base_addr |= start; | ||
646 | |||
647 | dcache = &cpu_data->dcache; | ||
648 | linesz = dcache->linesz; | ||
649 | way_incr = dcache->way_incr; | ||
650 | way_size = dcache->way_size; | ||
651 | |||
652 | a0 = base_addr; | ||
653 | a1 = a0 + way_incr; | ||
654 | a0e = base_addr + extent_per_way; | ||
655 | do { | ||
656 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
657 | asm volatile("movca.l r0, @%0\n\t" | ||
658 | "movca.l r0, @%1\n\t" | ||
659 | "ocbi @%0\n\t" | ||
660 | "ocbi @%1" : : | ||
661 | "r" (a0), "r" (a1)); | ||
662 | a0 += linesz; | ||
663 | a1 += linesz; | ||
664 | asm volatile("movca.l r0, @%0\n\t" | ||
665 | "movca.l r0, @%1\n\t" | ||
666 | "ocbi @%0\n\t" | ||
667 | "ocbi @%1" : : | ||
668 | "r" (a0), "r" (a1)); | ||
669 | a0 += linesz; | ||
670 | a1 += linesz; | ||
671 | asm volatile("movca.l r0, @%0\n\t" | ||
672 | "movca.l r0, @%1\n\t" | ||
673 | "ocbi @%0\n\t" | ||
674 | "ocbi @%1" : : | ||
675 | "r" (a0), "r" (a1)); | ||
676 | a0 += linesz; | ||
677 | a1 += linesz; | ||
678 | asm volatile("movca.l r0, @%0\n\t" | ||
679 | "movca.l r0, @%1\n\t" | ||
680 | "ocbi @%0\n\t" | ||
681 | "ocbi @%1" : : | ||
682 | "r" (a0), "r" (a1)); | ||
683 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
684 | a0 += linesz; | ||
685 | a1 += linesz; | ||
686 | } while (a0 < a0e); | ||
687 | } | ||
688 | |||
689 | static void __flush_dcache_segment_4way(unsigned long start, | ||
690 | unsigned long extent_per_way) | ||
691 | { | ||
692 | unsigned long orig_sr, sr_with_bl; | ||
693 | unsigned long base_addr; | ||
694 | unsigned long way_incr, linesz, way_size; | ||
695 | struct cache_info *dcache; | ||
696 | register unsigned long a0, a1, a2, a3, a0e; | ||
697 | |||
698 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | ||
699 | sr_with_bl = orig_sr | (1<<28); | ||
700 | base_addr = ((unsigned long)&empty_zero_page[0]); | ||
701 | |||
702 | /* See comment under 1-way above */ | ||
703 | base_addr = ((base_addr >> 16) << 16); | ||
704 | base_addr |= start; | ||
705 | |||
706 | dcache = &cpu_data->dcache; | ||
707 | linesz = dcache->linesz; | ||
708 | way_incr = dcache->way_incr; | ||
709 | way_size = dcache->way_size; | ||
710 | |||
711 | a0 = base_addr; | ||
712 | a1 = a0 + way_incr; | ||
713 | a2 = a1 + way_incr; | ||
714 | a3 = a2 + way_incr; | ||
715 | a0e = base_addr + extent_per_way; | ||
716 | do { | ||
717 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
718 | asm volatile("movca.l r0, @%0\n\t" | ||
719 | "movca.l r0, @%1\n\t" | ||
720 | "movca.l r0, @%2\n\t" | ||
721 | "movca.l r0, @%3\n\t" | ||
722 | "ocbi @%0\n\t" | ||
723 | "ocbi @%1\n\t" | ||
724 | "ocbi @%2\n\t" | ||
725 | "ocbi @%3\n\t" : : | ||
726 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
727 | a0 += linesz; | ||
728 | a1 += linesz; | ||
729 | a2 += linesz; | ||
730 | a3 += linesz; | ||
731 | asm volatile("movca.l r0, @%0\n\t" | ||
732 | "movca.l r0, @%1\n\t" | ||
733 | "movca.l r0, @%2\n\t" | ||
734 | "movca.l r0, @%3\n\t" | ||
735 | "ocbi @%0\n\t" | ||
736 | "ocbi @%1\n\t" | ||
737 | "ocbi @%2\n\t" | ||
738 | "ocbi @%3\n\t" : : | ||
739 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
740 | a0 += linesz; | ||
741 | a1 += linesz; | ||
742 | a2 += linesz; | ||
743 | a3 += linesz; | ||
744 | asm volatile("movca.l r0, @%0\n\t" | ||
745 | "movca.l r0, @%1\n\t" | ||
746 | "movca.l r0, @%2\n\t" | ||
747 | "movca.l r0, @%3\n\t" | ||
748 | "ocbi @%0\n\t" | ||
749 | "ocbi @%1\n\t" | ||
750 | "ocbi @%2\n\t" | ||
751 | "ocbi @%3\n\t" : : | ||
752 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
753 | a0 += linesz; | ||
754 | a1 += linesz; | ||
755 | a2 += linesz; | ||
756 | a3 += linesz; | ||
757 | asm volatile("movca.l r0, @%0\n\t" | ||
758 | "movca.l r0, @%1\n\t" | ||
759 | "movca.l r0, @%2\n\t" | ||
760 | "movca.l r0, @%3\n\t" | ||
761 | "ocbi @%0\n\t" | ||
762 | "ocbi @%1\n\t" | ||
763 | "ocbi @%2\n\t" | ||
764 | "ocbi @%3\n\t" : : | ||
765 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
766 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
767 | a0 += linesz; | ||
768 | a1 += linesz; | ||
769 | a2 += linesz; | ||
770 | a3 += linesz; | ||
771 | } while (a0 < a0e); | ||
772 | } | ||
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c index bf94eedb0a8e..045abdf078f5 100644 --- a/arch/sh/mm/cache-sh7705.c +++ b/arch/sh/mm/cache-sh7705.c | |||
@@ -9,7 +9,6 @@ | |||
9 | * for more details. | 9 | * for more details. |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | |||
13 | #include <linux/init.h> | 12 | #include <linux/init.h> |
14 | #include <linux/mman.h> | 13 | #include <linux/mman.h> |
15 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
@@ -25,14 +24,10 @@ | |||
25 | #include <asm/mmu_context.h> | 24 | #include <asm/mmu_context.h> |
26 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
27 | 26 | ||
28 | /* The 32KB cache on the SH7705 suffers from the same synonym problem | 27 | /* |
29 | * as SH4 CPUs */ | 28 | * The 32KB cache on the SH7705 suffers from the same synonym problem |
30 | 29 | * as SH4 CPUs | |
31 | #define __pte_offset(address) \ | 30 | */ |
32 | ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) | ||
33 | #define pte_offset(dir, address) ((pte_t *) pmd_page_vaddr(*(dir)) + \ | ||
34 | __pte_offset(address)) | ||
35 | |||
36 | static inline void cache_wback_all(void) | 31 | static inline void cache_wback_all(void) |
37 | { | 32 | { |
38 | unsigned long ways, waysize, addrstart; | 33 | unsigned long ways, waysize, addrstart; |
@@ -73,7 +68,6 @@ void flush_icache_range(unsigned long start, unsigned long end) | |||
73 | __flush_wback_region((void *)start, end - start); | 68 | __flush_wback_region((void *)start, end - start); |
74 | } | 69 | } |
75 | 70 | ||
76 | |||
77 | /* | 71 | /* |
78 | * Writeback&Invalidate the D-cache of the page | 72 | * Writeback&Invalidate the D-cache of the page |
79 | */ | 73 | */ |
@@ -128,7 +122,6 @@ static void __flush_dcache_page(unsigned long phys) | |||
128 | local_irq_restore(flags); | 122 | local_irq_restore(flags); |
129 | } | 123 | } |
130 | 124 | ||
131 | |||
132 | /* | 125 | /* |
133 | * Write back & invalidate the D-cache of the page. | 126 | * Write back & invalidate the D-cache of the page. |
134 | * (To avoid "alias" issues) | 127 | * (To avoid "alias" issues) |
@@ -186,7 +179,8 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | |||
186 | * | 179 | * |
187 | * ADDRESS: Virtual Address (U0 address) | 180 | * ADDRESS: Virtual Address (U0 address) |
188 | */ | 181 | */ |
189 | void flush_cache_page(struct vm_area_struct *vma, unsigned long address, unsigned long pfn) | 182 | void flush_cache_page(struct vm_area_struct *vma, unsigned long address, |
183 | unsigned long pfn) | ||
190 | { | 184 | { |
191 | __flush_dcache_page(pfn << PAGE_SHIFT); | 185 | __flush_dcache_page(pfn << PAGE_SHIFT); |
192 | } | 186 | } |
@@ -203,4 +197,3 @@ void flush_icache_page(struct vm_area_struct *vma, struct page *page) | |||
203 | { | 197 | { |
204 | __flush_purge_region(page_address(page), PAGE_SIZE); | 198 | __flush_purge_region(page_address(page), PAGE_SIZE); |
205 | } | 199 | } |
206 | |||
diff --git a/arch/sh/mm/clear_page.S b/arch/sh/mm/clear_page.S index 08acead7b2a1..7b96425ae270 100644 --- a/arch/sh/mm/clear_page.S +++ b/arch/sh/mm/clear_page.S | |||
@@ -193,102 +193,5 @@ ENTRY(__clear_user_page) | |||
193 | nop | 193 | nop |
194 | .L4096: .word 4096 | 194 | .L4096: .word 4096 |
195 | 195 | ||
196 | ENTRY(__flush_cache_4096) | ||
197 | mov.l 1f,r3 | ||
198 | add r6,r3 | ||
199 | mov r4,r0 | ||
200 | mov #64,r2 | ||
201 | shll r2 | ||
202 | mov #64,r6 | ||
203 | jmp @r3 | ||
204 | mov #96,r7 | ||
205 | .align 2 | ||
206 | 1: .long 2f | ||
207 | 2: | ||
208 | .rept 32 | ||
209 | mov.l r5,@r0 | ||
210 | mov.l r5,@(32,r0) | ||
211 | mov.l r5,@(r0,r6) | ||
212 | mov.l r5,@(r0,r7) | ||
213 | add r2,r5 | ||
214 | add r2,r0 | ||
215 | .endr | ||
216 | nop | ||
217 | nop | ||
218 | nop | ||
219 | nop | ||
220 | nop | ||
221 | nop | ||
222 | nop | ||
223 | rts | ||
224 | nop | ||
225 | |||
226 | ENTRY(__flush_dcache_all) | ||
227 | mov.l 2f,r0 | ||
228 | mov.l 3f,r4 | ||
229 | and r0,r4 ! r4 = (unsigned long)&empty_zero_page[0] & ~0xffffc000 | ||
230 | stc sr,r1 ! save SR | ||
231 | mov.l 4f,r2 | ||
232 | or r1,r2 | ||
233 | mov #32,r3 | ||
234 | shll2 r3 | ||
235 | 1: | ||
236 | ldc r2,sr ! set BL bit | ||
237 | movca.l r0,@r4 | ||
238 | ocbi @r4 | ||
239 | add #32,r4 | ||
240 | movca.l r0,@r4 | ||
241 | ocbi @r4 | ||
242 | add #32,r4 | ||
243 | movca.l r0,@r4 | ||
244 | ocbi @r4 | ||
245 | add #32,r4 | ||
246 | movca.l r0,@r4 | ||
247 | ocbi @r4 | ||
248 | ldc r1,sr ! restore SR | ||
249 | dt r3 | ||
250 | bf/s 1b | ||
251 | add #32,r4 | ||
252 | |||
253 | rts | ||
254 | nop | ||
255 | .align 2 | ||
256 | 2: .long 0xffffc000 | ||
257 | 3: .long empty_zero_page | ||
258 | 4: .long 0x10000000 ! BL bit | ||
259 | |||
260 | /* __flush_cache_4096_all(unsigned long addr) */ | ||
261 | ENTRY(__flush_cache_4096_all) | ||
262 | mov.l 2f,r0 | ||
263 | mov.l 3f,r2 | ||
264 | and r0,r2 | ||
265 | or r2,r4 ! r4 = addr | (unsigned long)&empty_zero_page[0] & ~0x3fff | ||
266 | stc sr,r1 ! save SR | ||
267 | mov.l 4f,r2 | ||
268 | or r1,r2 | ||
269 | mov #32,r3 | ||
270 | 1: | ||
271 | ldc r2,sr ! set BL bit | ||
272 | movca.l r0,@r4 | ||
273 | ocbi @r4 | ||
274 | add #32,r4 | ||
275 | movca.l r0,@r4 | ||
276 | ocbi @r4 | ||
277 | add #32,r4 | ||
278 | movca.l r0,@r4 | ||
279 | ocbi @r4 | ||
280 | add #32,r4 | ||
281 | movca.l r0,@r4 | ||
282 | ocbi @r4 | ||
283 | ldc r1,sr ! restore SR | ||
284 | dt r3 | ||
285 | bf/s 1b | ||
286 | add #32,r4 | ||
287 | |||
288 | rts | ||
289 | nop | ||
290 | .align 2 | ||
291 | 2: .long 0xffffc000 | ||
292 | 3: .long empty_zero_page | ||
293 | 4: .long 0x10000000 ! BL bit | ||
294 | #endif | 196 | #endif |
197 | |||
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c index ee73e30263af..c81e6b67ad30 100644 --- a/arch/sh/mm/consistent.c +++ b/arch/sh/mm/consistent.c | |||
@@ -9,6 +9,8 @@ | |||
9 | */ | 9 | */ |
10 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
11 | #include <linux/dma-mapping.h> | 11 | #include <linux/dma-mapping.h> |
12 | #include <asm/cacheflush.h> | ||
13 | #include <asm/addrspace.h> | ||
12 | #include <asm/io.h> | 14 | #include <asm/io.h> |
13 | 15 | ||
14 | void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle) | 16 | void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle) |
diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 775f86cd3fe8..c69fd603226a 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c | |||
@@ -1,33 +1,22 @@ | |||
1 | /* $Id: fault.c,v 1.14 2004/01/13 05:52:11 kkojima Exp $ | 1 | /* |
2 | * Page fault handler for SH with an MMU. | ||
2 | * | 3 | * |
3 | * linux/arch/sh/mm/fault.c | ||
4 | * Copyright (C) 1999 Niibe Yutaka | 4 | * Copyright (C) 1999 Niibe Yutaka |
5 | * Copyright (C) 2003 Paul Mundt | 5 | * Copyright (C) 2003 Paul Mundt |
6 | * | 6 | * |
7 | * Based on linux/arch/i386/mm/fault.c: | 7 | * Based on linux/arch/i386/mm/fault.c: |
8 | * Copyright (C) 1995 Linus Torvalds | 8 | * Copyright (C) 1995 Linus Torvalds |
9 | * | ||
10 | * This file is subject to the terms and conditions of the GNU General Public | ||
11 | * License. See the file "COPYING" in the main directory of this archive | ||
12 | * for more details. | ||
9 | */ | 13 | */ |
10 | |||
11 | #include <linux/signal.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
14 | #include <linux/errno.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/ptrace.h> | ||
18 | #include <linux/mman.h> | ||
19 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
20 | #include <linux/smp.h> | 16 | #include <linux/hardirq.h> |
21 | #include <linux/smp_lock.h> | 17 | #include <linux/kprobes.h> |
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/module.h> | ||
24 | |||
25 | #include <asm/system.h> | 18 | #include <asm/system.h> |
26 | #include <asm/io.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <asm/pgalloc.h> | ||
29 | #include <asm/mmu_context.h> | 19 | #include <asm/mmu_context.h> |
30 | #include <asm/cacheflush.h> | ||
31 | #include <asm/kgdb.h> | 20 | #include <asm/kgdb.h> |
32 | 21 | ||
33 | extern void die(const char *,struct pt_regs *,long); | 22 | extern void die(const char *,struct pt_regs *,long); |
@@ -187,18 +176,30 @@ do_sigbus: | |||
187 | goto no_context; | 176 | goto no_context; |
188 | } | 177 | } |
189 | 178 | ||
179 | #ifdef CONFIG_SH_STORE_QUEUES | ||
190 | /* | 180 | /* |
191 | * Called with interrupt disabled. | 181 | * This is a special case for the SH-4 store queues, as pages for this |
182 | * space still need to be faulted in before it's possible to flush the | ||
183 | * store queue cache for writeout to the remapped region. | ||
192 | */ | 184 | */ |
193 | asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess, | 185 | #define P3_ADDR_MAX (P4SEG_STORE_QUE + 0x04000000) |
194 | unsigned long address) | 186 | #else |
187 | #define P3_ADDR_MAX P4SEG | ||
188 | #endif | ||
189 | |||
190 | /* | ||
191 | * Called with interrupts disabled. | ||
192 | */ | ||
193 | asmlinkage int __kprobes __do_page_fault(struct pt_regs *regs, | ||
194 | unsigned long writeaccess, | ||
195 | unsigned long address) | ||
195 | { | 196 | { |
196 | unsigned long addrmax = P4SEG; | ||
197 | pgd_t *pgd; | 197 | pgd_t *pgd; |
198 | pud_t *pud; | ||
198 | pmd_t *pmd; | 199 | pmd_t *pmd; |
199 | pte_t *pte; | 200 | pte_t *pte; |
200 | pte_t entry; | 201 | pte_t entry; |
201 | struct mm_struct *mm; | 202 | struct mm_struct *mm = current->mm; |
202 | spinlock_t *ptl; | 203 | spinlock_t *ptl; |
203 | int ret = 1; | 204 | int ret = 1; |
204 | 205 | ||
@@ -207,31 +208,37 @@ asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess, | |||
207 | kgdb_bus_err_hook(); | 208 | kgdb_bus_err_hook(); |
208 | #endif | 209 | #endif |
209 | 210 | ||
210 | #ifdef CONFIG_SH_STORE_QUEUES | 211 | /* |
211 | addrmax = P4SEG_STORE_QUE + 0x04000000; | 212 | * We don't take page faults for P1, P2, and parts of P4, these |
212 | #endif | 213 | * are always mapped, whether it be due to legacy behaviour in |
213 | 214 | * 29-bit mode, or due to PMB configuration in 32-bit mode. | |
214 | if (address >= P3SEG && address < addrmax) { | 215 | */ |
216 | if (address >= P3SEG && address < P3_ADDR_MAX) { | ||
215 | pgd = pgd_offset_k(address); | 217 | pgd = pgd_offset_k(address); |
216 | mm = NULL; | 218 | mm = NULL; |
217 | } else if (address >= TASK_SIZE) | 219 | } else { |
218 | return 1; | 220 | if (unlikely(address >= TASK_SIZE || !mm)) |
219 | else if (!(mm = current->mm)) | 221 | return 1; |
220 | return 1; | 222 | |
221 | else | ||
222 | pgd = pgd_offset(mm, address); | 223 | pgd = pgd_offset(mm, address); |
224 | } | ||
223 | 225 | ||
224 | pmd = pmd_offset(pgd, address); | 226 | pud = pud_offset(pgd, address); |
227 | if (pud_none_or_clear_bad(pud)) | ||
228 | return 1; | ||
229 | pmd = pmd_offset(pud, address); | ||
225 | if (pmd_none_or_clear_bad(pmd)) | 230 | if (pmd_none_or_clear_bad(pmd)) |
226 | return 1; | 231 | return 1; |
232 | |||
227 | if (mm) | 233 | if (mm) |
228 | pte = pte_offset_map_lock(mm, pmd, address, &ptl); | 234 | pte = pte_offset_map_lock(mm, pmd, address, &ptl); |
229 | else | 235 | else |
230 | pte = pte_offset_kernel(pmd, address); | 236 | pte = pte_offset_kernel(pmd, address); |
231 | 237 | ||
232 | entry = *pte; | 238 | entry = *pte; |
233 | if (pte_none(entry) || pte_not_present(entry) | 239 | if (unlikely(pte_none(entry) || pte_not_present(entry))) |
234 | || (writeaccess && !pte_write(entry))) | 240 | goto unlock; |
241 | if (unlikely(writeaccess && !pte_write(entry))) | ||
235 | goto unlock; | 242 | goto unlock; |
236 | 243 | ||
237 | if (writeaccess) | 244 | if (writeaccess) |
@@ -243,13 +250,7 @@ asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess, | |||
243 | * ITLB is not affected by "ldtlb" instruction. | 250 | * ITLB is not affected by "ldtlb" instruction. |
244 | * So, we need to flush the entry by ourselves. | 251 | * So, we need to flush the entry by ourselves. |
245 | */ | 252 | */ |
246 | 253 | __flush_tlb_page(get_asid(), address & PAGE_MASK); | |
247 | { | ||
248 | unsigned long flags; | ||
249 | local_irq_save(flags); | ||
250 | __flush_tlb_page(get_asid(), address&PAGE_MASK); | ||
251 | local_irq_restore(flags); | ||
252 | } | ||
253 | #endif | 254 | #endif |
254 | 255 | ||
255 | set_pte(pte, entry); | 256 | set_pte(pte, entry); |
@@ -260,121 +261,3 @@ unlock: | |||
260 | pte_unmap_unlock(pte, ptl); | 261 | pte_unmap_unlock(pte, ptl); |
261 | return ret; | 262 | return ret; |
262 | } | 263 | } |
263 | |||
264 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
265 | { | ||
266 | if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) { | ||
267 | unsigned long flags; | ||
268 | unsigned long asid; | ||
269 | unsigned long saved_asid = MMU_NO_ASID; | ||
270 | |||
271 | asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK; | ||
272 | page &= PAGE_MASK; | ||
273 | |||
274 | local_irq_save(flags); | ||
275 | if (vma->vm_mm != current->mm) { | ||
276 | saved_asid = get_asid(); | ||
277 | set_asid(asid); | ||
278 | } | ||
279 | __flush_tlb_page(asid, page); | ||
280 | if (saved_asid != MMU_NO_ASID) | ||
281 | set_asid(saved_asid); | ||
282 | local_irq_restore(flags); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
287 | unsigned long end) | ||
288 | { | ||
289 | struct mm_struct *mm = vma->vm_mm; | ||
290 | |||
291 | if (mm->context != NO_CONTEXT) { | ||
292 | unsigned long flags; | ||
293 | int size; | ||
294 | |||
295 | local_irq_save(flags); | ||
296 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
297 | if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ | ||
298 | mm->context = NO_CONTEXT; | ||
299 | if (mm == current->mm) | ||
300 | activate_context(mm); | ||
301 | } else { | ||
302 | unsigned long asid = mm->context&MMU_CONTEXT_ASID_MASK; | ||
303 | unsigned long saved_asid = MMU_NO_ASID; | ||
304 | |||
305 | start &= PAGE_MASK; | ||
306 | end += (PAGE_SIZE - 1); | ||
307 | end &= PAGE_MASK; | ||
308 | if (mm != current->mm) { | ||
309 | saved_asid = get_asid(); | ||
310 | set_asid(asid); | ||
311 | } | ||
312 | while (start < end) { | ||
313 | __flush_tlb_page(asid, start); | ||
314 | start += PAGE_SIZE; | ||
315 | } | ||
316 | if (saved_asid != MMU_NO_ASID) | ||
317 | set_asid(saved_asid); | ||
318 | } | ||
319 | local_irq_restore(flags); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
324 | { | ||
325 | unsigned long flags; | ||
326 | int size; | ||
327 | |||
328 | local_irq_save(flags); | ||
329 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
330 | if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ | ||
331 | flush_tlb_all(); | ||
332 | } else { | ||
333 | unsigned long asid = init_mm.context&MMU_CONTEXT_ASID_MASK; | ||
334 | unsigned long saved_asid = get_asid(); | ||
335 | |||
336 | start &= PAGE_MASK; | ||
337 | end += (PAGE_SIZE - 1); | ||
338 | end &= PAGE_MASK; | ||
339 | set_asid(asid); | ||
340 | while (start < end) { | ||
341 | __flush_tlb_page(asid, start); | ||
342 | start += PAGE_SIZE; | ||
343 | } | ||
344 | set_asid(saved_asid); | ||
345 | } | ||
346 | local_irq_restore(flags); | ||
347 | } | ||
348 | |||
349 | void flush_tlb_mm(struct mm_struct *mm) | ||
350 | { | ||
351 | /* Invalidate all TLB of this process. */ | ||
352 | /* Instead of invalidating each TLB, we get new MMU context. */ | ||
353 | if (mm->context != NO_CONTEXT) { | ||
354 | unsigned long flags; | ||
355 | |||
356 | local_irq_save(flags); | ||
357 | mm->context = NO_CONTEXT; | ||
358 | if (mm == current->mm) | ||
359 | activate_context(mm); | ||
360 | local_irq_restore(flags); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | void flush_tlb_all(void) | ||
365 | { | ||
366 | unsigned long flags, status; | ||
367 | |||
368 | /* | ||
369 | * Flush all the TLB. | ||
370 | * | ||
371 | * Write to the MMU control register's bit: | ||
372 | * TF-bit for SH-3, TI-bit for SH-4. | ||
373 | * It's same position, bit #2. | ||
374 | */ | ||
375 | local_irq_save(flags); | ||
376 | status = ctrl_inl(MMUCR); | ||
377 | status |= 0x04; | ||
378 | ctrl_outl(status, MMUCR); | ||
379 | local_irq_restore(flags); | ||
380 | } | ||
diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index 2a85bc15a412..329059d6b54a 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c | |||
@@ -26,61 +26,41 @@ | |||
26 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | 26 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) |
27 | { | 27 | { |
28 | pgd_t *pgd; | 28 | pgd_t *pgd; |
29 | pud_t *pud; | ||
29 | pmd_t *pmd; | 30 | pmd_t *pmd; |
30 | pte_t *pte = NULL; | 31 | pte_t *pte = NULL; |
31 | 32 | ||
32 | pgd = pgd_offset(mm, addr); | 33 | pgd = pgd_offset(mm, addr); |
33 | if (pgd) { | 34 | if (pgd) { |
34 | pmd = pmd_alloc(mm, pgd, addr); | 35 | pud = pud_alloc(mm, pgd, addr); |
35 | if (pmd) | 36 | if (pud) { |
36 | pte = pte_alloc_map(mm, pmd, addr); | 37 | pmd = pmd_alloc(mm, pud, addr); |
38 | if (pmd) | ||
39 | pte = pte_alloc_map(mm, pmd, addr); | ||
40 | } | ||
37 | } | 41 | } |
42 | |||
38 | return pte; | 43 | return pte; |
39 | } | 44 | } |
40 | 45 | ||
41 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | 46 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) |
42 | { | 47 | { |
43 | pgd_t *pgd; | 48 | pgd_t *pgd; |
49 | pud_t *pud; | ||
44 | pmd_t *pmd; | 50 | pmd_t *pmd; |
45 | pte_t *pte = NULL; | 51 | pte_t *pte = NULL; |
46 | 52 | ||
47 | pgd = pgd_offset(mm, addr); | 53 | pgd = pgd_offset(mm, addr); |
48 | if (pgd) { | 54 | if (pgd) { |
49 | pmd = pmd_offset(pgd, addr); | 55 | pud = pud_offset(pgd, addr); |
50 | if (pmd) | 56 | if (pud) { |
51 | pte = pte_offset_map(pmd, addr); | 57 | pmd = pmd_offset(pud, addr); |
52 | } | 58 | if (pmd) |
53 | return pte; | 59 | pte = pte_offset_map(pmd, addr); |
54 | } | 60 | } |
55 | |||
56 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | ||
57 | pte_t *ptep, pte_t entry) | ||
58 | { | ||
59 | int i; | ||
60 | |||
61 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { | ||
62 | set_pte_at(mm, addr, ptep, entry); | ||
63 | ptep++; | ||
64 | addr += PAGE_SIZE; | ||
65 | pte_val(entry) += PAGE_SIZE; | ||
66 | } | 61 | } |
67 | } | ||
68 | |||
69 | pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, | ||
70 | pte_t *ptep) | ||
71 | { | ||
72 | pte_t entry; | ||
73 | int i; | ||
74 | |||
75 | entry = *ptep; | ||
76 | 62 | ||
77 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { | 63 | return pte; |
78 | pte_clear(mm, addr, ptep); | ||
79 | addr += PAGE_SIZE; | ||
80 | ptep++; | ||
81 | } | ||
82 | |||
83 | return entry; | ||
84 | } | 64 | } |
85 | 65 | ||
86 | struct page *follow_huge_addr(struct mm_struct *mm, | 66 | struct page *follow_huge_addr(struct mm_struct *mm, |
diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 8ea27ca4b700..7154d1ce9785 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <linux/highmem.h> | 24 | #include <linux/highmem.h> |
25 | #include <linux/bootmem.h> | 25 | #include <linux/bootmem.h> |
26 | #include <linux/pagemap.h> | 26 | #include <linux/pagemap.h> |
27 | 27 | #include <linux/proc_fs.h> | |
28 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
29 | #include <asm/system.h> | 29 | #include <asm/system.h> |
30 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
@@ -80,6 +80,7 @@ void show_mem(void) | |||
80 | static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) | 80 | static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) |
81 | { | 81 | { |
82 | pgd_t *pgd; | 82 | pgd_t *pgd; |
83 | pud_t *pud; | ||
83 | pmd_t *pmd; | 84 | pmd_t *pmd; |
84 | pte_t *pte; | 85 | pte_t *pte; |
85 | 86 | ||
@@ -89,7 +90,17 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) | |||
89 | return; | 90 | return; |
90 | } | 91 | } |
91 | 92 | ||
92 | pmd = pmd_offset(pgd, addr); | 93 | pud = pud_offset(pgd, addr); |
94 | if (pud_none(*pud)) { | ||
95 | pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); | ||
96 | set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE | _PAGE_USER)); | ||
97 | if (pmd != pmd_offset(pud, 0)) { | ||
98 | pud_ERROR(*pud); | ||
99 | return; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pmd = pmd_offset(pud, addr); | ||
93 | if (pmd_none(*pmd)) { | 104 | if (pmd_none(*pmd)) { |
94 | pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); | 105 | pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); |
95 | set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE | _PAGE_USER)); | 106 | set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE | _PAGE_USER)); |
@@ -212,6 +223,8 @@ void __init paging_init(void) | |||
212 | free_area_init_node(0, NODE_DATA(0), zones_size, __MEMORY_START >> PAGE_SHIFT, 0); | 223 | free_area_init_node(0, NODE_DATA(0), zones_size, __MEMORY_START >> PAGE_SHIFT, 0); |
213 | } | 224 | } |
214 | 225 | ||
226 | static struct kcore_list kcore_mem, kcore_vmalloc; | ||
227 | |||
215 | void __init mem_init(void) | 228 | void __init mem_init(void) |
216 | { | 229 | { |
217 | extern unsigned long empty_zero_page[1024]; | 230 | extern unsigned long empty_zero_page[1024]; |
@@ -237,8 +250,13 @@ void __init mem_init(void) | |||
237 | * Setup wrappers for copy/clear_page(), these will get overridden | 250 | * Setup wrappers for copy/clear_page(), these will get overridden |
238 | * later in the boot process if a better method is available. | 251 | * later in the boot process if a better method is available. |
239 | */ | 252 | */ |
253 | #ifdef CONFIG_MMU | ||
240 | copy_page = copy_page_slow; | 254 | copy_page = copy_page_slow; |
241 | clear_page = clear_page_slow; | 255 | clear_page = clear_page_slow; |
256 | #else | ||
257 | copy_page = copy_page_nommu; | ||
258 | clear_page = clear_page_nommu; | ||
259 | #endif | ||
242 | 260 | ||
243 | /* this will put all low memory onto the freelists */ | 261 | /* this will put all low memory onto the freelists */ |
244 | totalram_pages += free_all_bootmem_node(NODE_DATA(0)); | 262 | totalram_pages += free_all_bootmem_node(NODE_DATA(0)); |
@@ -254,7 +272,12 @@ void __init mem_init(void) | |||
254 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | 272 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; |
255 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | 273 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; |
256 | 274 | ||
257 | printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", | 275 | kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT); |
276 | kclist_add(&kcore_vmalloc, (void *)VMALLOC_START, | ||
277 | VMALLOC_END - VMALLOC_START); | ||
278 | |||
279 | printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " | ||
280 | "%dk reserved, %dk data, %dk init)\n", | ||
258 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), | 281 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), |
259 | max_mapnr << (PAGE_SHIFT-10), | 282 | max_mapnr << (PAGE_SHIFT-10), |
260 | codesize >> 10, | 283 | codesize >> 10, |
@@ -263,6 +286,9 @@ void __init mem_init(void) | |||
263 | initsize >> 10); | 286 | initsize >> 10); |
264 | 287 | ||
265 | p3_cache_init(); | 288 | p3_cache_init(); |
289 | |||
290 | /* Initialize the vDSO */ | ||
291 | vsyscall_init(); | ||
266 | } | 292 | } |
267 | 293 | ||
268 | void free_initmem(void) | 294 | void free_initmem(void) |
diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c index 96fa4a999e2a..a9fe80cfc233 100644 --- a/arch/sh/mm/ioremap.c +++ b/arch/sh/mm/ioremap.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/vmalloc.h> | 15 | #include <linux/vmalloc.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/mm.h> | 17 | #include <linux/mm.h> |
18 | #include <linux/pci.h> | ||
18 | #include <asm/io.h> | 19 | #include <asm/io.h> |
19 | #include <asm/page.h> | 20 | #include <asm/page.h> |
20 | #include <asm/pgalloc.h> | 21 | #include <asm/pgalloc.h> |
@@ -135,6 +136,20 @@ void __iomem *__ioremap(unsigned long phys_addr, unsigned long size, | |||
135 | return (void __iomem *)phys_to_virt(phys_addr); | 136 | return (void __iomem *)phys_to_virt(phys_addr); |
136 | 137 | ||
137 | /* | 138 | /* |
139 | * If we're on an SH7751 or SH7780 PCI controller, PCI memory is | ||
140 | * mapped at the end of the address space (typically 0xfd000000) | ||
141 | * in a non-translatable area, so mapping through page tables for | ||
142 | * this area is not only pointless, but also fundamentally | ||
143 | * broken. Just return the physical address instead. | ||
144 | * | ||
145 | * For boards that map a small PCI memory aperture somewhere in | ||
146 | * P1/P2 space, ioremap() will already do the right thing, | ||
147 | * and we'll never get this far. | ||
148 | */ | ||
149 | if (is_pci_memaddr(phys_addr) && is_pci_memaddr(last_addr)) | ||
150 | return (void __iomem *)phys_addr; | ||
151 | |||
152 | /* | ||
138 | * Don't allow anybody to remap normal RAM that we're using.. | 153 | * Don't allow anybody to remap normal RAM that we're using.. |
139 | */ | 154 | */ |
140 | if (phys_addr < virt_to_phys(high_memory)) | 155 | if (phys_addr < virt_to_phys(high_memory)) |
@@ -192,7 +207,7 @@ void __iounmap(void __iomem *addr) | |||
192 | unsigned long vaddr = (unsigned long __force)addr; | 207 | unsigned long vaddr = (unsigned long __force)addr; |
193 | struct vm_struct *p; | 208 | struct vm_struct *p; |
194 | 209 | ||
195 | if (PXSEG(vaddr) < P3SEG) | 210 | if (PXSEG(vaddr) < P3SEG || is_pci_memaddr(vaddr)) |
196 | return; | 211 | return; |
197 | 212 | ||
198 | #ifdef CONFIG_32BIT | 213 | #ifdef CONFIG_32BIT |
diff --git a/arch/sh/mm/pg-nommu.c b/arch/sh/mm/pg-nommu.c index 8f9165a4e333..d15221beaa16 100644 --- a/arch/sh/mm/pg-nommu.c +++ b/arch/sh/mm/pg-nommu.c | |||
@@ -14,23 +14,24 @@ | |||
14 | #include <linux/string.h> | 14 | #include <linux/string.h> |
15 | #include <asm/page.h> | 15 | #include <asm/page.h> |
16 | 16 | ||
17 | static void copy_page_nommu(void *to, void *from) | 17 | void copy_page_nommu(void *to, void *from) |
18 | { | 18 | { |
19 | memcpy(to, from, PAGE_SIZE); | 19 | memcpy(to, from, PAGE_SIZE); |
20 | } | 20 | } |
21 | 21 | ||
22 | static void clear_page_nommu(void *to) | 22 | void clear_page_nommu(void *to) |
23 | { | 23 | { |
24 | memset(to, 0, PAGE_SIZE); | 24 | memset(to, 0, PAGE_SIZE); |
25 | } | 25 | } |
26 | 26 | ||
27 | static int __init pg_nommu_init(void) | 27 | __kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n) |
28 | { | 28 | { |
29 | copy_page = copy_page_nommu; | 29 | memcpy(to, from, n); |
30 | clear_page = clear_page_nommu; | ||
31 | |||
32 | return 0; | 30 | return 0; |
33 | } | 31 | } |
34 | 32 | ||
35 | subsys_initcall(pg_nommu_init); | 33 | __kernel_size_t __clear_user(void *to, __kernel_size_t n) |
36 | 34 | { | |
35 | memset(to, 0, n); | ||
36 | return 0; | ||
37 | } | ||
diff --git a/arch/sh/mm/pg-sh4.c b/arch/sh/mm/pg-sh4.c index c776b60fc250..07371ed7a313 100644 --- a/arch/sh/mm/pg-sh4.c +++ b/arch/sh/mm/pg-sh4.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * arch/sh/mm/pg-sh4.c | 2 | * arch/sh/mm/pg-sh4.c |
3 | * | 3 | * |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | 4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka |
5 | * Copyright (C) 2002 Paul Mundt | 5 | * Copyright (C) 2002 - 2005 Paul Mundt |
6 | * | 6 | * |
7 | * Released under the terms of the GNU GPL v2.0. | 7 | * Released under the terms of the GNU GPL v2.0. |
8 | */ | 8 | */ |
@@ -23,6 +23,8 @@ | |||
23 | 23 | ||
24 | extern struct semaphore p3map_sem[]; | 24 | extern struct semaphore p3map_sem[]; |
25 | 25 | ||
26 | #define CACHE_ALIAS (cpu_data->dcache.alias_mask) | ||
27 | |||
26 | /* | 28 | /* |
27 | * clear_user_page | 29 | * clear_user_page |
28 | * @to: P1 address | 30 | * @to: P1 address |
@@ -35,14 +37,15 @@ void clear_user_page(void *to, unsigned long address, struct page *page) | |||
35 | if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) | 37 | if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) |
36 | clear_page(to); | 38 | clear_page(to); |
37 | else { | 39 | else { |
38 | pgprot_t pgprot = __pgprot(_PAGE_PRESENT | | 40 | pgprot_t pgprot = __pgprot(_PAGE_PRESENT | |
39 | _PAGE_RW | _PAGE_CACHABLE | | 41 | _PAGE_RW | _PAGE_CACHABLE | |
40 | _PAGE_DIRTY | _PAGE_ACCESSED | | 42 | _PAGE_DIRTY | _PAGE_ACCESSED | |
41 | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); | 43 | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); |
42 | unsigned long phys_addr = PHYSADDR(to); | 44 | unsigned long phys_addr = PHYSADDR(to); |
43 | unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); | 45 | unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); |
44 | pgd_t *dir = pgd_offset_k(p3_addr); | 46 | pgd_t *pgd = pgd_offset_k(p3_addr); |
45 | pmd_t *pmd = pmd_offset(dir, p3_addr); | 47 | pud_t *pud = pud_offset(pgd, p3_addr); |
48 | pmd_t *pmd = pmd_offset(pud, p3_addr); | ||
46 | pte_t *pte = pte_offset_kernel(pmd, p3_addr); | 49 | pte_t *pte = pte_offset_kernel(pmd, p3_addr); |
47 | pte_t entry; | 50 | pte_t entry; |
48 | unsigned long flags; | 51 | unsigned long flags; |
@@ -67,21 +70,22 @@ void clear_user_page(void *to, unsigned long address, struct page *page) | |||
67 | * @address: U0 address to be mapped | 70 | * @address: U0 address to be mapped |
68 | * @page: page (virt_to_page(to)) | 71 | * @page: page (virt_to_page(to)) |
69 | */ | 72 | */ |
70 | void copy_user_page(void *to, void *from, unsigned long address, | 73 | void copy_user_page(void *to, void *from, unsigned long address, |
71 | struct page *page) | 74 | struct page *page) |
72 | { | 75 | { |
73 | __set_bit(PG_mapped, &page->flags); | 76 | __set_bit(PG_mapped, &page->flags); |
74 | if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) | 77 | if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) |
75 | copy_page(to, from); | 78 | copy_page(to, from); |
76 | else { | 79 | else { |
77 | pgprot_t pgprot = __pgprot(_PAGE_PRESENT | | 80 | pgprot_t pgprot = __pgprot(_PAGE_PRESENT | |
78 | _PAGE_RW | _PAGE_CACHABLE | | 81 | _PAGE_RW | _PAGE_CACHABLE | |
79 | _PAGE_DIRTY | _PAGE_ACCESSED | | 82 | _PAGE_DIRTY | _PAGE_ACCESSED | |
80 | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); | 83 | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); |
81 | unsigned long phys_addr = PHYSADDR(to); | 84 | unsigned long phys_addr = PHYSADDR(to); |
82 | unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); | 85 | unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); |
83 | pgd_t *dir = pgd_offset_k(p3_addr); | 86 | pgd_t *pgd = pgd_offset_k(p3_addr); |
84 | pmd_t *pmd = pmd_offset(dir, p3_addr); | 87 | pud_t *pud = pud_offset(pgd, p3_addr); |
88 | pmd_t *pmd = pmd_offset(pud, p3_addr); | ||
85 | pte_t *pte = pte_offset_kernel(pmd, p3_addr); | 89 | pte_t *pte = pte_offset_kernel(pmd, p3_addr); |
86 | pte_t entry; | 90 | pte_t entry; |
87 | unsigned long flags; | 91 | unsigned long flags; |
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c new file mode 100644 index 000000000000..92e745341e4d --- /dev/null +++ b/arch/sh/mm/pmb.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/pmb.c | ||
3 | * | ||
4 | * Privileged Space Mapping Buffer (PMB) Support. | ||
5 | * | ||
6 | * Copyright (C) 2005, 2006 Paul Mundt | ||
7 | * | ||
8 | * P1/P2 Section mapping definitions from map32.h, which was: | ||
9 | * | ||
10 | * Copyright 2003 (c) Lineo Solutions,Inc. | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file "COPYING" in the main directory of this archive | ||
14 | * for more details. | ||
15 | */ | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/fs.h> | ||
23 | #include <linux/seq_file.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/pgtable.h> | ||
28 | #include <asm/mmu.h> | ||
29 | #include <asm/io.h> | ||
30 | |||
31 | #define NR_PMB_ENTRIES 16 | ||
32 | |||
33 | static kmem_cache_t *pmb_cache; | ||
34 | static unsigned long pmb_map; | ||
35 | |||
36 | static struct pmb_entry pmb_init_map[] = { | ||
37 | /* vpn ppn flags (ub/sz/c/wt) */ | ||
38 | |||
39 | /* P1 Section Mappings */ | ||
40 | { 0x80000000, 0x00000000, PMB_SZ_64M | PMB_C, }, | ||
41 | { 0x84000000, 0x04000000, PMB_SZ_64M | PMB_C, }, | ||
42 | { 0x88000000, 0x08000000, PMB_SZ_128M | PMB_C, }, | ||
43 | { 0x90000000, 0x10000000, PMB_SZ_64M | PMB_C, }, | ||
44 | { 0x94000000, 0x14000000, PMB_SZ_64M | PMB_C, }, | ||
45 | { 0x98000000, 0x18000000, PMB_SZ_64M | PMB_C, }, | ||
46 | |||
47 | /* P2 Section Mappings */ | ||
48 | { 0xa0000000, 0x00000000, PMB_UB | PMB_SZ_64M | PMB_WT, }, | ||
49 | { 0xa4000000, 0x04000000, PMB_UB | PMB_SZ_64M | PMB_WT, }, | ||
50 | { 0xa8000000, 0x08000000, PMB_UB | PMB_SZ_128M | PMB_WT, }, | ||
51 | { 0xb0000000, 0x10000000, PMB_UB | PMB_SZ_64M | PMB_WT, }, | ||
52 | { 0xb4000000, 0x14000000, PMB_UB | PMB_SZ_64M | PMB_WT, }, | ||
53 | { 0xb8000000, 0x18000000, PMB_UB | PMB_SZ_64M | PMB_WT, }, | ||
54 | }; | ||
55 | |||
56 | static inline unsigned long mk_pmb_entry(unsigned int entry) | ||
57 | { | ||
58 | return (entry & PMB_E_MASK) << PMB_E_SHIFT; | ||
59 | } | ||
60 | |||
61 | static inline unsigned long mk_pmb_addr(unsigned int entry) | ||
62 | { | ||
63 | return mk_pmb_entry(entry) | PMB_ADDR; | ||
64 | } | ||
65 | |||
66 | static inline unsigned long mk_pmb_data(unsigned int entry) | ||
67 | { | ||
68 | return mk_pmb_entry(entry) | PMB_DATA; | ||
69 | } | ||
70 | |||
71 | struct pmb_entry *pmb_alloc(unsigned long vpn, unsigned long ppn, | ||
72 | unsigned long flags) | ||
73 | { | ||
74 | struct pmb_entry *pmbe; | ||
75 | |||
76 | pmbe = kmem_cache_alloc(pmb_cache, GFP_KERNEL); | ||
77 | if (!pmbe) | ||
78 | return ERR_PTR(-ENOMEM); | ||
79 | |||
80 | pmbe->vpn = vpn; | ||
81 | pmbe->ppn = ppn; | ||
82 | pmbe->flags = flags; | ||
83 | |||
84 | return pmbe; | ||
85 | } | ||
86 | |||
87 | void pmb_free(struct pmb_entry *pmbe) | ||
88 | { | ||
89 | kmem_cache_free(pmb_cache, pmbe); | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Must be in P2 for __set_pmb_entry() | ||
94 | */ | ||
95 | int __set_pmb_entry(unsigned long vpn, unsigned long ppn, | ||
96 | unsigned long flags, int *entry) | ||
97 | { | ||
98 | unsigned int pos = *entry; | ||
99 | |||
100 | if (unlikely(pos == PMB_NO_ENTRY)) | ||
101 | pos = find_first_zero_bit(&pmb_map, NR_PMB_ENTRIES); | ||
102 | |||
103 | repeat: | ||
104 | if (unlikely(pos > NR_PMB_ENTRIES)) | ||
105 | return -ENOSPC; | ||
106 | |||
107 | if (test_and_set_bit(pos, &pmb_map)) { | ||
108 | pos = find_first_zero_bit(&pmb_map, NR_PMB_ENTRIES); | ||
109 | goto repeat; | ||
110 | } | ||
111 | |||
112 | ctrl_outl(vpn | PMB_V, mk_pmb_addr(pos)); | ||
113 | |||
114 | #ifdef CONFIG_SH_WRITETHROUGH | ||
115 | /* | ||
116 | * When we are in 32-bit address extended mode, CCR.CB becomes | ||
117 | * invalid, so care must be taken to manually adjust cacheable | ||
118 | * translations. | ||
119 | */ | ||
120 | if (likely(flags & PMB_C)) | ||
121 | flags |= PMB_WT; | ||
122 | #endif | ||
123 | |||
124 | ctrl_outl(ppn | flags | PMB_V, mk_pmb_data(pos)); | ||
125 | |||
126 | *entry = pos; | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int set_pmb_entry(struct pmb_entry *pmbe) | ||
132 | { | ||
133 | int ret; | ||
134 | |||
135 | jump_to_P2(); | ||
136 | ret = __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &pmbe->entry); | ||
137 | back_to_P1(); | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | void clear_pmb_entry(struct pmb_entry *pmbe) | ||
143 | { | ||
144 | unsigned int entry = pmbe->entry; | ||
145 | unsigned long addr; | ||
146 | |||
147 | /* | ||
148 | * Don't allow clearing of wired init entries, P1 or P2 access | ||
149 | * without a corresponding mapping in the PMB will lead to reset | ||
150 | * by the TLB. | ||
151 | */ | ||
152 | if (unlikely(entry < ARRAY_SIZE(pmb_init_map) || | ||
153 | entry >= NR_PMB_ENTRIES)) | ||
154 | return; | ||
155 | |||
156 | jump_to_P2(); | ||
157 | |||
158 | /* Clear V-bit */ | ||
159 | addr = mk_pmb_addr(entry); | ||
160 | ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr); | ||
161 | |||
162 | addr = mk_pmb_data(entry); | ||
163 | ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr); | ||
164 | |||
165 | back_to_P1(); | ||
166 | |||
167 | clear_bit(entry, &pmb_map); | ||
168 | } | ||
169 | |||
170 | static DEFINE_SPINLOCK(pmb_list_lock); | ||
171 | static struct pmb_entry *pmb_list; | ||
172 | |||
173 | static inline void pmb_list_add(struct pmb_entry *pmbe) | ||
174 | { | ||
175 | struct pmb_entry **p, *tmp; | ||
176 | |||
177 | p = &pmb_list; | ||
178 | while ((tmp = *p) != NULL) | ||
179 | p = &tmp->next; | ||
180 | |||
181 | pmbe->next = tmp; | ||
182 | *p = pmbe; | ||
183 | } | ||
184 | |||
185 | static inline void pmb_list_del(struct pmb_entry *pmbe) | ||
186 | { | ||
187 | struct pmb_entry **p, *tmp; | ||
188 | |||
189 | for (p = &pmb_list; (tmp = *p); p = &tmp->next) | ||
190 | if (tmp == pmbe) { | ||
191 | *p = tmp->next; | ||
192 | return; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static struct { | ||
197 | unsigned long size; | ||
198 | int flag; | ||
199 | } pmb_sizes[] = { | ||
200 | { .size = 0x20000000, .flag = PMB_SZ_512M, }, | ||
201 | { .size = 0x08000000, .flag = PMB_SZ_128M, }, | ||
202 | { .size = 0x04000000, .flag = PMB_SZ_64M, }, | ||
203 | { .size = 0x01000000, .flag = PMB_SZ_16M, }, | ||
204 | }; | ||
205 | |||
206 | long pmb_remap(unsigned long vaddr, unsigned long phys, | ||
207 | unsigned long size, unsigned long flags) | ||
208 | { | ||
209 | struct pmb_entry *pmbp; | ||
210 | unsigned long wanted; | ||
211 | int pmb_flags, i; | ||
212 | |||
213 | /* Convert typical pgprot value to the PMB equivalent */ | ||
214 | if (flags & _PAGE_CACHABLE) { | ||
215 | if (flags & _PAGE_WT) | ||
216 | pmb_flags = PMB_WT; | ||
217 | else | ||
218 | pmb_flags = PMB_C; | ||
219 | } else | ||
220 | pmb_flags = PMB_WT | PMB_UB; | ||
221 | |||
222 | pmbp = NULL; | ||
223 | wanted = size; | ||
224 | |||
225 | again: | ||
226 | for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) { | ||
227 | struct pmb_entry *pmbe; | ||
228 | int ret; | ||
229 | |||
230 | if (size < pmb_sizes[i].size) | ||
231 | continue; | ||
232 | |||
233 | pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag); | ||
234 | if (IS_ERR(pmbe)) | ||
235 | return PTR_ERR(pmbe); | ||
236 | |||
237 | ret = set_pmb_entry(pmbe); | ||
238 | if (ret != 0) { | ||
239 | pmb_free(pmbe); | ||
240 | return -EBUSY; | ||
241 | } | ||
242 | |||
243 | phys += pmb_sizes[i].size; | ||
244 | vaddr += pmb_sizes[i].size; | ||
245 | size -= pmb_sizes[i].size; | ||
246 | |||
247 | /* | ||
248 | * Link adjacent entries that span multiple PMB entries | ||
249 | * for easier tear-down. | ||
250 | */ | ||
251 | if (likely(pmbp)) | ||
252 | pmbp->link = pmbe; | ||
253 | |||
254 | pmbp = pmbe; | ||
255 | } | ||
256 | |||
257 | if (size >= 0x1000000) | ||
258 | goto again; | ||
259 | |||
260 | return wanted - size; | ||
261 | } | ||
262 | |||
263 | void pmb_unmap(unsigned long addr) | ||
264 | { | ||
265 | struct pmb_entry **p, *pmbe; | ||
266 | |||
267 | for (p = &pmb_list; (pmbe = *p); p = &pmbe->next) | ||
268 | if (pmbe->vpn == addr) | ||
269 | break; | ||
270 | |||
271 | if (unlikely(!pmbe)) | ||
272 | return; | ||
273 | |||
274 | WARN_ON(!test_bit(pmbe->entry, &pmb_map)); | ||
275 | |||
276 | do { | ||
277 | struct pmb_entry *pmblink = pmbe; | ||
278 | |||
279 | clear_pmb_entry(pmbe); | ||
280 | pmbe = pmblink->link; | ||
281 | |||
282 | pmb_free(pmblink); | ||
283 | } while (pmbe); | ||
284 | } | ||
285 | |||
286 | static void pmb_cache_ctor(void *pmb, kmem_cache_t *cachep, unsigned long flags) | ||
287 | { | ||
288 | struct pmb_entry *pmbe = pmb; | ||
289 | |||
290 | memset(pmb, 0, sizeof(struct pmb_entry)); | ||
291 | |||
292 | spin_lock_irq(&pmb_list_lock); | ||
293 | |||
294 | pmbe->entry = PMB_NO_ENTRY; | ||
295 | pmb_list_add(pmbe); | ||
296 | |||
297 | spin_unlock_irq(&pmb_list_lock); | ||
298 | } | ||
299 | |||
300 | static void pmb_cache_dtor(void *pmb, kmem_cache_t *cachep, unsigned long flags) | ||
301 | { | ||
302 | spin_lock_irq(&pmb_list_lock); | ||
303 | pmb_list_del(pmb); | ||
304 | spin_unlock_irq(&pmb_list_lock); | ||
305 | } | ||
306 | |||
307 | static int __init pmb_init(void) | ||
308 | { | ||
309 | unsigned int nr_entries = ARRAY_SIZE(pmb_init_map); | ||
310 | unsigned int entry; | ||
311 | |||
312 | BUG_ON(unlikely(nr_entries >= NR_PMB_ENTRIES)); | ||
313 | |||
314 | pmb_cache = kmem_cache_create("pmb", sizeof(struct pmb_entry), | ||
315 | 0, 0, pmb_cache_ctor, pmb_cache_dtor); | ||
316 | BUG_ON(!pmb_cache); | ||
317 | |||
318 | jump_to_P2(); | ||
319 | |||
320 | /* | ||
321 | * Ordering is important, P2 must be mapped in the PMB before we | ||
322 | * can set PMB.SE, and P1 must be mapped before we jump back to | ||
323 | * P1 space. | ||
324 | */ | ||
325 | for (entry = 0; entry < nr_entries; entry++) { | ||
326 | struct pmb_entry *pmbe = pmb_init_map + entry; | ||
327 | |||
328 | __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &entry); | ||
329 | } | ||
330 | |||
331 | ctrl_outl(0, PMB_IRMCR); | ||
332 | |||
333 | /* PMB.SE and UB[7] */ | ||
334 | ctrl_outl((1 << 31) | (1 << 7), PMB_PASCR); | ||
335 | |||
336 | back_to_P1(); | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | arch_initcall(pmb_init); | ||
341 | |||
342 | static int pmb_seq_show(struct seq_file *file, void *iter) | ||
343 | { | ||
344 | int i; | ||
345 | |||
346 | seq_printf(file, "V: Valid, C: Cacheable, WT: Write-Through\n" | ||
347 | "CB: Copy-Back, B: Buffered, UB: Unbuffered\n"); | ||
348 | seq_printf(file, "ety vpn ppn size flags\n"); | ||
349 | |||
350 | for (i = 0; i < NR_PMB_ENTRIES; i++) { | ||
351 | unsigned long addr, data; | ||
352 | unsigned int size; | ||
353 | char *sz_str = NULL; | ||
354 | |||
355 | addr = ctrl_inl(mk_pmb_addr(i)); | ||
356 | data = ctrl_inl(mk_pmb_data(i)); | ||
357 | |||
358 | size = data & PMB_SZ_MASK; | ||
359 | sz_str = (size == PMB_SZ_16M) ? " 16MB": | ||
360 | (size == PMB_SZ_64M) ? " 64MB": | ||
361 | (size == PMB_SZ_128M) ? "128MB": | ||
362 | "512MB"; | ||
363 | |||
364 | /* 02: V 0x88 0x08 128MB C CB B */ | ||
365 | seq_printf(file, "%02d: %c 0x%02lx 0x%02lx %s %c %s %s\n", | ||
366 | i, ((addr & PMB_V) && (data & PMB_V)) ? 'V' : ' ', | ||
367 | (addr >> 24) & 0xff, (data >> 24) & 0xff, | ||
368 | sz_str, (data & PMB_C) ? 'C' : ' ', | ||
369 | (data & PMB_WT) ? "WT" : "CB", | ||
370 | (data & PMB_UB) ? "UB" : " B"); | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int pmb_debugfs_open(struct inode *inode, struct file *file) | ||
377 | { | ||
378 | return single_open(file, pmb_seq_show, NULL); | ||
379 | } | ||
380 | |||
381 | static struct file_operations pmb_debugfs_fops = { | ||
382 | .owner = THIS_MODULE, | ||
383 | .open = pmb_debugfs_open, | ||
384 | .read = seq_read, | ||
385 | .llseek = seq_lseek, | ||
386 | .release = seq_release, | ||
387 | }; | ||
388 | |||
389 | static int __init pmb_debugfs_init(void) | ||
390 | { | ||
391 | struct dentry *dentry; | ||
392 | |||
393 | dentry = debugfs_create_file("pmb", S_IFREG | S_IRUGO, | ||
394 | NULL, NULL, &pmb_debugfs_fops); | ||
395 | if (IS_ERR(dentry)) | ||
396 | return PTR_ERR(dentry); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | postcore_initcall(pmb_debugfs_init); | ||
diff --git a/arch/sh/mm/tlb-flush.c b/arch/sh/mm/tlb-flush.c new file mode 100644 index 000000000000..73ec7f6084fa --- /dev/null +++ b/arch/sh/mm/tlb-flush.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * TLB flushing operations for SH with an MMU. | ||
3 | * | ||
4 | * Copyright (C) 1999 Niibe Yutaka | ||
5 | * Copyright (C) 2003 Paul Mundt | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file "COPYING" in the main directory of this archive | ||
9 | * for more details. | ||
10 | */ | ||
11 | #include <linux/mm.h> | ||
12 | #include <asm/mmu_context.h> | ||
13 | #include <asm/tlbflush.h> | ||
14 | |||
15 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
16 | { | ||
17 | if (vma->vm_mm && vma->vm_mm->context.id != NO_CONTEXT) { | ||
18 | unsigned long flags; | ||
19 | unsigned long asid; | ||
20 | unsigned long saved_asid = MMU_NO_ASID; | ||
21 | |||
22 | asid = vma->vm_mm->context.id & MMU_CONTEXT_ASID_MASK; | ||
23 | page &= PAGE_MASK; | ||
24 | |||
25 | local_irq_save(flags); | ||
26 | if (vma->vm_mm != current->mm) { | ||
27 | saved_asid = get_asid(); | ||
28 | set_asid(asid); | ||
29 | } | ||
30 | __flush_tlb_page(asid, page); | ||
31 | if (saved_asid != MMU_NO_ASID) | ||
32 | set_asid(saved_asid); | ||
33 | local_irq_restore(flags); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
38 | unsigned long end) | ||
39 | { | ||
40 | struct mm_struct *mm = vma->vm_mm; | ||
41 | |||
42 | if (mm->context.id != NO_CONTEXT) { | ||
43 | unsigned long flags; | ||
44 | int size; | ||
45 | |||
46 | local_irq_save(flags); | ||
47 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
48 | if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ | ||
49 | mm->context.id = NO_CONTEXT; | ||
50 | if (mm == current->mm) | ||
51 | activate_context(mm); | ||
52 | } else { | ||
53 | unsigned long asid; | ||
54 | unsigned long saved_asid = MMU_NO_ASID; | ||
55 | |||
56 | asid = mm->context.id & MMU_CONTEXT_ASID_MASK; | ||
57 | start &= PAGE_MASK; | ||
58 | end += (PAGE_SIZE - 1); | ||
59 | end &= PAGE_MASK; | ||
60 | if (mm != current->mm) { | ||
61 | saved_asid = get_asid(); | ||
62 | set_asid(asid); | ||
63 | } | ||
64 | while (start < end) { | ||
65 | __flush_tlb_page(asid, start); | ||
66 | start += PAGE_SIZE; | ||
67 | } | ||
68 | if (saved_asid != MMU_NO_ASID) | ||
69 | set_asid(saved_asid); | ||
70 | } | ||
71 | local_irq_restore(flags); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
76 | { | ||
77 | unsigned long flags; | ||
78 | int size; | ||
79 | |||
80 | local_irq_save(flags); | ||
81 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
82 | if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ | ||
83 | flush_tlb_all(); | ||
84 | } else { | ||
85 | unsigned long asid; | ||
86 | unsigned long saved_asid = get_asid(); | ||
87 | |||
88 | asid = init_mm.context.id & MMU_CONTEXT_ASID_MASK; | ||
89 | start &= PAGE_MASK; | ||
90 | end += (PAGE_SIZE - 1); | ||
91 | end &= PAGE_MASK; | ||
92 | set_asid(asid); | ||
93 | while (start < end) { | ||
94 | __flush_tlb_page(asid, start); | ||
95 | start += PAGE_SIZE; | ||
96 | } | ||
97 | set_asid(saved_asid); | ||
98 | } | ||
99 | local_irq_restore(flags); | ||
100 | } | ||
101 | |||
102 | void flush_tlb_mm(struct mm_struct *mm) | ||
103 | { | ||
104 | /* Invalidate all TLB of this process. */ | ||
105 | /* Instead of invalidating each TLB, we get new MMU context. */ | ||
106 | if (mm->context.id != NO_CONTEXT) { | ||
107 | unsigned long flags; | ||
108 | |||
109 | local_irq_save(flags); | ||
110 | mm->context.id = NO_CONTEXT; | ||
111 | if (mm == current->mm) | ||
112 | activate_context(mm); | ||
113 | local_irq_restore(flags); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | void flush_tlb_all(void) | ||
118 | { | ||
119 | unsigned long flags, status; | ||
120 | |||
121 | /* | ||
122 | * Flush all the TLB. | ||
123 | * | ||
124 | * Write to the MMU control register's bit: | ||
125 | * TF-bit for SH-3, TI-bit for SH-4. | ||
126 | * It's same position, bit #2. | ||
127 | */ | ||
128 | local_irq_save(flags); | ||
129 | status = ctrl_inl(MMUCR); | ||
130 | status |= 0x04; | ||
131 | ctrl_outl(status, MMUCR); | ||
132 | ctrl_barrier(); | ||
133 | local_irq_restore(flags); | ||
134 | } | ||
diff --git a/arch/sh/mm/tlb-sh4.c b/arch/sh/mm/tlb-sh4.c index 115b1b6be40b..812b2d567de2 100644 --- a/arch/sh/mm/tlb-sh4.c +++ b/arch/sh/mm/tlb-sh4.c | |||
@@ -36,7 +36,6 @@ void update_mmu_cache(struct vm_area_struct * vma, | |||
36 | unsigned long vpn; | 36 | unsigned long vpn; |
37 | struct page *page; | 37 | struct page *page; |
38 | unsigned long pfn; | 38 | unsigned long pfn; |
39 | unsigned long ptea; | ||
40 | 39 | ||
41 | /* Ptrace may call this routine. */ | 40 | /* Ptrace may call this routine. */ |
42 | if (vma && current->active_mm != vma->vm_mm) | 41 | if (vma && current->active_mm != vma->vm_mm) |
@@ -59,10 +58,11 @@ void update_mmu_cache(struct vm_area_struct * vma, | |||
59 | ctrl_outl(vpn, MMU_PTEH); | 58 | ctrl_outl(vpn, MMU_PTEH); |
60 | 59 | ||
61 | pteval = pte_val(pte); | 60 | pteval = pte_val(pte); |
61 | |||
62 | /* Set PTEA register */ | 62 | /* Set PTEA register */ |
63 | /* TODO: make this look less hacky */ | 63 | if (cpu_data->flags & CPU_HAS_PTEA) |
64 | ptea = ((pteval >> 28) & 0xe) | (pteval & 0x1); | 64 | /* TODO: make this look less hacky */ |
65 | ctrl_outl(ptea, MMU_PTEA); | 65 | ctrl_outl(((pteval >> 28) & 0xe) | (pteval & 0x1), MMU_PTEA); |
66 | 66 | ||
67 | /* Set PTEL register */ | 67 | /* Set PTEL register */ |
68 | pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ | 68 | pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ |