aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-04-19 10:26:21 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-04-19 10:26:21 -0400
commiteb3e5cce2b39a266a1a167fa4290939db20ef5d6 (patch)
treec4a0e4f4528de93d0c35c5a000e3cb7546dfce32 /arch
parent13bd8e4673d527a9e48f41956b11d391e7c2cfe0 (diff)
parentb1cdbb5f8342d99b732c5535ee7d2de8e7b2cc2e (diff)
Merge master.kernel.org:/home/rmk/linux-2.6-arm
* master.kernel.org:/home/rmk/linux-2.6-arm: ARM: 5974/1: arm/mach-at91 Makefile: remove two blanks. ARM: 6052/1: kdump: make kexec work in interrupt context ARM: 6051/1: VFP: preserve the HW context when calling signal handlers ARM: 6050/1: VFP: fix the SMP versions of vfp_{sync,flush}_hwstate ARM: 6007/1: fix highmem with VIPT cache and DMA ARM: 5975/1: AT91 slow-clock suspend: don't wait when turning PLLs off
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/include/asm/highmem.h15
-rw-r--r--arch/arm/include/asm/kmap_types.h1
-rw-r--r--arch/arm/include/asm/ucontext.h23
-rw-r--r--arch/arm/include/asm/user.h12
-rw-r--r--arch/arm/kernel/signal.c93
-rw-r--r--arch/arm/mach-at91/Makefile4
-rw-r--r--arch/arm/mach-at91/pm_slowclock.S4
-rw-r--r--arch/arm/mm/copypage-v6.c9
-rw-r--r--arch/arm/mm/dma-mapping.c5
-rw-r--r--arch/arm/mm/flush.c25
-rw-r--r--arch/arm/mm/highmem.c87
-rw-r--r--arch/arm/mm/mmu.c10
-rw-r--r--arch/arm/vfp/vfpmodule.c31
13 files changed, 251 insertions, 68 deletions
diff --git a/arch/arm/include/asm/highmem.h b/arch/arm/include/asm/highmem.h
index 7f36d00600b4..feb988a7ec37 100644
--- a/arch/arm/include/asm/highmem.h
+++ b/arch/arm/include/asm/highmem.h
@@ -11,7 +11,11 @@
11 11
12#define kmap_prot PAGE_KERNEL 12#define kmap_prot PAGE_KERNEL
13 13
14#define flush_cache_kmaps() flush_cache_all() 14#define flush_cache_kmaps() \
15 do { \
16 if (cache_is_vivt()) \
17 flush_cache_all(); \
18 } while (0)
15 19
16extern pte_t *pkmap_page_table; 20extern pte_t *pkmap_page_table;
17 21
@@ -21,11 +25,20 @@ extern void *kmap_high(struct page *page);
21extern void *kmap_high_get(struct page *page); 25extern void *kmap_high_get(struct page *page);
22extern void kunmap_high(struct page *page); 26extern void kunmap_high(struct page *page);
23 27
28extern void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte);
29extern void kunmap_high_l1_vipt(struct page *page, pte_t saved_pte);
30
31/*
32 * The following functions are already defined by <linux/highmem.h>
33 * when CONFIG_HIGHMEM is not set.
34 */
35#ifdef CONFIG_HIGHMEM
24extern void *kmap(struct page *page); 36extern void *kmap(struct page *page);
25extern void kunmap(struct page *page); 37extern void kunmap(struct page *page);
26extern void *kmap_atomic(struct page *page, enum km_type type); 38extern void *kmap_atomic(struct page *page, enum km_type type);
27extern void kunmap_atomic(void *kvaddr, enum km_type type); 39extern void kunmap_atomic(void *kvaddr, enum km_type type);
28extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type); 40extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
29extern struct page *kmap_atomic_to_page(const void *ptr); 41extern struct page *kmap_atomic_to_page(const void *ptr);
42#endif
30 43
31#endif 44#endif
diff --git a/arch/arm/include/asm/kmap_types.h b/arch/arm/include/asm/kmap_types.h
index c019949a5189..c4b2ea3fbe42 100644
--- a/arch/arm/include/asm/kmap_types.h
+++ b/arch/arm/include/asm/kmap_types.h
@@ -18,6 +18,7 @@ enum km_type {
18 KM_IRQ1, 18 KM_IRQ1,
19 KM_SOFTIRQ0, 19 KM_SOFTIRQ0,
20 KM_SOFTIRQ1, 20 KM_SOFTIRQ1,
21 KM_L1_CACHE,
21 KM_L2_CACHE, 22 KM_L2_CACHE,
22 KM_TYPE_NR 23 KM_TYPE_NR
23}; 24};
diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h
index bf65e9f4525d..47f023aa8495 100644
--- a/arch/arm/include/asm/ucontext.h
+++ b/arch/arm/include/asm/ucontext.h
@@ -59,23 +59,22 @@ struct iwmmxt_sigframe {
59#endif /* CONFIG_IWMMXT */ 59#endif /* CONFIG_IWMMXT */
60 60
61#ifdef CONFIG_VFP 61#ifdef CONFIG_VFP
62#if __LINUX_ARM_ARCH__ < 6
63/* For ARM pre-v6, we use fstmiax and fldmiax. This adds one extra
64 * word after the registers, and a word of padding at the end for
65 * alignment. */
66#define VFP_MAGIC 0x56465001 62#define VFP_MAGIC 0x56465001
67#define VFP_STORAGE_SIZE 152
68#else
69#define VFP_MAGIC 0x56465002
70#define VFP_STORAGE_SIZE 144
71#endif
72 63
73struct vfp_sigframe 64struct vfp_sigframe
74{ 65{
75 unsigned long magic; 66 unsigned long magic;
76 unsigned long size; 67 unsigned long size;
77 union vfp_state storage; 68 struct user_vfp ufp;
78}; 69 struct user_vfp_exc ufp_exc;
70} __attribute__((__aligned__(8)));
71
72/*
73 * 8 byte for magic and size, 264 byte for ufp, 12 bytes for ufp_exc,
74 * 4 bytes padding.
75 */
76#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe)
77
79#endif /* CONFIG_VFP */ 78#endif /* CONFIG_VFP */
80 79
81/* 80/*
@@ -91,7 +90,7 @@ struct aux_sigframe {
91#ifdef CONFIG_IWMMXT 90#ifdef CONFIG_IWMMXT
92 struct iwmmxt_sigframe iwmmxt; 91 struct iwmmxt_sigframe iwmmxt;
93#endif 92#endif
94#if 0 && defined CONFIG_VFP /* Not yet saved. */ 93#ifdef CONFIG_VFP
95 struct vfp_sigframe vfp; 94 struct vfp_sigframe vfp;
96#endif 95#endif
97 /* Something that isn't a valid magic number for any coprocessor. */ 96 /* Something that isn't a valid magic number for any coprocessor. */
diff --git a/arch/arm/include/asm/user.h b/arch/arm/include/asm/user.h
index df95e050f9dd..05ac4b06876a 100644
--- a/arch/arm/include/asm/user.h
+++ b/arch/arm/include/asm/user.h
@@ -83,11 +83,21 @@ struct user{
83 83
84/* 84/*
85 * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 85 * User specific VFP registers. If only VFPv2 is present, registers 16 to 31
86 * are ignored by the ptrace system call. 86 * are ignored by the ptrace system call and the signal handler.
87 */ 87 */
88struct user_vfp { 88struct user_vfp {
89 unsigned long long fpregs[32]; 89 unsigned long long fpregs[32];
90 unsigned long fpscr; 90 unsigned long fpscr;
91}; 91};
92 92
93/*
94 * VFP exception registers exposed to user space during signal delivery.
95 * Fields not relavant to the current VFP architecture are ignored.
96 */
97struct user_vfp_exc {
98 unsigned long fpexc;
99 unsigned long fpinst;
100 unsigned long fpinst2;
101};
102
93#endif /* _ARM_USER_H */ 103#endif /* _ARM_USER_H */
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index e7714f367eb8..907d5a620bca 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -18,6 +18,7 @@
18#include <asm/cacheflush.h> 18#include <asm/cacheflush.h>
19#include <asm/ucontext.h> 19#include <asm/ucontext.h>
20#include <asm/unistd.h> 20#include <asm/unistd.h>
21#include <asm/vfp.h>
21 22
22#include "ptrace.h" 23#include "ptrace.h"
23#include "signal.h" 24#include "signal.h"
@@ -175,6 +176,90 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
175 176
176#endif 177#endif
177 178
179#ifdef CONFIG_VFP
180
181static int preserve_vfp_context(struct vfp_sigframe __user *frame)
182{
183 struct thread_info *thread = current_thread_info();
184 struct vfp_hard_struct *h = &thread->vfpstate.hard;
185 const unsigned long magic = VFP_MAGIC;
186 const unsigned long size = VFP_STORAGE_SIZE;
187 int err = 0;
188
189 vfp_sync_hwstate(thread);
190 __put_user_error(magic, &frame->magic, err);
191 __put_user_error(size, &frame->size, err);
192
193 /*
194 * Copy the floating point registers. There can be unused
195 * registers see asm/hwcap.h for details.
196 */
197 err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs,
198 sizeof(h->fpregs));
199 /*
200 * Copy the status and control register.
201 */
202 __put_user_error(h->fpscr, &frame->ufp.fpscr, err);
203
204 /*
205 * Copy the exception registers.
206 */
207 __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err);
208 __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
209 __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
210
211 return err ? -EFAULT : 0;
212}
213
214static int restore_vfp_context(struct vfp_sigframe __user *frame)
215{
216 struct thread_info *thread = current_thread_info();
217 struct vfp_hard_struct *h = &thread->vfpstate.hard;
218 unsigned long magic;
219 unsigned long size;
220 unsigned long fpexc;
221 int err = 0;
222
223 __get_user_error(magic, &frame->magic, err);
224 __get_user_error(size, &frame->size, err);
225
226 if (err)
227 return -EFAULT;
228 if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
229 return -EINVAL;
230
231 /*
232 * Copy the floating point registers. There can be unused
233 * registers see asm/hwcap.h for details.
234 */
235 err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs,
236 sizeof(h->fpregs));
237 /*
238 * Copy the status and control register.
239 */
240 __get_user_error(h->fpscr, &frame->ufp.fpscr, err);
241
242 /*
243 * Sanitise and restore the exception registers.
244 */
245 __get_user_error(fpexc, &frame->ufp_exc.fpexc, err);
246 /* Ensure the VFP is enabled. */
247 fpexc |= FPEXC_EN;
248 /* Ensure FPINST2 is invalid and the exception flag is cleared. */
249 fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
250 h->fpexc = fpexc;
251
252 __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
253 __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
254
255 if (!err)
256 vfp_flush_hwstate(thread);
257
258 return err ? -EFAULT : 0;
259}
260
261#endif
262
178/* 263/*
179 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 264 * Do a signal return; undo the signal stack. These are aligned to 64-bit.
180 */ 265 */
@@ -233,8 +318,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
233 err |= restore_iwmmxt_context(&aux->iwmmxt); 318 err |= restore_iwmmxt_context(&aux->iwmmxt);
234#endif 319#endif
235#ifdef CONFIG_VFP 320#ifdef CONFIG_VFP
236// if (err == 0) 321 if (err == 0)
237// err |= vfp_restore_state(&sf->aux.vfp); 322 err |= restore_vfp_context(&aux->vfp);
238#endif 323#endif
239 324
240 return err; 325 return err;
@@ -348,8 +433,8 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
348 err |= preserve_iwmmxt_context(&aux->iwmmxt); 433 err |= preserve_iwmmxt_context(&aux->iwmmxt);
349#endif 434#endif
350#ifdef CONFIG_VFP 435#ifdef CONFIG_VFP
351// if (err == 0) 436 if (err == 0)
352// err |= vfp_save_state(&sf->aux.vfp); 437 err |= preserve_vfp_context(&aux->vfp);
353#endif 438#endif
354 __put_user_error(0, &aux->end_magic, err); 439 __put_user_error(0, &aux->end_magic, err);
355 440
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index 027dd570dcc3..d4004557532a 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -16,8 +16,8 @@ obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_d
16obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o 16obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o
17obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o 17obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o
18obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o 18obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o
19obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o 19obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o
20 obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o 20obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o
21obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o 21obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o
22obj-$(CONFIG_ARCH_AT572D940HF) += at572d940hf.o at91sam926x_time.o at572d940hf_devices.o sam9_smc.o 22obj-$(CONFIG_ARCH_AT572D940HF) += at572d940hf.o at91sam926x_time.o at572d940hf_devices.o sam9_smc.o
23obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o 23obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o
diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S
index 9fcbd6ca0090..9c5b48e68a71 100644
--- a/arch/arm/mach-at91/pm_slowclock.S
+++ b/arch/arm/mach-at91/pm_slowclock.S
@@ -175,8 +175,6 @@ ENTRY(at91_slow_clock)
175 orr r3, r3, #(1 << 29) /* bit 29 always set */ 175 orr r3, r3, #(1 << 29) /* bit 29 always set */
176 str r3, [r1, #(AT91_CKGR_PLLAR - AT91_PMC)] 176 str r3, [r1, #(AT91_CKGR_PLLAR - AT91_PMC)]
177 177
178 wait_pllalock
179
180 /* Save PLLB setting and disable it */ 178 /* Save PLLB setting and disable it */
181 ldr r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] 179 ldr r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)]
182 str r3, .saved_pllbr 180 str r3, .saved_pllbr
@@ -184,8 +182,6 @@ ENTRY(at91_slow_clock)
184 mov r3, #AT91_PMC_PLLCOUNT 182 mov r3, #AT91_PMC_PLLCOUNT
185 str r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] 183 str r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)]
186 184
187 wait_pllblock
188
189 /* Turn off the main oscillator */ 185 /* Turn off the main oscillator */
190 ldr r3, [r1, #(AT91_CKGR_MOR - AT91_PMC)] 186 ldr r3, [r1, #(AT91_CKGR_MOR - AT91_PMC)]
191 bic r3, r3, #AT91_PMC_MOSCEN 187 bic r3, r3, #AT91_PMC_MOSCEN
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 8bca4dea6dfa..f55fa1044f72 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -41,14 +41,7 @@ static void v6_copy_user_highpage_nonaliasing(struct page *to,
41 kfrom = kmap_atomic(from, KM_USER0); 41 kfrom = kmap_atomic(from, KM_USER0);
42 kto = kmap_atomic(to, KM_USER1); 42 kto = kmap_atomic(to, KM_USER1);
43 copy_page(kto, kfrom); 43 copy_page(kto, kfrom);
44#ifdef CONFIG_HIGHMEM 44 __cpuc_flush_dcache_area(kto, PAGE_SIZE);
45 /*
46 * kmap_atomic() doesn't set the page virtual address, and
47 * kunmap_atomic() takes care of cache flushing already.
48 */
49 if (page_address(to) != NULL)
50#endif
51 __cpuc_flush_dcache_area(kto, PAGE_SIZE);
52 kunmap_atomic(kto, KM_USER1); 45 kunmap_atomic(kto, KM_USER1);
53 kunmap_atomic(kfrom, KM_USER0); 46 kunmap_atomic(kfrom, KM_USER0);
54} 47}
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 1351edc0b26f..13fa536d82e6 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -464,6 +464,11 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
464 vaddr += offset; 464 vaddr += offset;
465 op(vaddr, len, dir); 465 op(vaddr, len, dir);
466 kunmap_high(page); 466 kunmap_high(page);
467 } else if (cache_is_vipt()) {
468 pte_t saved_pte;
469 vaddr = kmap_high_l1_vipt(page, &saved_pte);
470 op(vaddr + offset, len, dir);
471 kunmap_high_l1_vipt(page, saved_pte);
467 } 472 }
468 } else { 473 } else {
469 vaddr = page_address(page) + offset; 474 vaddr = page_address(page) + offset;
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index e34f095e2090..c6844cb9b508 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -13,6 +13,7 @@
13 13
14#include <asm/cacheflush.h> 14#include <asm/cacheflush.h>
15#include <asm/cachetype.h> 15#include <asm/cachetype.h>
16#include <asm/highmem.h>
16#include <asm/smp_plat.h> 17#include <asm/smp_plat.h>
17#include <asm/system.h> 18#include <asm/system.h>
18#include <asm/tlbflush.h> 19#include <asm/tlbflush.h>
@@ -152,21 +153,25 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
152 153
153void __flush_dcache_page(struct address_space *mapping, struct page *page) 154void __flush_dcache_page(struct address_space *mapping, struct page *page)
154{ 155{
155 void *addr = page_address(page);
156
157 /* 156 /*
158 * Writeback any data associated with the kernel mapping of this 157 * Writeback any data associated with the kernel mapping of this
159 * page. This ensures that data in the physical page is mutually 158 * page. This ensures that data in the physical page is mutually
160 * coherent with the kernels mapping. 159 * coherent with the kernels mapping.
161 */ 160 */
162#ifdef CONFIG_HIGHMEM 161 if (!PageHighMem(page)) {
163 /* 162 __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
164 * kmap_atomic() doesn't set the page virtual address, and 163 } else {
165 * kunmap_atomic() takes care of cache flushing already. 164 void *addr = kmap_high_get(page);
166 */ 165 if (addr) {
167 if (addr) 166 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
168#endif 167 kunmap_high(page);
169 __cpuc_flush_dcache_area(addr, PAGE_SIZE); 168 } else if (cache_is_vipt()) {
169 pte_t saved_pte;
170 addr = kmap_high_l1_vipt(page, &saved_pte);
171 __cpuc_flush_dcache_area(addr, PAGE_SIZE);
172 kunmap_high_l1_vipt(page, saved_pte);
173 }
174 }
170 175
171 /* 176 /*
172 * If this is a page cache page, and we have an aliasing VIPT cache, 177 * If this is a page cache page, and we have an aliasing VIPT cache,
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c
index 2be1ec7c1b41..77b030f5ec09 100644
--- a/arch/arm/mm/highmem.c
+++ b/arch/arm/mm/highmem.c
@@ -79,7 +79,8 @@ void kunmap_atomic(void *kvaddr, enum km_type type)
79 unsigned int idx = type + KM_TYPE_NR * smp_processor_id(); 79 unsigned int idx = type + KM_TYPE_NR * smp_processor_id();
80 80
81 if (kvaddr >= (void *)FIXADDR_START) { 81 if (kvaddr >= (void *)FIXADDR_START) {
82 __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); 82 if (cache_is_vivt())
83 __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
83#ifdef CONFIG_DEBUG_HIGHMEM 84#ifdef CONFIG_DEBUG_HIGHMEM
84 BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); 85 BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
85 set_pte_ext(TOP_PTE(vaddr), __pte(0), 0); 86 set_pte_ext(TOP_PTE(vaddr), __pte(0), 0);
@@ -124,3 +125,87 @@ struct page *kmap_atomic_to_page(const void *ptr)
124 pte = TOP_PTE(vaddr); 125 pte = TOP_PTE(vaddr);
125 return pte_page(*pte); 126 return pte_page(*pte);
126} 127}
128
129#ifdef CONFIG_CPU_CACHE_VIPT
130
131#include <linux/percpu.h>
132
133/*
134 * The VIVT cache of a highmem page is always flushed before the page
135 * is unmapped. Hence unmapped highmem pages need no cache maintenance
136 * in that case.
137 *
138 * However unmapped pages may still be cached with a VIPT cache, and
139 * it is not possible to perform cache maintenance on them using physical
140 * addresses unfortunately. So we have no choice but to set up a temporary
141 * virtual mapping for that purpose.
142 *
143 * Yet this VIPT cache maintenance may be triggered from DMA support
144 * functions which are possibly called from interrupt context. As we don't
145 * want to keep interrupt disabled all the time when such maintenance is
146 * taking place, we therefore allow for some reentrancy by preserving and
147 * restoring the previous fixmap entry before the interrupted context is
148 * resumed. If the reentrancy depth is 0 then there is no need to restore
149 * the previous fixmap, and leaving the current one in place allow it to
150 * be reused the next time without a TLB flush (common with DMA).
151 */
152
153static DEFINE_PER_CPU(int, kmap_high_l1_vipt_depth);
154
155void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte)
156{
157 unsigned int idx, cpu = smp_processor_id();
158 int *depth = &per_cpu(kmap_high_l1_vipt_depth, cpu);
159 unsigned long vaddr, flags;
160 pte_t pte, *ptep;
161
162 idx = KM_L1_CACHE + KM_TYPE_NR * cpu;
163 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
164 ptep = TOP_PTE(vaddr);
165 pte = mk_pte(page, kmap_prot);
166
167 if (!in_interrupt())
168 preempt_disable();
169
170 raw_local_irq_save(flags);
171 (*depth)++;
172 if (pte_val(*ptep) == pte_val(pte)) {
173 *saved_pte = pte;
174 } else {
175 *saved_pte = *ptep;
176 set_pte_ext(ptep, pte, 0);
177 local_flush_tlb_kernel_page(vaddr);
178 }
179 raw_local_irq_restore(flags);
180
181 return (void *)vaddr;
182}
183
184void kunmap_high_l1_vipt(struct page *page, pte_t saved_pte)
185{
186 unsigned int idx, cpu = smp_processor_id();
187 int *depth = &per_cpu(kmap_high_l1_vipt_depth, cpu);
188 unsigned long vaddr, flags;
189 pte_t pte, *ptep;
190
191 idx = KM_L1_CACHE + KM_TYPE_NR * cpu;
192 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
193 ptep = TOP_PTE(vaddr);
194 pte = mk_pte(page, kmap_prot);
195
196 BUG_ON(pte_val(*ptep) != pte_val(pte));
197 BUG_ON(*depth <= 0);
198
199 raw_local_irq_save(flags);
200 (*depth)--;
201 if (*depth != 0 && pte_val(pte) != pte_val(saved_pte)) {
202 set_pte_ext(ptep, saved_pte, 0);
203 local_flush_tlb_kernel_page(vaddr);
204 }
205 raw_local_irq_restore(flags);
206
207 if (!in_interrupt())
208 preempt_enable();
209}
210
211#endif /* CONFIG_CPU_CACHE_VIPT */
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 4223d086aa17..241c24a1c18f 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -1054,10 +1054,12 @@ void setup_mm_for_reboot(char mode)
1054 pgd_t *pgd; 1054 pgd_t *pgd;
1055 int i; 1055 int i;
1056 1056
1057 if (current->mm && current->mm->pgd) 1057 /*
1058 pgd = current->mm->pgd; 1058 * We need to access to user-mode page tables here. For kernel threads
1059 else 1059 * we don't have any user-mode mappings so we use the context that we
1060 pgd = init_mm.pgd; 1060 * "borrowed".
1061 */
1062 pgd = current->active_mm->pgd;
1061 1063
1062 base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; 1064 base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT;
1063 if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale()) 1065 if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index a420cb949328..315a540c7ce5 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -428,26 +428,6 @@ static void vfp_pm_init(void)
428static inline void vfp_pm_init(void) { } 428static inline void vfp_pm_init(void) { }
429#endif /* CONFIG_PM */ 429#endif /* CONFIG_PM */
430 430
431/*
432 * Synchronise the hardware VFP state of a thread other than current with the
433 * saved one. This function is used by the ptrace mechanism.
434 */
435#ifdef CONFIG_SMP
436void vfp_sync_hwstate(struct thread_info *thread)
437{
438}
439
440void vfp_flush_hwstate(struct thread_info *thread)
441{
442 /*
443 * On SMP systems, the VFP state is automatically saved at every
444 * context switch. We mark the thread VFP state as belonging to a
445 * non-existent CPU so that the saved one will be reloaded when
446 * needed.
447 */
448 thread->vfpstate.hard.cpu = NR_CPUS;
449}
450#else
451void vfp_sync_hwstate(struct thread_info *thread) 431void vfp_sync_hwstate(struct thread_info *thread)
452{ 432{
453 unsigned int cpu = get_cpu(); 433 unsigned int cpu = get_cpu();
@@ -490,9 +470,18 @@ void vfp_flush_hwstate(struct thread_info *thread)
490 last_VFP_context[cpu] = NULL; 470 last_VFP_context[cpu] = NULL;
491 } 471 }
492 472
473#ifdef CONFIG_SMP
474 /*
475 * For SMP we still have to take care of the case where the thread
476 * migrates to another CPU and then back to the original CPU on which
477 * the last VFP user is still the same thread. Mark the thread VFP
478 * state as belonging to a non-existent CPU so that the saved one will
479 * be reloaded in the above case.
480 */
481 thread->vfpstate.hard.cpu = NR_CPUS;
482#endif
493 put_cpu(); 483 put_cpu();
494} 484}
495#endif
496 485
497#include <linux/smp.h> 486#include <linux/smp.h>
498 487