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 */ |
